diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f2d7ae4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,156 @@ +cmake_minimum_required(VERSION 3.8) + +project(rknn_yolo_demo_linux) +set(CMAKE_CXX_STANDARD 11) +add_definitions(-g -O0 -ggdb -gdwarf -funwind-tables -rdynamic) +add_definitions(-Wno-write-strings -Wno-return-type) + + +set(TOOLCHAIN_DIR /opt/atk-dlrv1126-toolchain) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/usr/bin/arm-linux-gnueabihf-g++) +set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/usr/bin/arm-linux-gnueabihf-gcc) +set(SYSROOT ${TOOLCHAIN_DIR}/arm-buildroot-linux-gnueabihf/sysroot/usr/include) +set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/arm-buildroot-linux-gnueabihf/sysroot) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread") + +# for linux +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# # for android +# if (${TARGET_SOC} STREQUAL "RK3399PRO") +# set(LIB_ARCH lib64) +# endif() + +# rga +set(RGA_DIR ${MZ_ROOT}/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga) +include_directories(${RGA_DIR}/include) + +# drm +set(DRM_DIR ${MZ_ROOT}/libs/common/drm) +include_directories(${DRM_DIR}/include) +include_directories(${DRM_DIR}/include/libdrm) + +#stb +set(STB_DIR ${MZ_ROOT}/libs/common/) +include_directories(${STB_DIR}) + +# rknn api +if (${TARGET_SOC} STREQUAL "RK3399PRO") + set(RKNN_API_PATH ${MZ_ROOT}/libs/rklibs/RK3399Pro_npu/rknn-api/librknn_api) + include_directories(${RKNN_API_PATH}/include) + set(RKNN_API_LIB ${RKNN_API_PATH}/${CMAKE_SYSTEM_NAME}/${LIB_ARCH}/librknn_api.so) +else() + set(RKNN_API_PATH ${MZ_ROOT}/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api) + include_directories(${RKNN_API_PATH}/include) + set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) +endif() + +# libjpeg +set(LIBJPEG_DIR ${MZ_ROOT}/libs/platform/${TARGET_SOC}/libjpeg) +include_directories(${LIBJPEG_DIR}/include) +set(LIBJPEG_LIB ${LIBJPEG_DIR}/lib/${CMAKE_SYSTEM_NAME}/libjpeg.a) + +# libpng +set(LIBPNG_DIR ${MZ_ROOT}/libs/platform/${TARGET_SOC}/libpng) +include_directories(${LIBPNG_DIR}/include) +set(LIBPNG_LIB ${LIBPNG_DIR}/lib/${CMAKE_SYSTEM_NAME}/libpng.a) + +# zlib(used by libpng) +set(ZLIB_DIR ${MZ_ROOT}/libs/platform/${TARGET_SOC}/zlib) +include_directories(${ZLIB_DIR}/include) +set(ZLIB_LIB ${ZLIB_DIR}/lib/${CMAKE_SYSTEM_NAME}/libz.a) + +# #curl +include_directories(${CMAKE_SOURCE_DIR}/include) +# set(CURL_LIB /opt/atk-dlrv1126-toolchain/arm-buildroot-linux-gnueabihf/sysroot/usr/lib/libcurl.so) + +set(libs + jes_bas + jes_upgrade + jes_user + jes_network + jes_mss + jes_isp + jes_mcs + jes_spk + jes_record + jes_recsnap + jes_replay + jes_storage + jlinux + jbus + jbuffer + dbus-1 + jvbase + arch + utility + cjson + ${LIBINTL}) + +link_directories(/opt/atk-dlrv1126-toolchain/arm-buildroot-linux-gnueabihf/sysroot/usr/lib) +find_package(CURL REQUIRED) +include_directories(${CURL_INCLUDE_DIR}) +message("CURL_LIBRARIES: ${CURL_LIBRARIES}") +message("CURL_INCLUDE_DIR: ${CURL_INCLUDE_DIR}") +find_package(OpenCV REQUIRED) +find_package(OpenSSL REQUIRED) + +set(OPENCV_LIBS opencv_core opencv_imgcodecs opencv_imgproc opencv_features2d opencv_flann opencv_highgui opencv_freetype) +set(RKNN_LIBS easymedia rga rkaiq sample_common_isp) + +link_directories(include lib) +link_directories(include ./include/librtsp) +link_directories(/home/developer/sysroot/usr/lib) +link_directories(/home/developer/sysroot/lib) + +include_directories(/home/developer/sysroot/usr/include/) +include_directories(${SYSROOT}) +include_directories(${SYSROOT}/rga) +include_directories(${SYSROOT}/easymedia) +include_directories(${SYSROOT}/rkaiq/uAPI) +include_directories(${SYSROOT}/rkaiq/xcore) +include_directories(${SYSROOT}/rkaiq/algos) +include_directories(${SYSROOT}/rkaiq/common) +include_directories(${SYSROOT}/rkaiq/iq_parser) +include_directories(./include/librtsp) +include_directories(./include/nlohmann) + + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(yolov5-JES + src/drm_func.c + src/rga_func.c + src/main.cpp + src/yolov5_detect_postprocess.cpp + src/yolov5_detect.cpp + ) + +target_link_libraries(yolov5-JES + ${RKNN_API_LIB} + ${LIBJPEG_LIB} + ${LIBPNG_LIB} + ${ZLIB_LIB} + ${OPENCV_LIBS} + ${CURL_LIBRARIES} + librtsp.a + ${RKNN_LIBS} + dl + ${libs} + OpenSSL::Crypto + OpenSSL::SSL +) +add_definitions(-DRKAIQ) +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/yolov5-JES) +install(TARGETS yolov5-JES DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib ) + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..583fe8d --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# rknn_yolo_EAI_pic +#### 简要说明 + +用于跑EAI提供仓库训练出来的yolov5模型的视频检测例程。 +集成了从串口读取红外温度数据,到达阈值后报警并运行rknn模型 + +需注意运行该例程需要将npu驱动更新为1.7.3版本 + +本项目只适用于EAI-YOLOV5 +详情请参考EAI官网:https://www.easy-eai.com/document_details/3/342 + + +## 准备工作 +根据EAI给出的教程训练、转化得到rknn模型 +有两类检测模型:火焰检测和烟雾检测 + + + +## 安装(install) + +通过adb连接设备并将编译结果推送至板端,执行以下命令推送,这里板端默认使用/userdata路径 + +``` +adb push install/rknn_yolo_demo /userdata/ +``` + +将前面准备好的RKNN模型推送至板端,这里假设模型名字为 yolov5s_u8.rknn + +``` +adb push ./yolov5s_u8.rknn /userdata/rknn_yolo_demo/model/yolov5s_u8.rknn +``` + + + +## 单图测试执行 +详情请了解官网:9.模型部署实例 + +进入EAI环境之后编译,将编译文件放置到可执行程序,假设是first_yolov5_detect_demo +用下列命令将可执行程序推送到开发板端 + +``` +cp first_yolov5_detect_demo/ /mnt/userdata/ -rf +``` + +进入办卡运行环境 + +``` +adb shell +``` + +定位到指定位置后,运行程序 + +``` +./rknn_yolo_EAI +``` + + +## 编译(build) + +在项目文件中,根据设备,调整 GCC_COMPILER 参数,终端执行下面命令进行编译 + +``` +./build.sh +``` diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..da176e8 --- /dev/null +++ b/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +set -e + +rm build -rf +mkdir build + +RV1109_TOOL_CHAIN="/opt/atk-dlrv1126-toolchain" + + +# for rv1109/rv1126 armhf +GCC_COMPILER=${RV1109_TOOL_CHAIN}/usr/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# model_zoo_path root path +MZ_ROOT=$(pwd | sed 's/\(rknn_model_zoo-main\).*/\1/g') +echo "MZ_ROOT:" +echo $MZ_ROOT +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ \ + -DMZ_ROOT=${MZ_ROOT} \ + -DTARGET_SOC=RV1109_1126 \ + -DCMAKE_SYSTEM_NAME=Linux +make -j4 +make install +cd .. + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ \ + -DMZ_ROOT=${MZ_ROOT} \ + -DTARGET_SOC=RV1109_1126 \ + -DCMAKE_SYSTEM_NAME=Linux +make -j4 +make install + diff --git a/include/drm_func.h b/include/drm_func.h new file mode 100644 index 0000000..624b49d --- /dev/null +++ b/include/drm_func.h @@ -0,0 +1,39 @@ +#ifndef __DRM_FUNC_H__ +#define __DRM_FUNC_H__ +#include +#include +#include +#include +#include // open function +#include // close function +#include +#include + + +#include +#include "libdrm/drm_fourcc.h" +#include "xf86drm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (* FUNC_DRM_IOCTL)(int fd, unsigned long request, void *arg); + +typedef struct _drm_context{ + void *drm_handle; + FUNC_DRM_IOCTL io_func; +} drm_context; + +int drm_init(drm_context *drm_ctx); + +void* drm_buf_alloc(drm_context *drm_ctx,int drm_fd, int TexWidth, int TexHeight,int bpp,int *fd,unsigned int *handle,size_t *actual_size); + +int drm_buf_destroy(drm_context *drm_ctx,int drm_fd,int buf_fd, int handle,void *drm_buf,size_t size); + +void drm_deinit(drm_context *drm_ctx, int drm_fd); + +#ifdef __cplusplus +} +#endif +#endif /*__DRM_FUNC_H__*/ \ No newline at end of file diff --git a/include/json.hpp b/include/json.hpp new file mode 100644 index 0000000..95d6bf1 --- /dev/null +++ b/include/json.hpp @@ -0,0 +1,5258 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-License-Identifier: MIT + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file docs/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(JSON_HAS_CPP_17) + #if JSON_HAS_STATIC_RTTI + #include + #endif + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +NLOHMANN_JSON_NAMESPACE_BEGIN + +/*! +@brief a class to store JSON values + +@internal +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) + : public ::nlohmann::detail::json_base_class +{ + private: + template friend struct detail::external_constructor; + + template + friend class ::nlohmann::json_pointer; + // can be restored when json_pointer backwards compatibility is removed + // friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + using json_base_class_t = ::nlohmann::detail::json_base_class; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + using exception = detail::exception; + using parse_error = detail::parse_error; + using invalid_iterator = detail::invalid_iterator; + using type_error = detail::type_error; + using out_of_range = detail::out_of_range; + using other_error = detail::other_error; + + /// @} + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + /// @brief returns the allocator associated with the container + /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /// @brief returns version information on the library + /// @sa https://json.nlohmann.me/api/basic_json/meta/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2023 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.', + std::to_string(NLOHMANN_JSON_VERSION_PATCH)); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", detail::concat( + std::to_string(__GNUC__), '.', + std::to_string(__GNUC_MINOR__), '.', + std::to_string(__GNUC_PATCHLEVEL__)) + } + }; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#if defined(_MSVC_LANG) + result["compiler"]["c++"] = std::to_string(_MSVC_LANG); +#elif defined(__cplusplus) + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /// @brief default object key comparator type + /// The actual object key comparator type (@ref object_comparator_t) may be + /// different. + /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/ +#if defined(JSON_HAS_CPP_14) + // use of transparent comparator avoids unnecessary repeated construction of temporaries + // in functions involving lookup by key with types other than object_t::key_type (aka. StringType) + using default_object_comparator_t = std::less<>; +#else + using default_object_comparator_t = std::less; +#endif + + /// @brief a type for an object + /// @sa https://json.nlohmann.me/api/basic_json/object_t/ + using object_t = ObjectType>>; + + /// @brief a type for an array + /// @sa https://json.nlohmann.me/api/basic_json/array_t/ + using array_t = ArrayType>; + + /// @brief a type for a string + /// @sa https://json.nlohmann.me/api/basic_json/string_t/ + using string_t = StringType; + + /// @brief a type for a boolean + /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/ + using boolean_t = BooleanType; + + /// @brief a type for a number (integer) + /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/ + using number_integer_t = NumberIntegerType; + + /// @brief a type for a number (unsigned) + /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/ + using number_unsigned_t = NumberUnsignedType; + + /// @brief a type for a number (floating-point) + /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/ + using number_float_t = NumberFloatType; + + /// @brief a type for a packed binary type + /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ + using binary_t = nlohmann::byte_container_with_subtype; + + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ + using object_comparator_t = detail::actual_object_comparator_t; + + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = static_cast(false); + break; + } + + case value_t::number_integer: + { + number_integer = static_cast(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = static_cast(0); + break; + } + + case value_t::number_float: + { + number_float = static_cast(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.3", nullptr)); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) : string(create(value)) {} + + /// constructor for rvalue strings + json_value(string_t&& value) : string(create(std::move(value))) {} + + /// constructor for objects + json_value(const object_t& value) : object(create(value)) {} + + /// constructor for rvalue objects + json_value(object_t&& value) : object(create(std::move(value))) {} + + /// constructor for arrays + json_value(const array_t& value) : array(create(value)) {} + + /// constructor for rvalue arrays + json_value(array_t&& value) : array(create(std::move(value))) {} + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) : binary(create(std::move(value))) {} + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) : binary(create(std::move(value))) {} + + void destroy(value_t t) + { + if ( + (t == value_t::object && object == nullptr) || + (t == value_t::array && array == nullptr) || + (t == value_t::string && string == nullptr) || + (t == value_t::binary && binary == nullptr) + ) + { + //not initialized (e.g. due to exception in the ctor) + return; + } + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_data.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_data.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_data.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr); + JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr); + JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr); + JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_data.m_type) + { + case value_t::array: + { + for (auto& element : *m_data.m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_data.m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count_set_parents) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count_set_parents; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count_set_parents); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = static_cast(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != static_cast(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /// @brief parser event types + /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/ + using parse_event_t = detail::parse_event_t; + + /// @brief per-element parser callback type + /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /// @brief create an empty value with a given type + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const value_t v) + : m_data(v) + { + assert_invariant(); + } + + /// @brief create a null object + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape) + : basic_json(value_t::null) + { + assert_invariant(); + } + + /// @brief create a JSON value from compatible types + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /// @brief create a JSON value from an existing one + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_data.m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + JSON_ASSERT(m_data.m_type == val.type()); + set_parents(); + assert_invariant(); + } + + /// @brief create a container (array or object) from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int; + // (many string types can be constructed from 0 via its null-pointer guise, so we get a + // broken call to op[key_type], the wrong semantics and a 4804 warning on Windows) + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast(0)].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr)); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_data.m_type = value_t::object; + m_data.m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_data.m_value.object->emplace( + std::move(*((*element.m_data.m_value.array)[0].m_data.m_value.string)), + std::move((*element.m_data.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_data.m_type = value_t::array; + m_data.m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /// @brief explicitly create a binary array (without subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = init; + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = binary_t(init, subtype); + return res; + } + + /// @brief explicitly create a binary array + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = std::move(init); + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_data.m_type = value_t::binary; + res.m_data.m_value = binary_t(std::move(init), subtype); + return res; + } + + /// @brief explicitly create an array from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/array/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /// @brief explicitly create an object from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/object/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /// @brief construct an array with count copies of given value + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(size_type cnt, const basic_json& val): + m_data{cnt, val} + { + set_parents(); + assert_invariant(); + } + + /// @brief construct a JSON container given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr)); + } + + // copy type from first iterator + m_data.m_type = first.m_object->m_data.m_type; + + // check if iterator range is complete for primitive values + switch (m_data.m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_data.m_type) + { + case value_t::number_integer: + { + m_data.m_value.number_integer = first.m_object->m_data.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_data.m_value.number_unsigned = first.m_object->m_data.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_data.m_value.number_float = first.m_object->m_data.m_value.number_float; + break; + } + + case value_t::boolean: + { + m_data.m_value.boolean = first.m_object->m_data.m_value.boolean; + break; + } + + case value_t::string: + { + m_data.m_value = *first.m_object->m_data.m_value.string; + break; + } + + case value_t::object: + { + m_data.m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_data.m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_data.m_value = *first.m_object->m_data.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /// @brief copy constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const basic_json& other) + : json_base_class_t(other) + { + m_data.m_type = other.m_data.m_type; + // check of passed value is valid + other.assert_invariant(); + + switch (m_data.m_type) + { + case value_t::object: + { + m_data.m_value = *other.m_data.m_value.object; + break; + } + + case value_t::array: + { + m_data.m_value = *other.m_data.m_value.array; + break; + } + + case value_t::string: + { + m_data.m_value = *other.m_data.m_value.string; + break; + } + + case value_t::boolean: + { + m_data.m_value = other.m_data.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_data.m_value = other.m_data.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_data.m_value = other.m_data.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_data.m_value = other.m_data.m_value.number_float; + break; + } + + case value_t::binary: + { + m_data.m_value = *other.m_data.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /// @brief move constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(basic_json&& other) noexcept + : json_base_class_t(std::forward(other)), + m_data(std::move(other.m_data)) + { + // check that passed value is valid + other.assert_invariant(false); + + // invalidate payload + other.m_data.m_type = value_t::null; + other.m_data.m_value = {}; + + set_parents(); + assert_invariant(); + } + + /// @brief copy assignment + /// @sa https://json.nlohmann.me/api/basic_json/operator=/ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_data.m_type, other.m_data.m_type); + swap(m_data.m_value, other.m_data.m_value); + json_base_class_t::operator=(std::move(other)); + + set_parents(); + assert_invariant(); + return *this; + } + + /// @brief destructor + /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/ + ~basic_json() noexcept + { + assert_invariant(false); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /// @brief serialization + /// @sa https://json.nlohmann.me/api/basic_json/dump/ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /// @brief return the type of the JSON value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/type/ + constexpr value_t type() const noexcept + { + return m_data.m_type; + } + + /// @brief return whether type is primitive + /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /// @brief return whether type is structured + /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /// @brief return whether value is null + /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + constexpr bool is_null() const noexcept + { + return m_data.m_type == value_t::null; + } + + /// @brief return whether value is a boolean + /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + constexpr bool is_boolean() const noexcept + { + return m_data.m_type == value_t::boolean; + } + + /// @brief return whether value is a number + /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /// @brief return whether value is an integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + constexpr bool is_number_integer() const noexcept + { + return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned; + } + + /// @brief return whether value is an unsigned integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + constexpr bool is_number_unsigned() const noexcept + { + return m_data.m_type == value_t::number_unsigned; + } + + /// @brief return whether value is a floating-point number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + constexpr bool is_number_float() const noexcept + { + return m_data.m_type == value_t::number_float; + } + + /// @brief return whether value is an object + /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + constexpr bool is_object() const noexcept + { + return m_data.m_type == value_t::object; + } + + /// @brief return whether value is an array + /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + constexpr bool is_array() const noexcept + { + return m_data.m_type == value_t::array; + } + + /// @brief return whether value is a string + /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + constexpr bool is_string() const noexcept + { + return m_data.m_type == value_t::string; + } + + /// @brief return whether value is a binary array + /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + constexpr bool is_binary() const noexcept + { + return m_data.m_type == value_t::binary; + } + + /// @brief return whether value is discarded + /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + constexpr bool is_discarded() const noexcept + { + return m_data.m_type == value_t::discarded; + } + + /// @brief return the type of the JSON value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ + constexpr operator value_t() const noexcept + { + return m_data.m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_data.m_value.boolean; + } + + JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_data.m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_data.m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_data.m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_data.m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_data.m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_data.m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_data.m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_data.m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_data.m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_data.m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_data.m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_data.m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_data.m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + auto ret = ValueType(); + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /// @brief get a value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_to/ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow calling get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif +#if defined(JSON_HAS_CPP_17) && JSON_HAS_STATIC_RTTI + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); + } + + return *get_ptr(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this)); + } + + return *get_ptr(); + } + + /// @} + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_data.m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); + } + } + else + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + } + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_data.m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); + } + } + else + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(key); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); + } + return set_parent(it->second); + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template::value, int> = 0> + reference at(KeyType && key) + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(std::forward(key)); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward(key)), "' not found"), this)); + } + return set_parent(it->second); + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(key); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this)); + } + return it->second; + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + template::value, int> = 0> + const_reference at(KeyType && key) const + { + // at only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this)); + } + + auto it = m_data.m_value.object->find(std::forward(key)); + if (it == m_data.m_value.object->end()) + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward(key)), "' not found"), this)); + } + return it->second; + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_data.m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size & capacity before resizing + const auto old_size = m_data.m_value.array->size(); + const auto old_capacity = m_data.m_value.array->capacity(); +#endif + m_data.m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + } + else + { + // set parent for values added above + set_parents(begin() + static_cast(old_size), static_cast(idx + 1 - old_size)); + } +#endif + assert_invariant(); + } + + return m_data.m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_data.m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](typename object_t::key_type key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto result = m_data.m_value.object->emplace(std::move(key), nullptr); + return set_parent(result.first->second); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto it = m_data.m_value.object->find(key); + JSON_ASSERT(it != m_data.m_value.object->end()); + return it->second; + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC + // (they seemingly cannot be constrained to resolve the ambiguity) + template + reference operator[](T* key) + { + return operator[](typename object_t::key_type(key)); + } + + template + const_reference operator[](T* key) const + { + return operator[](typename object_t::key_type(key)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template::value, int > = 0 > + reference operator[](KeyType && key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto result = m_data.m_value.object->emplace(std::forward(key), nullptr); + return set_parent(result.first->second); + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template::value, int > = 0 > + const_reference operator[](KeyType && key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + auto it = m_data.m_value.object->find(std::forward(key)); + JSON_ASSERT(it != m_data.m_value.object->end()); + return it->second; + } + + JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); + } + + private: + template + using is_comparable_with_object_key = detail::is_comparable < + object_comparator_t, const typename object_t::key_type&, KeyType >; + + template + using value_return_type = std::conditional < + detail::is_c_string_uncvref::value, + string_t, typename std::decay::type >; + + public: + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, detail::enable_if_t < + !detail::is_transparent::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + !detail::is_transparent::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class KeyType, detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(KeyType && key, const ValueType& default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(std::forward(key)); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class KeyType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(KeyType && key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(std::forward(key)); + if (it != end()) + { + return it->template get(); + } + + return std::forward(default_value); + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, detail::enable_if_t < + detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const json_pointer& ptr, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return std::forward(default_value); + } + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + template < class ValueType, class BasicJsonType, detail::enable_if_t < + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const + { + return value(ptr.convert(), default_value); + } + + template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + ReturnType value(const ::nlohmann::json_pointer& ptr, ValueType && default_value) const + { + return value(ptr.convert(), std::forward(default_value)); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + reference front() + { + return *begin(); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + const_reference front() const + { + return *cbegin(); + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /// @brief remove element given an iterator + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, detail::enable_if_t < + std::is_same::value || + std::is_same::value, int > = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + IteratorType result = end(); + + switch (m_data.m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.string); + std::allocator_traits::deallocate(alloc, m_data.m_value.string, 1); + m_data.m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.binary); + std::allocator_traits::deallocate(alloc, m_data.m_value.binary, 1); + m_data.m_value.binary = nullptr; + } + + m_data.m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_data.m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_data.m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return result; + } + + /// @brief remove elements given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, detail::enable_if_t < + std::is_same::value || + std::is_same::value, int > = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this)); + } + + IteratorType result = end(); + + switch (m_data.m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.string); + std::allocator_traits::deallocate(alloc, m_data.m_value.string, 1); + m_data.m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_data.m_value.binary); + std::allocator_traits::deallocate(alloc, m_data.m_value.binary, 1); + m_data.m_value.binary = nullptr; + } + + m_data.m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_data.m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return result; + } + + private: + template < typename KeyType, detail::enable_if_t < + detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) + { + // this erase only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + return m_data.m_value.object->erase(std::forward(key)); + } + + template < typename KeyType, detail::enable_if_t < + !detail::has_erase_with_key_type::value, int > = 0 > + size_type erase_internal(KeyType && key) + { + // this erase only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + + const auto it = m_data.m_value.object->find(std::forward(key)); + if (it != m_data.m_value.object->end()) + { + m_data.m_value.object->erase(it); + return 1; + } + return 0; + } + + public: + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // the indirection via erase_internal() is added to avoid making this + // function a template and thus de-rank it during overload resolution + return erase_internal(key); + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template::value, int> = 0> + size_type erase(KeyType && key) + { + return erase_internal(std::forward(key)); + } + + /// @brief remove element from a JSON array given an index + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this)); + } + + m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this)); + } + } + + /// @} + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + iterator find(const typename object_t::key_type& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(key); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + const_iterator find(const typename object_t::key_type& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(key); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template::value, int> = 0> + iterator find(KeyType && key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template::value, int> = 0> + const_iterator find(KeyType && key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_data.m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + size_type count(const typename object_t::key_type& key) const + { + // return 0 for all nonobject types + return is_object() ? m_data.m_value.object->count(key) : 0; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template::value, int> = 0> + size_type count(KeyType && key) const + { + // return 0 for all nonobject types + return is_object() ? m_data.m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const typename object_t::key_type& key) const + { + return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + template::value, int> = 0> + bool contains(KeyType && key) const + { + return is_object() && m_data.m_value.object->find(std::forward(key)) != m_data.m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object given a JSON pointer + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + bool contains(const typename ::nlohmann::json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /// @brief returns a const iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/cbegin/ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + const_iterator end() const noexcept + { + return cend(); + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/cend/ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /// @brief returns a const reverse iterator to the last element + /// @sa https://json.nlohmann.me/api/basic_json/crbegin/ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /// @brief returns a const reverse iterator to one before the first + /// @sa https://json.nlohmann.me/api/basic_json/crend/ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /// @brief checks whether the container is empty. + /// @sa https://json.nlohmann.me/api/basic_json/empty/ + bool empty() const noexcept + { + switch (m_data.m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_data.m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_data.m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /// @brief returns the number of elements + /// @sa https://json.nlohmann.me/api/basic_json/size/ + size_type size() const noexcept + { + switch (m_data.m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_data.m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_data.m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /// @brief returns the maximum possible number of elements + /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + size_type max_size() const noexcept + { + switch (m_data.m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_data.m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_data.m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /// @brief clears the contents + /// @sa https://json.nlohmann.me/api/basic_json/clear/ + void clear() noexcept + { + switch (m_data.m_type) + { + case value_t::number_integer: + { + m_data.m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_data.m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_data.m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_data.m_value.boolean = false; + break; + } + + case value_t::string: + { + m_data.m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_data.m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_data.m_value.array->clear(); + break; + } + + case value_t::object: + { + m_data.m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); + } + + // transform null object into an array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + const auto old_capacity = m_data.m_value.array->capacity(); + m_data.m_value.array->push_back(std::move(val)); + set_parent(m_data.m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); + } + + // transform null object into an array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value = value_t::array; + assert_invariant(); + } + + // add element to array + const auto old_capacity = m_data.m_value.array->capacity(); + m_data.m_value.array->push_back(val); + set_parent(m_data.m_value.array->back(), old_capacity); + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this)); + } + + // transform null object into an object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value = value_t::object; + assert_invariant(); + } + + // add element to object + auto res = m_data.m_value.object->insert(val); + set_parent(res.first->second); + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this)); + } + + // transform null object into an array + if (is_null()) + { + m_data.m_type = value_t::array; + m_data.m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + const auto old_capacity = m_data.m_value.array->capacity(); + m_data.m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_data.m_value.array->back(), old_capacity); + } + + /// @brief add an object to an object if key does not exist + /// @sa https://json.nlohmann.me/api/basic_json/emplace/ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this)); + } + + // transform null object into an object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_data.m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_data.m_value.array != nullptr); + + auto insert_pos = std::distance(m_data.m_value.array->begin(), pos.m_it.array_iterator); + m_data.m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_data.m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_data.m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /// @brief inserts copies of element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + /// @brief inserts range of elements into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /// @brief inserts elements from initializer list into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /// @brief inserts range of elements into object + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this)); + } + + m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_reference j, bool merge_objects = false) + { + update(j.begin(), j.end(), merge_objects); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_iterator first, const_iterator last, bool merge_objects = false) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_data.m_type = value_t::object; + m_data.m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object)); + } + + for (auto it = first; it != last; ++it) + { + if (merge_objects && it.value().is_object()) + { + auto it2 = m_data.m_value.object->find(it.key()); + if (it2 != m_data.m_value.object->end()) + { + it2->second.update(it.value(), true); + continue; + } + } + m_data.m_value.object->operator[](it.key()) = it.value(); +#if JSON_DIAGNOSTICS + m_data.m_value.object->operator[](it.key()).m_parent = this; +#endif + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_data.m_type, other.m_data.m_type); + std::swap(m_data.m_value, other.m_data.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + using std::swap; + swap(*(m_data.m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + using std::swap; + swap(*(m_data.m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + using std::swap; + swap(*(m_data.m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + using std::swap; + swap(*(m_data.m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + using std::swap; + swap(*(m_data.m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this)); + } + } + + /// @} + + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + // note parentheses around operands are necessary; see + // https://github.com/nlohmann/json/issues/1530 +#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \ + const auto lhs_type = lhs.type(); \ + const auto rhs_type = rhs.type(); \ + \ + if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \ + { \ + switch (lhs_type) \ + { \ + case value_t::array: \ + return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array); \ + \ + case value_t::object: \ + return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \ + \ + case value_t::null: \ + return (null_result); \ + \ + case value_t::string: \ + return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \ + \ + case value_t::boolean: \ + return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \ + \ + case value_t::number_integer: \ + return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \ + \ + case value_t::number_unsigned: \ + return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \ + \ + case value_t::number_float: \ + return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float); \ + \ + case value_t::binary: \ + return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary); \ + \ + case value_t::discarded: \ + default: \ + return (unordered_result); \ + } \ + } \ + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \ + { \ + return static_cast(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_float; \ + } \ + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \ + { \ + return lhs.m_data.m_value.number_float op static_cast(rhs.m_data.m_value.number_integer); \ + } \ + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \ + { \ + return static_cast(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_float; \ + } \ + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \ + { \ + return lhs.m_data.m_value.number_float op static_cast(rhs.m_data.m_value.number_unsigned); \ + } \ + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \ + { \ + return static_cast(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_integer; \ + } \ + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \ + { \ + return lhs.m_data.m_value.number_integer op static_cast(rhs.m_data.m_value.number_unsigned); \ + } \ + else if(compares_unordered(lhs, rhs))\ + {\ + return (unordered_result);\ + }\ + \ + return (default_result); + + JSON_PRIVATE_UNLESS_TESTED: + // returns true if: + // - any operand is NaN and the other operand is of number type + // - any operand is discarded + // in legacy mode, discarded values are considered ordered if + // an operation is computed as an odd number of inverses of others + static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept + { + if ((lhs.is_number_float() && std::isnan(lhs.m_data.m_value.number_float) && rhs.is_number()) + || (rhs.is_number_float() && std::isnan(rhs.m_data.m_value.number_float) && lhs.is_number())) + { + return true; + } +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + return (lhs.is_discarded() || rhs.is_discarded()) && !inverse; +#else + static_cast(inverse); + return lhs.is_discarded() || rhs.is_discarded(); +#endif + } + + private: + bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept + { + return compares_unordered(*this, rhs, inverse); + } + + public: +#if JSON_HAS_THREE_WAY_COMPARISON + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + bool operator==(const_reference rhs) const noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const_reference lhs = *this; + JSON_IMPLEMENT_OPERATOR( ==, true, false, false) +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template + requires std::is_scalar_v + bool operator==(ScalarType rhs) const noexcept + { + return *this == basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + bool operator!=(const_reference rhs) const noexcept + { + if (compares_unordered(rhs, true)) + { + return false; + } + return !operator==(rhs); + } + + /// @brief comparison: 3-way + /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/ + std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD* + { + const_reference lhs = *this; + // default_result is used if we cannot compare values. In that case, + // we compare types. + JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD* + std::partial_ordering::equivalent, + std::partial_ordering::unordered, + lhs_type <=> rhs_type) // *NOPAD* + } + + /// @brief comparison: 3-way + /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/ + template + requires std::is_scalar_v + std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD* + { + return *this <=> basic_json(rhs); // *NOPAD* + } + +#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON + // all operators that are computed as an odd number of inverses of others + // need to be overloaded to emulate the legacy comparison behavior + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON) + bool operator<=(const_reference rhs) const noexcept + { + if (compares_unordered(rhs, true)) + { + return false; + } + return !(rhs < *this); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template + requires std::is_scalar_v + bool operator<=(ScalarType rhs) const noexcept + { + return *this <= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON) + bool operator>=(const_reference rhs) const noexcept + { + if (compares_unordered(rhs, true)) + { + return false; + } + return !(*this < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template + requires std::is_scalar_v + bool operator>=(ScalarType rhs) const noexcept + { + return *this >= basic_json(rhs); + } +#endif +#else + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + JSON_IMPLEMENT_OPERATOR( ==, true, false, false) +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + if (compares_unordered(lhs, rhs, true)) + { + return false; + } + return !(lhs == rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + // default_result is used if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type)) + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + if (compares_unordered(lhs, rhs, true)) + { + return false; + } + return !(rhs < lhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + // double inverse + if (compares_unordered(lhs, rhs)) + { + return false; + } + return !(lhs <= rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + if (compares_unordered(lhs, rhs, true)) + { + return false; + } + return !(lhs < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } +#endif + +#undef JSON_IMPLEMENT_OPERATOR + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + /// @deprecated This function is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator<<(std::ostream&, const basic_json&) instead; that is, + /// replace calls like `j >> o;` with `o << j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /// @brief deserialize from a compatible input + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief deserialize from a pair of character iterators + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + /// @deprecated This function is deprecated since 3.8.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// sax_parse(ptr, ptr + len) instead. + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia), format).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator>>(std::istream&, basic_json&) instead; that is, + /// replace calls like `j << i;` with `i >> j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /// @brief return the type as string + /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + switch (m_data.m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; + } + } + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + struct data + { + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + + data(const value_t v) + : m_type(v), m_value(v) + { + } + + data(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + } + + data() noexcept = default; + data(data&&) noexcept = default; + data(const data&) noexcept = delete; + data& operator=(data&&) noexcept = delete; + data& operator=(const data&) noexcept = delete; + + ~data() noexcept + { + m_value.destroy(m_type); + } + }; + + data m_data = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static std::vector to_bjdata(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_bjdata(j, result, use_size, use_type); + return result; + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + } + + /// @brief create a BJData serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/ + static void to_bjdata(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type, true, true); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BJData format + /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bjdata(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference operator[](const ::nlohmann::json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference operator[](const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + reference at(const ::nlohmann::json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + template::value, int> = 0> + JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) + const_reference at(const ::nlohmann::json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /// @brief return flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/flatten/ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /// @brief unflatten a previously flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /// @brief applies a JSON patch in-place without copying the object + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + void patch_inplace(const basic_json& json_patch) + { + basic_json& result = *this; + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer const top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + // parent must exist when performing patch add per RFC6902 specs + basic_json& parent = result.at(ptr); + + switch (parent.m_data.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::template array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, & result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::template array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_data.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val)); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json const v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json const v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val)); + } + } + } + } + + /// @brief applies a JSON patch to a copy of the current object + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + basic_json result = *this; + result.patch_inplace(json_patch); + return result; + } + + /// @brief creates a diff as a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/diff/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i))); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", detail::concat(path, '/', std::to_string(i))} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", detail::concat(path, "/-")}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = detail::concat(path, '/', detail::escape(it.key())); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /// @brief applies a JSON Merge Patch + /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/// @brief user-defined to_string function for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/to_string/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} + +inline namespace literals +{ +inline namespace json_literals +{ + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ +JSON_HEDLEY_NON_NULL(1) +#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + inline nlohmann::json operator ""_json(const char* s, std::size_t n) +#else + inline nlohmann::json operator "" _json(const char* s, std::size_t n) +#endif +{ + return nlohmann::json::parse(s, s + n); +} + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ +JSON_HEDLEY_NON_NULL(1) +#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n) +#else + inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +#endif +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +} // namespace json_literals +} // namespace literals +NLOHMANN_JSON_NAMESPACE_END + +/////////////////////// +// nonmember support // +/////////////////////// + +namespace std // NOLINT(cert-dcl58-cpp) +{ + +/// @brief hash value for JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_hash/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct hash // NOLINT(cert-dcl58-cpp) +{ + std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const + { + return nlohmann::detail::hash(j); + } +}; + +// specialization for std::less +template<> +struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679 +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(::nlohmann::detail::value_t lhs, + ::nlohmann::detail::value_t rhs) const noexcept + { +#if JSON_HAS_THREE_WAY_COMPARISON + return std::is_lt(lhs <=> rhs); // *NOPAD* +#else + return ::nlohmann::detail::operator<(lhs, rhs); +#endif + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/// @brief exchanges the values of two JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_swap/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap) + is_nothrow_move_assignable::value) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +#if JSON_USE_GLOBAL_UDLS + #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) + using nlohmann::literals::json_literals::operator ""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator ""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #else + using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers) + #endif +#endif + +#include + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/include/rga_func.h b/include/rga_func.h new file mode 100644 index 0000000..d053015 --- /dev/null +++ b/include/rga_func.h @@ -0,0 +1,34 @@ +#ifndef __RGA_FUNC_H__ +#define __RGA_FUNC_H__ + +#include +#include "RgaApi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(* FUNC_RGA_INIT)(); +typedef void(* FUNC_RGA_DEINIT)(); +typedef int(* FUNC_RGA_BLIT)(rga_info_t *, rga_info_t *, rga_info_t *); + +typedef struct _rga_context{ + void *rga_handle; + FUNC_RGA_INIT init_func; + FUNC_RGA_DEINIT deinit_func; + FUNC_RGA_BLIT blit_func; +} rga_context; + +int RGA_init(rga_context* rga_ctx); + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h); + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h, int w_offset, + int h_offset, RgaSURF_FORMAT color, bool add_extra_sz_w, bool add_extra_sz_h); + +int RGA_deinit(rga_context* rga_ctx); + +#ifdef __cplusplus +} +#endif +#endif/*__RGA_FUNC_H__*/ diff --git a/include/yolov5_detect.h b/include/yolov5_detect.h new file mode 100644 index 0000000..f9641f3 --- /dev/null +++ b/include/yolov5_detect.h @@ -0,0 +1,47 @@ +#ifndef _YOLOV5_DETECT_H_ +#define _YOLOV5_DETECT_H_ + +#include "yolov5_detect_postprocess.h" +#include "rknn_api.h" +#include + + +// 标准库中的base64编码器 +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); + +static inline bool is_base64(unsigned char c) ; + + + +/* + * yolov5检测初始化函数 + * ctx:输入参数,rknn_context句柄 + * path:输入参数,算法模型路径 + */ +int yolov5_detect_init(rknn_context *ctx, const char * path); + + +/* + * yolov5检测执行函数 + * ctx:输入参数,rknn_context句柄 + * input_image:输入参数,图像数据输入(cv::Mat是Opencv的类型) + * output_dets:输出参数,目标检测框输出 + */ +int yolov5_detect_run(rknn_context ctx, cv::Mat input_image, yolov5_detect_result_group_t *detect_result_group); + + +/* + * yolov5检测释放函数 + * ctx:输入参数,rknn_context句柄 + */ +int yolov5_detect_release(rknn_context ctx); + + + + +#endif diff --git a/include/yolov5_detect_postprocess.h b/include/yolov5_detect_postprocess.h new file mode 100644 index 0000000..e933fd7 --- /dev/null +++ b/include/yolov5_detect_postprocess.h @@ -0,0 +1,43 @@ +#ifndef _YOLOV5_DETECT_POSTPROCESS_H_ +#define _YOLOV5_DETECT_POSTPROCESS_H_ + +#include + +#define YOLOV5_NAME_MAX_SIZE 16 +#define YOLOV5_NUMB_MAX_SIZE 200 +#define YOLOV5_CLASS_NUM 2 +#define YOLOV5_PROP_BOX_SIZE (5+YOLOV5_CLASS_NUM) + +typedef struct +{ + int left; + int right; + int top; + int bottom; +} YOLOV5_BOX_RECT; + +typedef struct +{ + char name[YOLOV5_NAME_MAX_SIZE]; + int class_index; + YOLOV5_BOX_RECT box; + float prop; +} yolov5_detect_result_t; + +typedef struct +{ + int id; + int count; + yolov5_detect_result_t results[YOLOV5_NUMB_MAX_SIZE]; +} yolov5_detect_result_group_t; + +int yolov5_post_process_u8(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, + std::vector &qnt_zps, std::vector &qnt_scales, + yolov5_detect_result_group_t *group); + +int yolov5_post_process_fp(float *input0, float *input1, float *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, + yolov5_detect_result_group_t *group); + +#endif //_RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ diff --git a/install/yolov5-JES/lib/librknn_api.so b/install/yolov5-JES/lib/librknn_api.so new file mode 100644 index 0000000..ae3b0d1 Binary files /dev/null and b/install/yolov5-JES/lib/librknn_api.so differ diff --git a/install/yolov5-JES/yolov5-JES b/install/yolov5-JES/yolov5-JES new file mode 100644 index 0000000..1c0d17b Binary files /dev/null and b/install/yolov5-JES/yolov5-JES differ diff --git a/lib/libeasymedia.so b/lib/libeasymedia.so new file mode 100644 index 0000000..3978490 Binary files /dev/null and b/lib/libeasymedia.so differ diff --git a/lib/libeasymedia.so.1 b/lib/libeasymedia.so.1 new file mode 100644 index 0000000..3978490 Binary files /dev/null and b/lib/libeasymedia.so.1 differ diff --git a/lib/libeasymedia.so.1.0.1 b/lib/libeasymedia.so.1.0.1 new file mode 100644 index 0000000..3978490 Binary files /dev/null and b/lib/libeasymedia.so.1.0.1 differ diff --git a/libs/README.md b/libs/README.md new file mode 100644 index 0000000..f8b0898 --- /dev/null +++ b/libs/README.md @@ -0,0 +1,38 @@ +# Libs 说明 + +libs 文件夹存放 C demo 编译时依赖的库,目前分为以下子文件夹 + + + +## common + +存放无需区分使用平台的依赖库 + + + +## platform + +存放需区分使用平台的依赖库 + + + +## rklibs + +存放 Rockchip 维护的依赖库,编译任意 C demo 前需执行以下指令进行初始化 + +``` +cd rklibs + +# RK1808/RV1126/RV1109 NPU 依赖库 +git clone https://github.com/rockchip-linux/rknpu + +# RK3399pro NPU 依赖库 +git clone https://github.com/airockchip/RK3399Pro_npu + +# RK3566/RK3568/RK3588/RV1106/RV1103 NPU 依赖库 +git clone https://github.com/rockchip-linux/rknpu2 + +# RGA调用依赖库,不区分硬件平台 +git clone https://github.com/airockchip/librga +``` + diff --git a/libs/README_EN.md b/libs/README_EN.md new file mode 100644 index 0000000..cf1a328 --- /dev/null +++ b/libs/README_EN.md @@ -0,0 +1,8 @@ +## Libs + +Collection of the libs used for RKNN_C_demo + + + + + diff --git a/libs/common/CImg/CImg.h b/libs/common/CImg/CImg.h new file mode 100644 index 0000000..2bfe567 --- /dev/null +++ b/libs/common/CImg/CImg.h @@ -0,0 +1,65138 @@ +/* + # + # File : CImg.h + # ( C++ header file ) + # + # Description : C++ Template Image Processing Toolkit. + # This file is the main component of the CImg Library project. + # ( http://cimg.eu ) + # + # Project manager : David Tschumperlé + # ( http://tschumperle.users.greyc.fr/ ) + # + # A complete list of contributors is available in file 'README.txt' + # distributed within the CImg package. + # + # Licenses : This file is 'dual-licensed', you have to choose one + # of the two licenses below to apply. + # + # CeCILL-C + # The CeCILL-C license is close to the GNU LGPL. + # ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html ) + # + # or CeCILL v2.1 + # The CeCILL license is compatible with the GNU GPL. + # ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html ) + # + # This software is governed either by the CeCILL or the CeCILL-C license + # under French law and abiding by the rules of distribution of free software. + # You can use, modify and or redistribute the software under the terms of + # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA + # at the following URL: "http://cecill.info". + # + # As a counterpart to the access to the source code and rights to copy, + # modify and redistribute granted by the license, users are provided only + # with a limited warranty and the software's author, the holder of the + # economic rights, and the successive licensors have only limited + # liability. + # + # In this respect, the user's attention is drawn to the risks associated + # with loading, using, modifying and/or developing or reproducing the + # software by the user in light of its specific status of free software, + # that may mean that it is complicated to manipulate, and that also + # therefore means that it is reserved for developers and experienced + # professionals having in-depth computer knowledge. Users are therefore + # encouraged to load and test the software's suitability as regards their + # requirements in conditions enabling the security of their systems and/or + # data to be ensured and, more generally, to use and operate it in the + # same conditions as regards security. + # + # The fact that you are presently reading this means that you have had + # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. + # +*/ + +// Set version number of the library. +#ifndef cimg_version +#define cimg_version 298 + +/*----------------------------------------------------------- + # + # Test and possibly auto-set CImg configuration variables + # and include required headers. + # + # If you find that the default configuration variables are + # not adapted to your system, you can override their values + # before including the header file "CImg.h" + # (use the #define directive). + # + ------------------------------------------------------------*/ + +// Include standard C++ headers. +// This is the minimal set of required headers to make CImg-based codes compile. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define cimg_str(x) #x +#define cimg_str2(x) cimg_str(x) + +// Detect/configure OS variables. +// +// Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies). +// '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). +// '2' for Microsoft Windows. +// (auto-detection is performed if 'cimg_OS' is not set by the user). +#ifndef cimg_OS +#if defined(unix) || defined(__unix) || defined(__unix__) \ + || defined(linux) || defined(__linux) || defined(__linux__) \ + || defined(sun) || defined(__sun) \ + || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \ + || defined(__FreeBSD__) || defined (__DragonFly__) \ + || defined(sgi) || defined(__sgi) \ + || defined(__OSX__) || defined(__MACOSX__) || defined(__APPLE__) \ + || defined(__CYGWIN__) +#define cimg_OS 1 +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +#define cimg_OS 2 +#else +#define cimg_OS 0 +#endif +#elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) +#error CImg Library: Invalid configuration variable 'cimg_OS'. +#error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows'). +#endif +#ifndef cimg_date +#define cimg_date __DATE__ +#endif +#ifndef cimg_time +#define cimg_time __TIME__ +#endif + +// Disable silly warnings on some Microsoft VC++ compilers. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#pragma warning(disable:4244) +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#pragma warning(disable:4319) +#pragma warning(disable:4512) +#pragma warning(disable:4571) +#pragma warning(disable:4640) +#pragma warning(disable:4706) +#pragma warning(disable:4710) +#pragma warning(disable:4800) +#pragma warning(disable:4804) +#pragma warning(disable:4820) +#pragma warning(disable:4996) + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif +#endif + +// Define correct string functions for each compiler and OS. +#if cimg_OS==2 && defined(_MSC_VER) +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#include +#if defined(__MACOSX__) || defined(__APPLE__) +#define cimg_sscanf cimg::_sscanf +#define cimg_sprintf cimg::_sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf snprintf +#define cimg_vsnprintf vsnprintf +#endif +#endif + +// Include OS-specific headers. +#if cimg_OS==1 +#include +#include +#include +#include +#include +#include +#elif cimg_OS==2 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif +#include +#include +#include +enum {FALSE_WIN = 0}; +#endif + +// Look for C++11 features. +#ifndef cimg_use_cpp11 +#if __cplusplus>201100 +#define cimg_use_cpp11 1 +#else +#define cimg_use_cpp11 0 +#endif +#endif +#if cimg_use_cpp11==1 +#include +#include +#endif + +// Convenient macro to define pragma +#ifdef _MSC_VER +#define cimg_pragma(x) __pragma(x) +#else +#define cimg_pragma(x) _Pragma(#x) +#endif + +// Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability. +// ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ). +#if cimg_OS==2 + +#define cimg_uint64 unsigned __int64 +#define cimg_int64 __int64 +#define cimg_ulong UINT_PTR +#define cimg_long INT_PTR +#ifdef _MSC_VER +#define cimg_fuint64 "%I64u" +#define cimg_fint64 "%I64d" +#else +#define cimg_fuint64 "%llu" +#define cimg_fint64 "%lld" +#endif + +#else + +#if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)) +#define cimg_uint64 unsigned long long +#define cimg_int64 long long +#define cimg_fuint64 "%llu" +#define cimg_fint64 "%lld" +#else +#define cimg_uint64 unsigned long +#define cimg_int64 long +#define cimg_fuint64 "%lu" +#define cimg_fint64 "%ld" +#endif + +#if defined(__arm__) || defined(_M_ARM) +#define cimg_ulong unsigned long long +#define cimg_long long long +#else +#define cimg_ulong unsigned long +#define cimg_long long +#endif + +#endif + +// Configure filename separator. +// +// Filename separator is set by default to '/', except for Windows where it is '\'. +#ifndef cimg_file_separator +#if cimg_OS==2 +#define cimg_file_separator '\\' +#else +#define cimg_file_separator '/' +#endif +#endif + +// Configure verbosity of output messages. +// +// Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode). +// '1' to output library messages on the console. +// '2' to output library messages on a basic dialog window (default behavior). +// '3' to do as '1' + add extra warnings (may slow down the code!). +// '4' to do as '2' + add extra warnings (may slow down the code!). +// +// Define 'cimg_strict_warnings' to replace warning messages by exception throwns. +// +// Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals. +#ifndef cimg_verbosity +#if cimg_OS==2 +#define cimg_verbosity 2 +#else +#define cimg_verbosity 1 +#endif +#elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) +#error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. +#error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). +#endif + +// Configure OpenMP support. +// (http://www.openmp.org) +// +// Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+). +// +// OpenMP directives are used in many CImg functions to get +// advantages of multi-core CPUs. +#if !defined(cimg_use_openmp) +#ifdef _OPENMP +#define cimg_use_openmp 1 +#else +#define cimg_use_openmp 0 +#endif +#endif +#if cimg_use_openmp!=0 +#include +#define cimg_pragma_openmp(p) cimg_pragma(omp p) +#else +#define cimg_pragma_openmp(p) +#endif + +// Configure the 'abort' signal handler (does nothing by default). +// A typical signal handler can be defined in your own source like this: +// #define cimg_abort_test if (is_abort) throw CImgAbortException("") +// +// where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method. +// 'cimg_abort_test2' does the same but is called more often (in inner loops). +#if defined(cimg_abort_test) && cimg_use_openmp!=0 + +// Define abort macros to be used with OpenMP. +#ifndef _cimg_abort_init_openmp +#define _cimg_abort_init_openmp bool _cimg_abort_go_openmp = true; cimg::unused(_cimg_abort_go_openmp) +#endif +#ifndef _cimg_abort_try_openmp +#define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try +#endif +#ifndef _cimg_abort_catch_openmp +#define _cimg_abort_catch_openmp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#endif +#ifndef _cimg_abort_catch_fill_openmp +#define _cimg_abort_catch_fill_openmp \ + catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg::string(e._message).move_to(is_error); \ + cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#endif +#ifdef cimg_abort_test2 +#ifndef _cimg_abort_try_openmp2 +#define _cimg_abort_try_openmp2 _cimg_abort_try_openmp +#endif +#ifndef _cimg_abort_catch_openmp2 +#define _cimg_abort_catch_openmp2 _cimg_abort_catch_openmp +#endif +#endif +#endif + +#ifndef _cimg_abort_init_openmp +#define _cimg_abort_init_openmp +#endif +#ifndef _cimg_abort_try_openmp +#define _cimg_abort_try_openmp +#endif +#ifndef _cimg_abort_catch_openmp +#define _cimg_abort_catch_openmp +#endif +#ifndef _cimg_abort_try_openmp2 +#define _cimg_abort_try_openmp2 +#endif +#ifndef _cimg_abort_catch_openmp2 +#define _cimg_abort_catch_openmp2 +#endif +#ifndef _cimg_abort_catch_fill_openmp +#define _cimg_abort_catch_fill_openmp +#endif +#ifndef cimg_abort_init +#define cimg_abort_init +#endif +#ifndef cimg_abort_test +#define cimg_abort_test +#endif +#ifndef cimg_abort_test2 +#define cimg_abort_test2 +#endif + +// Configure display framework. +// +// Define 'cimg_display' to: '0' to disable display capabilities. +// '1' to use the X-Window framework (X11). +// '2' to use the Microsoft GDI32 framework. +#ifndef cimg_display +#if cimg_OS==0 +#define cimg_display 0 +#elif cimg_OS==1 +#define cimg_display 1 +#elif cimg_OS==2 +#define cimg_display 2 +#endif +#elif !(cimg_display==0 || cimg_display==1 || cimg_display==2) +#error CImg Library: Configuration variable 'cimg_display' is badly defined. +#error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }). +#endif + +// Include display-specific headers. +#if cimg_display==1 +#include +#include +#include +#include +#ifdef cimg_use_xshm +#include +#include +#include +#endif +#ifdef cimg_use_xrandr +#include +#endif +#endif +#ifndef cimg_appname +#define cimg_appname "CImg" +#endif + +// Configure OpenCV support. +// (http://opencv.willowgarage.com/wiki/) +// +// Define 'cimg_use_opencv' to enable OpenCV support. +// +// OpenCV library may be used to access images from cameras +// (see method 'CImg::load_camera()'). +#ifdef cimg_use_opencv +#ifdef True +#undef True +#define _cimg_redefine_True +#endif +#ifdef False +#undef False +#define _cimg_redefine_False +#endif +#ifdef Status +#undef Status +#define _cimg_redefine_Status +#endif +#include +#include +#if CV_MAJOR_VERSION>=3 +#define _cimg_fourcc cv::VideoWriter::fourcc +#define _cimg_cap_prop_frame_width cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH +#define _cimg_cap_prop_frame_height cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT +#define _cimg_cap_prop_frame_count cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT +#else +#define _cimg_fourcc CV_FOURCC +#define _cimg_cap_prop_frame_width CV_CAP_PROP_FRAME_WIDTH +#define _cimg_cap_prop_frame_height CV_CAP_PROP_FRAME_HEIGHT +#define _cimg_cap_prop_frame_count CV_CAP_PROP_FRAME_COUNT +#endif +#endif + +// Configure LibPNG support. +// (http://www.libpng.org) +// +// Define 'cimg_use_png' to enable LibPNG support. +// +// PNG library may be used to get a native support of '.png' files. +// (see methods 'CImg::{load,save}_png()'. +#ifdef cimg_use_png +extern "C" { +#include "png.h" +} +#endif + +// Configure LibJPEG support. +// (http://en.wikipedia.org/wiki/Libjpeg) +// +// Define 'cimg_use_jpeg' to enable LibJPEG support. +// +// JPEG library may be used to get a native support of '.jpg' files. +// (see methods 'CImg::{load,save}_jpeg()'). +#ifdef cimg_use_jpeg +extern "C" { +#include "jpeglib.h" +#include "setjmp.h" +} +#endif + +// Configure LibTIFF support. +// (http://www.libtiff.org) +// +// Define 'cimg_use_tiff' to enable LibTIFF support. +// +// TIFF library may be used to get a native support of '.tif' files. +// (see methods 'CImg[List]::{load,save}_tiff()'). +#ifdef cimg_use_tiff +extern "C" { +#define uint64 uint64_hack_ +#define int64 int64_hack_ +#include "tiffio.h" +#undef uint64 +#undef int64 +} +#endif + +// Configure HEIF support +// (https://github.com/strukturag/libheif) +// +// Define 'cimg_use_heif' to enable HEIF support. +// +// HEIF library may be used to get a native support of '.heic' and '.avif' files. +// (see method 'CImg::load_heif()'). +#ifdef cimg_use_heif +#include +#endif + +// Configure LibMINC2 support. +// (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference) +// +// Define 'cimg_use_minc2' to enable LibMINC2 support. +// +// MINC2 library may be used to get a native support of '.mnc' files. +// (see methods 'CImg::{load,save}_minc2()'). +#ifdef cimg_use_minc2 +#include "minc_io_simple_volume.h" +#include "minc_1_simple.h" +#include "minc_1_simple_rw.h" +#endif + +// Configure Zlib support. +// (http://www.zlib.net) +// +// Define 'cimg_use_zlib' to enable Zlib support. +// +// Zlib library may be used to allow compressed data in '.cimgz' files +// (see methods 'CImg[List]::{load,save}_cimg()'). +#ifdef cimg_use_zlib +extern "C" { +#include "zlib.h" +} +#endif + +// Configure libcurl support. +// (http://curl.haxx.se/libcurl/) +// +// Define 'cimg_use_curl' to enable libcurl support. +// +// Libcurl may be used to get a native support of file downloading from the network. +// (see method 'cimg::load_network()'.) +#ifdef cimg_use_curl +#include "curl/curl.h" +#endif + +// Configure Magick++ support. +// (http://www.imagemagick.org/Magick++) +// +// Define 'cimg_use_magick' to enable Magick++ support. +// +// Magick++ library may be used to get a native support of various image file formats. +// (see methods 'CImg::{load,save}()'). +#ifdef cimg_use_magick +#include "Magick++.h" +#endif + +// Configure FFTW3 support. +// (http://www.fftw.org) +// +// Define 'cimg_use_fftw3' to enable libFFTW3 support. +// +// FFTW3 library may be used to efficiently compute the Fast Fourier Transform +// of image data, without restriction on the image size. +// (see method 'CImg[List]::FFT()'). +#ifdef cimg_use_fftw3 +extern "C" { +#include "fftw3.h" +} +#endif + +// Configure LibBoard support. +// (http://libboard.sourceforge.net/) +// +// Define 'cimg_use_board' to enable Board support. +// +// Board library may be used to draw 3D objects in vector-graphics canvas +// that can be saved as '.ps' or '.svg' files afterwards. +// (see method 'CImg::draw_object3d()'). +#ifdef cimg_use_board +#include "Board.h" +#endif + +// Configure OpenEXR support. +// (http://www.openexr.com/) +// +// Define 'cimg_use_openexr' to enable OpenEXR support. +// +// OpenEXR library may be used to get a native support of '.exr' files. +// (see methods 'CImg::{load,save}_exr()'). +#ifdef cimg_use_openexr +#if __GNUC__>=5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" +#pragma GCC diagnostic ignored "-Wdeprecated-copy" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#include "ImfRgbaFile.h" +#include "ImfInputFile.h" +#include "ImfChannelList.h" +#include "ImfMatrixAttribute.h" +#include "ImfArray.h" +#if __GNUC__>=5 +#pragma GCC diagnostic pop +#endif +#endif + +// Configure TinyEXR support. +// (https://github.com/syoyo/tinyexr) +// +// Define 'cimg_use_tinyexr' to enable TinyEXR support. +// +// TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images. +#ifdef cimg_use_tinyexr +#ifndef TINYEXR_IMPLEMENTATION +#define TINYEXR_IMPLEMENTATION +#endif +#include "tinyexr.h" +#endif + +// Lapack configuration. +// (http://www.netlib.org/lapack) +// +// Define 'cimg_use_lapack' to enable LAPACK support. +// +// Lapack library may be used in several CImg methods to speed up +// matrix computations (eigenvalues, inverse, ...). +#ifdef cimg_use_lapack +extern "C" { + extern void sgetrf_(int*, int*, float*, int*, int*, int*); + extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); + extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); + extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); + extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); + extern void dgetrf_(int*, int*, double*, int*, int*, int*); + extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); + extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); + extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, + int*, double*, int*, double*, int*, int*); + extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); + extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*); + extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*); +} +#endif + +// Check if min/max/PI macros are defined. +// +// CImg does not compile if macros 'min', 'max' or 'PI' are defined, +// because it redefines functions min(), max() and const variable PI in the cimg:: namespace. +// so it '#undef' these macros if necessary, and restore them to reasonable +// values at the end of this file. +#ifdef min +#undef min +#define _cimg_redefine_min +#endif +#ifdef max +#undef max +#define _cimg_redefine_max +#endif +#ifdef PI +#undef PI +#define _cimg_redefine_PI +#endif + +// Define 'cimg_library' namespace suffix. +// +// You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work +// with several versions of the library at the same time. +#ifdef cimg_namespace_suffix +#define __cimg_library_suffixed(s) cimg_library_##s +#define _cimg_library_suffixed(s) __cimg_library_suffixed(s) +#define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix) +#else +#define cimg_library_suffixed cimg_library +#endif + +/*------------------------------------------------------------------------------ + # + # Define user-friendly macros. + # + # These CImg macros are prefixed by 'cimg_' and can be used safely in your own + # code. They are useful to parse command line options, or to write image loops. + # + ------------------------------------------------------------------------------*/ + +// Macros to define program usage, and retrieve command line arguments. +#define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) +#define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,_default,usage) cimg_library_suffixed::cimg::option(name,argc,argv,_default,usage) + +// Macros to define and manipulate local neighborhoods. +#define CImg_2x2(I,T) T I[4]; \ + T& I##cc = I[0]; T& I##nc = I[1]; \ + T& I##cn = I[2]; T& I##nn = I[3]; \ + I##cc = I##nc = \ + I##cn = I##nn = 0 + +#define CImg_3x3(I,T) T I[9]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ + T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ + T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ + I##pp = I##cp = I##np = \ + I##pc = I##cc = I##nc = \ + I##pn = I##cn = I##nn = 0 + +#define CImg_4x4(I,T) T I[16]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ + T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ + T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ + T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ + I##pp = I##cp = I##np = I##ap = \ + I##pc = I##cc = I##nc = I##ac = \ + I##pn = I##cn = I##nn = I##an = \ + I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_5x5(I,T) T I[25]; \ + T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \ + T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \ + T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \ + T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \ + T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \ + I##bb = I##pb = I##cb = I##nb = I##ab = \ + I##bp = I##pp = I##cp = I##np = I##ap = \ + I##bc = I##pc = I##cc = I##nc = I##ac = \ + I##bn = I##pn = I##cn = I##nn = I##an = \ + I##ba = I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_2x2x2(I,T) T I[8]; \ + T& I##ccc = I[0]; T& I##ncc = I[1]; \ + T& I##cnc = I[2]; T& I##nnc = I[3]; \ + T& I##ccn = I[4]; T& I##ncn = I[5]; \ + T& I##cnn = I[6]; T& I##nnn = I[7]; \ + I##ccc = I##ncc = \ + I##cnc = I##nnc = \ + I##ccn = I##ncn = \ + I##cnn = I##nnn = 0 + +#define CImg_3x3x3(I,T) T I[27]; \ + T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ + T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ + T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ + T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ + T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ + T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ + T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ + T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ + T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ + I##ppp = I##cpp = I##npp = \ + I##pcp = I##ccp = I##ncp = \ + I##pnp = I##cnp = I##nnp = \ + I##ppc = I##cpc = I##npc = \ + I##pcc = I##ccc = I##ncc = \ + I##pnc = I##cnc = I##nnc = \ + I##ppn = I##cpn = I##npn = \ + I##pcn = I##ccn = I##ncn = \ + I##pnn = I##cnn = I##nnn = 0 + +#define cimg_def2x2(img,x,y) \ + int _n1##x = x<(img).width() - 1?x + 1:(img).width() - 1, \ + _n1##y = y<(img).height() - 1?y + 1:(img).height() - 1 + +#define cimg_def3x3(img,x,y) \ + cimg_def2x2(img,x,y); \ + int _p1##x = x>1?x - 1:0, \ + _p1##y = y>1?y - 1:0 + +#define cimg_def4x4(img,x,y) \ + cimg_def3x3(img,x,y); \ + int _n2##x = x<(img).width() - 2?x + 2:(img).width() - 1, \ + _n2##y = y<(img).height() - 2?y + 2:(img).height() - 1 + +#define cimg_def5x5(img,x,y) \ + cimg_def4x4(img,x,y); \ + int _p2##x = x>2?x - 2:0, \ + _p2##y = y>2?y - 2:0 + +#define cimg_def6x6(img,x,y) \ + cimg_def5x5(img,x,y); \ + int _n3##x = x<(img).width() - 3?x + 3:(img).width() - 1, \ + _n3##y = y<(img).height() - 3?y + 3:(img).height() - 1 + +#define cimg_def7x7(img,x,y) \ + cimg_def6x6(img,x,y); \ + int _p3##x = x>3?x - 3:0, \ + _p3##y = y>3?y - 3:0 + +#define cimg_def8x8(img,x,y) \ + cimg_def7x7(img,x,y); \ + int _n4##x = x<(img).width() - 4?x + 4:(img).width() - 1, \ + _n4##y = y<(img).height() - 4?y + 4:(img).height() - 1 + +#define cimg_def9x9(img,x,y) \ + cimg_def8x8(img,x,y); \ + int _p4##x = x>4?x - 4:0, \ + _p4##y = y>4?y - 4:0 + +#define cimg_def2x2x2(img,x,y,z) \ + cimg_def2x2(img,x,y); \ + int _n1##z = z<(img).depth() - 1?z + 1:(img).depth() - 1 + +#define cimg_def3x3x3(img,x,y,z) \ + cimg_def2x2x2(img,x,y,z); \ + int _p1##x = x>1?x - 1:0, \ + _p1##y = y>1?y - 1:0, \ + _p1##z = z>1?z - 1:0 + +#define cimg_get2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ + I[3] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \ + I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get4x4(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \ + I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \ + I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \ + I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[15] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get5x5(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ + I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \ + I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), \ + I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \ + I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get6x6(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ + I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \ + I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \ + I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), \ + I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \ + I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \ + I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), \ + I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \ + I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \ + I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get7x7(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ + I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \ + I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), \ + I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \ + I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \ + I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), \ + I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \ + I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), \ + I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \ + I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[48] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get8x8(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ + I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \ + I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \ + I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), \ + I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), \ + I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \ + I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \ + I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \ + I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \ + I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \ + I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), \ + I[63] = (T)(img)(_n4##x,_n4##y,z,c); + +#define cimg_get9x9(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), \ + I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \ + I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \ + I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \ + I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), \ + I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \ + I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \ + I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), \ + I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), \ + I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \ + I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \ + I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \ + I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \ + I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \ + I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \ + I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), \ + I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \ + I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c) + +#define cimg_get2x2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ + I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \ + I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +#define cimg_get3x3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \ + I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \ + I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \ + I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \ + I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \ + I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \ + I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \ + I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \ + I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \ + I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +// Macros to perform various image loops. +// +// These macros are simpler to use than loops with C++ iterators. +#define cimg_for(img,ptrs,T_ptrs) \ + for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs) +#define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs) +#define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off) +#define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off) + +#define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) +#define cimg_forX(img,x) cimg_for1((img)._width,x) +#define cimg_forY(img,y) cimg_for1((img)._height,y) +#define cimg_forZ(img,z) cimg_for1((img)._depth,z) +#define cimg_forC(img,c) cimg_for1((img)._spectrum,c) +#define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) +#define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) +#define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) +#define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x) +#define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y) +#define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z) +#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) +#define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y) +#define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z) +#define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z) +#define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z) + +#define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i) +#define cimg_rofX(img,x) cimg_rof1((img)._width,x) +#define cimg_rofY(img,y) cimg_rof1((img)._height,y) +#define cimg_rofZ(img,z) cimg_rof1((img)._depth,z) +#define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c) +#define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x) +#define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x) +#define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y) +#define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x) +#define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y) +#define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z) +#define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y) +#define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y) +#define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z) +#define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z) +#define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z) + +#define cimg_for_in1(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i) +#define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x) +#define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y) +#define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z) +#define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c) +#define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z) +#define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) +#define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x) +#define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y) +#define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z) +#define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c) +#define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) +#define cimg_for_insideXYZ(img,x,y,z,n) \ + cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) +#define cimg_for_insideXYZC(img,x,y,z,c,n) \ + cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) + +#define cimg_for_out1(boundi,i0,i1,i) \ + for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i) +#define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \ + for (int j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ + ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \ + for (int k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ + ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \ + for (int l = 0; l<(int)(boundl); ++l) \ + for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \ + i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x) +#define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y) +#define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z) +#define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c) +#define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y) +#define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z) +#define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c) +#define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z) +#define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c) +#define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c) +#define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \ + cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \ + cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c) +#define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \ + cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c) +#define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \ + cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c) +#define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) +#define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x) +#define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y) +#define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z) +#define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c) +#define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) +#define cimg_for_borderXYZ(img,x,y,z,n) \ + cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) +#define cimg_for_borderXYZC(img,x,y,z,c,n) \ + cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \ + (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c) + +#define cimg_for_spiralXY(img,x,y) \ + for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \ + --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\ + ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1) + +#define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ + for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ + _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \ + _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \ + _counter = _dx, \ + _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ + _counter>=0; \ + --_counter, x+=_steep? \ + (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ + (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) + +#define cimg_for2(bound,i) \ + for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + ++i, ++_n1##i) +#define cimg_for2X(img,x) cimg_for2((img)._width,x) +#define cimg_for2Y(img,y) cimg_for2((img)._height,y) +#define cimg_for2Z(img,z) cimg_for2((img)._depth,z) +#define cimg_for2C(img,c) cimg_for2((img)._spectrum,c) +#define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) +#define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) +#define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x) +#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) +#define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y) +#define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z) +#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) +#define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z) +#define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z) +#define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z) + +#define cimg_for_in2(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + ++i, ++_n1##i) +#define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x) +#define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y) +#define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z) +#define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c) +#define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z) +#define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i) +#define cimg_for3X(img,x) cimg_for3((img)._width,x) +#define cimg_for3Y(img,y) cimg_for3((img)._height,y) +#define cimg_for3Z(img,z) cimg_for3((img)._depth,z) +#define cimg_for3C(img,c) cimg_for3((img)._spectrum,c) +#define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) +#define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) +#define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x) +#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) +#define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y) +#define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z) +#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) +#define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z) +#define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z) +#define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z) + +#define cimg_for_in3(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + _p1##i = i++, ++_n1##i) +#define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x) +#define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y) +#define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z) +#define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c) +#define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z) +#define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for4(bound,i) \ + for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for4X(img,x) cimg_for4((img)._width,x) +#define cimg_for4Y(img,y) cimg_for4((img)._height,y) +#define cimg_for4Z(img,z) cimg_for4((img)._depth,z) +#define cimg_for4C(img,c) cimg_for4((img)._spectrum,c) +#define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) +#define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) +#define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x) +#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) +#define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y) +#define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z) +#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) +#define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z) +#define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z) +#define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z) + +#define cimg_for_in4(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x) +#define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y) +#define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z) +#define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c) +#define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z) +#define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for5(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for5X(img,x) cimg_for5((img)._width,x) +#define cimg_for5Y(img,y) cimg_for5((img)._height,y) +#define cimg_for5Z(img,z) cimg_for5((img)._depth,z) +#define cimg_for5C(img,c) cimg_for5((img)._spectrum,c) +#define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) +#define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) +#define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x) +#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) +#define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y) +#define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z) +#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) +#define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z) +#define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z) +#define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z) + +#define cimg_for_in5(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x) +#define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y) +#define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z) +#define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c) +#define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z) +#define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for6(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for6X(img,x) cimg_for6((img)._width,x) +#define cimg_for6Y(img,y) cimg_for6((img)._height,y) +#define cimg_for6Z(img,z) cimg_for6((img)._depth,z) +#define cimg_for6C(img,c) cimg_for6((img)._spectrum,c) +#define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) +#define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) +#define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x) +#define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) +#define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y) +#define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z) +#define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) +#define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z) +#define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z) +#define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z) + +#define cimg_for_in6(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ + i<=(int)(i1) && \ + (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x) +#define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y) +#define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z) +#define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c) +#define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z) +#define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for7(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for7X(img,x) cimg_for7((img)._width,x) +#define cimg_for7Y(img,y) cimg_for7((img)._height,y) +#define cimg_for7Z(img,z) cimg_for7((img)._depth,z) +#define cimg_for7C(img,c) cimg_for7((img)._spectrum,c) +#define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) +#define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) +#define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x) +#define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) +#define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y) +#define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z) +#define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) +#define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z) +#define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z) +#define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z) + +#define cimg_for_in7(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ + i<=(int)(i1) && \ + (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x) +#define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y) +#define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z) +#define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c) +#define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z) +#define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for8(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3, \ + _n4##i = 4>=(bound)?(int)(bound) - 1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for8X(img,x) cimg_for8((img)._width,x) +#define cimg_for8Y(img,y) cimg_for8((img)._height,y) +#define cimg_for8Z(img,z) cimg_for8((img)._depth,z) +#define cimg_for8C(img,c) cimg_for8((img)._spectrum,c) +#define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x) +#define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x) +#define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x) +#define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) +#define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y) +#define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z) +#define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) +#define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z) +#define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z) +#define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z) + +#define cimg_for_in8(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ + _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x) +#define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y) +#define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z) +#define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c) +#define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z) +#define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for9(bound,i) \ + for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \ + _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for9X(img,x) cimg_for9((img)._width,x) +#define cimg_for9Y(img,y) cimg_for9((img)._height,y) +#define cimg_for9Z(img,z) cimg_for9((img)._depth,z) +#define cimg_for9C(img,c) cimg_for9((img)._spectrum,c) +#define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x) +#define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x) +#define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x) +#define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) +#define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y) +#define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z) +#define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) +#define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z) +#define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z) +#define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z) + +#define cimg_for_in9(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p4##i = i - 4<0?0:i - 4, \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ + _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x) +#define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y) +#define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z) +#define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c) +#define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z) +#define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[4] = (T)(img)(x,y,z,c)), \ + (I[7] = (T)(img)(x,_n1##y,z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for4x4(img,x,y,z,c,I,T) \ + cimg_for4((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = I[5] = (T)(img)(0,y,z,c)), \ + (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width() - 1:2); \ + (_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = (T)(img)(_p1##x,y,z,c)), \ + (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[5] = (T)(img)(x,y,z,c)), \ + (I[9] = (T)(img)(x,_n1##y,z,c)), \ + (I[13] = (T)(img)(x,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for5x5(img,x,y,z,c,I,T) \ + cimg_for5((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \ + (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \ + (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width() - 1:2); \ + (_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[10] = (T)(img)(_p2##x,y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[11] = (T)(img)(_p1##x,y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[7] = (T)(img)(x,_p1##y,z,c)), \ + (I[12] = (T)(img)(x,y,z,c)), \ + (I[17] = (T)(img)(x,_n1##y,z,c)), \ + (I[22] = (T)(img)(x,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for6x6(img,x,y,z,c,I,T) \ + cimg_for6((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = 2>=(img)._width?(img).width() - 1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \ + (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \ + (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \ + (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width() - 1:3); \ + (_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p2##x,y,z,c)), \ + (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_p1##x,y,z,c)), \ + (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[8] = (T)(img)(x,_p1##y,z,c)), \ + (I[14] = (T)(img)(x,y,z,c)), \ + (I[20] = (T)(img)(x,_n1##y,z,c)), \ + (I[26] = (T)(img)(x,_n2##y,z,c)), \ + (I[32] = (T)(img)(x,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for7x7(img,x,y,z,c,I,T) \ + cimg_for7((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = 2>=(img)._width?(img).width() - 1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \ + (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \ + (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \ + (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \ + (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \ + (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width() - 1:3); \ + (_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[21] = (T)(img)(_p3##x,y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[22] = (T)(img)(_p2##x,y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[23] = (T)(img)(_p1##x,y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[10] = (T)(img)(x,_p2##y,z,c)), \ + (I[17] = (T)(img)(x,_p1##y,z,c)), \ + (I[24] = (T)(img)(x,y,z,c)), \ + (I[31] = (T)(img)(x,_n1##y,z,c)), \ + (I[38] = (T)(img)(x,_n2##y,z,c)), \ + (I[45] = (T)(img)(x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for8x8(img,x,y,z,c,I,T) \ + cimg_for8((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ + _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ + _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \ + (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \ + (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \ + (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \ + (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \ + (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \ + (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width() - 1:4); \ + (_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ + _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[24] = (T)(img)(_p3##x,y,z,c)), \ + (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_p2##x,y,z,c)), \ + (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_p1##x,y,z,c)), \ + (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[11] = (T)(img)(x,_p2##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,z,c)), \ + (I[27] = (T)(img)(x,y,z,c)), \ + (I[35] = (T)(img)(x,_n1##y,z,c)), \ + (I[43] = (T)(img)(x,_n2##y,z,c)), \ + (I[51] = (T)(img)(x,_n3##y,z,c)), \ + (I[59] = (T)(img)(x,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x + 4>=(img).width()?(img).width() - 1:x + 4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for9x9(img,x,y,z,c,I,T) \ + cimg_for9((img)._height,y) for (int x = 0, \ + _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ + _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ + _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \ + (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \ + (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \ + (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \ + (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \ + (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \ + (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \ + (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width() - 1:4); \ + (_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ + I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ + I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ + I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p4##x = x - 4<0?0:x - 4, \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ + _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \ + (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \ + (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \ + (I[36] = (T)(img)(_p4##x,y,z,c)), \ + (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \ + (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \ + (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \ + (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \ + (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[37] = (T)(img)(_p3##x,y,z,c)), \ + (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \ + (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[38] = (T)(img)(_p2##x,y,z,c)), \ + (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[39] = (T)(img)(_p1##x,y,z,c)), \ + (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[4] = (T)(img)(x,_p4##y,z,c)), \ + (I[13] = (T)(img)(x,_p3##y,z,c)), \ + (I[22] = (T)(img)(x,_p2##y,z,c)), \ + (I[31] = (T)(img)(x,_p1##y,z,c)), \ + (I[40] = (T)(img)(x,y,z,c)), \ + (I[49] = (T)(img)(x,_n1##y,z,c)), \ + (I[58] = (T)(img)(x,_n2##y,z,c)), \ + (I[67] = (T)(img)(x,_n3##y,z,c)), \ + (I[76] = (T)(img)(x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x + 4>=(img).width()?(img).width() - 1:x + 4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ + I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ + I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ + I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for2x2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + (I[4] = (T)(img)(0,y,_n1##z,c)), \ + (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ + cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + (I[4] = (T)(img)(x,y,_n1##z,c)), \ + (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for3x3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \ + (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \ + (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \ + (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \ + (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ + cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \ + (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,y,z,c)), \ + (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \ + (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \ + (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \ + (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \ + (I[4] = (T)(img)(x,y,_p1##z,c)), \ + (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \ + (I[10] = (T)(img)(x,_p1##y,z,c)), \ + (I[13] = (T)(img)(x,y,z,c)), \ + (I[16] = (T)(img)(x,_n1##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \ + (I[22] = (T)(img)(x,y,_n1##z,c)), \ + (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l) +#define cimglist_rof(list,l) for (int l = (int)(list)._width - 1; l>=0; --l) +#define cimglist_for_in(list,l0,l1,l) \ + for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \ + l<=_max##l; ++l) + +#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn + +// Macros used to display error messages when exceptions are thrown. +// You should not use these macros is your own code. +#define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::" +#define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']' +#define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::" +#define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type() +#define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::" +#define cimglist_instance _width,_allocated_width,_data,pixel_type() + +/*------------------------------------------------ + # + # + # Define cimg_library:: namespace + # + # + -------------------------------------------------*/ +//! Contains all classes and functions of the \CImg library. +/** + This namespace is defined to avoid functions and class names collisions + that could happen with the inclusion of other C++ header files. + Anyway, it should not happen often and you should reasonably start most of your + \CImg-based programs with + \code + #include "CImg.h" + using namespace cimg_library; + \endcode + to simplify the declaration of \CImg Library objects afterwards. +**/ +namespace cimg_library_suffixed { + + // Declare the four classes of the CImg Library. + template struct CImg; + template struct CImgList; + struct CImgDisplay; + struct CImgException; + + // Declare cimg:: namespace. + // This is an incomplete namespace definition here. It only contains some + // necessary stuff to ensure a correct declaration order of the classes and functions + // defined afterwards. + namespace cimg { + + // Define character sequences for colored terminal output. +#ifdef cimg_use_vt100 + static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; + static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; + static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; + static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; + static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; + static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; + static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; + static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; + static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; + static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; + static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; +#else + static const char t_normal[] = { 0 }; + static const char *const t_black = cimg::t_normal, + *const t_red = cimg::t_normal, + *const t_green = cimg::t_normal, + *const t_yellow = cimg::t_normal, + *const t_blue = cimg::t_normal, + *const t_magenta = cimg::t_normal, + *const t_cyan = cimg::t_normal, + *const t_white = cimg::t_normal, + *const t_bold = cimg::t_normal, + *const t_underscore = cimg::t_normal; +#endif + + inline std::FILE* output(std::FILE *file=0); + inline void info(); + + //! Avoid warning messages due to unused parameters. Do nothing actually. + template + inline void unused(const T&, ...) {} + + // [internal] Lock/unlock a mutex for managing concurrent threads. + // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }. + // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg. + inline int mutex(const unsigned int n, const int lock_mode=1); + + inline unsigned int& exception_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = cimg_verbosity; + if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } + return mode; + } + + // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. + inline FILE* _stdin(const bool throw_exception=true); + inline FILE* _stdout(const bool throw_exception=true); + inline FILE* _stderr(const bool throw_exception=true); + + // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character + // at the end of the string. +#if cimg_OS==2 && defined(_MSC_VER) + inline int _snprintf(char *const s, const size_t size, const char *const format, ...) { + va_list ap; + va_start(ap,format); + const int result = _vsnprintf(s,size,format,ap); + va_end(ap); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) { + int result = -1; + cimg::mutex(6); + if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); + if (result==-1) result = _vscprintf(format,ap); + cimg::mutex(6,0); + return result; + } + + // Mutex-protected version of sscanf, sprintf and snprintf. + // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX. +#elif defined(__MACOSX__) || defined(__APPLE__) + inline int _sscanf(const char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsscanf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _sprintf(char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsprintf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _snprintf(char *const s, const size_t n, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsnprintf(s,n,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) { + cimg::mutex(6); + const int result = std::vsnprintf(s,size,format,ap); + cimg::mutex(6,0); + return result; + } +#endif + + //! Set current \CImg exception mode. + /** + The way error messages are handled by \CImg can be changed dynamically, using this function. + \param mode Desired exception mode. Possible values are: + - \c 0: Hide library messages (quiet mode). + - \c 1: Print library messages on the console. + - \c 2: Display library messages on a dialog window. + - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!). + - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!). + **/ + inline unsigned int& exception_mode(const unsigned int mode) { + return exception_mode(mode,true); + } + + //! Return current \CImg exception mode. + /** + \note By default, return the value of configuration macro \c cimg_verbosity + **/ + inline unsigned int& exception_mode() { + return exception_mode(0,false); + } + + inline unsigned int openmp_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = 2; + if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } + return mode; + } + + //! Set current \CImg openmp mode. + /** + The way openmp-based methods are handled by \CImg can be changed dynamically, using this function. + \param mode Desired openmp mode. Possible values are: + - \c 0: Never parallelize. + - \c 1: Always parallelize. + - \c 2: Adaptive parallelization mode (default behavior). + **/ + inline unsigned int openmp_mode(const unsigned int mode) { + return openmp_mode(mode,true); + } + + //! Return current \CImg openmp mode. + inline unsigned int openmp_mode() { + return openmp_mode(0,false); + } + +#ifndef cimg_openmp_sizefactor +#define cimg_openmp_sizefactor 1 +#endif +#define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond)))) +#define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size)) +#ifdef _MSC_VER +// Disable 'collapse()' directive for MSVC (supports only OpenMP 2.0). +#define cimg_openmp_collapse(k) +#else +#define cimg_openmp_collapse(k) collapse(k) +#endif + +#if cimg_OS==2 +// Disable parallelization of simple loops on Windows, due to noticed performance drop. +#define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr); +#else +#define cimg_openmp_for(instance,expr,min_size) \ + cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \ + cimg_rof((instance),ptr,T) *ptr = (T)(expr); +#endif + + // Display a simple dialog box, and wait for the user's response. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label="OK", const char *const button2_label=0, + const char *const button3_label=0, const char *const button4_label=0, + const char *const button5_label=0, const char *const button6_label=0, + const bool centering=false); + + // Evaluate math expression. + inline double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0); + + } // namespace cimg { ... + + /*--------------------------------------- + # + # Define the CImgException structures + # + --------------------------------------*/ + //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call. + /** + \par Overview + + CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException). + CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. + These classes can be: + + - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. + This is the only \c non-derived exception class. + + - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. + This is probably one of the most thrown exception by \CImg. + For instance, the following example throws a \c CImgArgumentException: + \code + CImg img(100,100,1,3); // Define a 100x100 color image with float-valued pixels + img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis + \endcode + + - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances. + + - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit + the function requirements. For instance, the following example throws a \c CImgInstanceException: + \code + const CImg img; // Define an empty image + const float value = img.at(0); // Try to read first pixel value (does not exist) + \endcode + + - \b CImgIOException: Thrown when an error occurred when trying to load or save image files. + This happens when trying to read files that do not exist or with invalid formats. + For instance, the following example throws a \c CImgIOException: + \code + const CImg img("missing_file.jpg"); // Try to load a file that does not exist + \endcode + + - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and + when a \CImg function has to display a warning message (see cimg::warn()). + + It is not recommended to throw CImgException instances by yourself, + since they are expected to be thrown only by \CImg. + When an error occurs in a library function call, \CImg may display error messages on the screen or on the + standard output, depending on the current \CImg exception mode. + The \CImg exception mode can be get and set by functions cimg::exception_mode() and + cimg::exception_mode(unsigned int). + + \par Exceptions handling + + In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown. + This may lead the program to break (this is the default behavior), but you can bypass this behavior by + handling the exceptions by yourself, + using a usual try { ... } catch () { ... } bloc, as in the following example: + \code + #define "CImg.h" + using namespace cimg_library; + int main() { + cimg::exception_mode(0); // Enable quiet exception mode + try { + ... // Here, do what you want to stress CImg + } catch (CImgException& e) { // You succeeded: something went wrong! + std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message + ... // Do what you want now to save the ship! + } + } + \endcode + **/ + struct CImgException : public std::exception { +#define _cimg_exception_err(etype,disp_flag) \ + std::va_list ap, ap2; \ + va_start(ap,format); va_start(ap2,format); \ + int size = cimg_vsnprintf(0,0,format,ap2); \ + if (size++>=0) { \ + delete[] _message; \ + _message = new char[(size_t)size]; \ + cimg_vsnprintf(_message,(size_t)size,format,ap); \ + if (cimg::exception_mode()) { \ + std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ + if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \ + catch (CImgException&) {} \ + if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ + } \ + } \ + va_end(ap); va_end(ap2); + + char *_message; + CImgException() { _message = new char[1]; *_message = 0; } + CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } + CImgException(const CImgException& e):std::exception(e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgException() throw() { delete[] _message; } + CImgException& operator=(const CImgException& e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; // struct CImgException { ... + + // The CImgAbortException class is used to throw an exception when + // a computationally-intensive function has been aborted by an external signal. + struct CImgAbortException : public std::exception { + char *_message; + CImgAbortException() { _message = new char[1]; *_message = 0; } + CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); } + CImgAbortException(const CImgAbortException& e):std::exception(e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgAbortException() throw() { delete[] _message; } + CImgAbortException& operator=(const CImgAbortException& e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; // struct CImgAbortException { ... + + // The CImgArgumentException class is used to throw an exception related + // to invalid arguments encountered in a library function call. + struct CImgArgumentException : public CImgException { + CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); } + }; // struct CImgArgumentException { ... + + // The CImgDisplayException class is used to throw an exception related + // to display problems encountered in a library function call. + struct CImgDisplayException : public CImgException { + CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); } + }; // struct CImgDisplayException { ... + + // The CImgInstanceException class is used to throw an exception related + // to an invalid instance encountered in a library function call. + struct CImgInstanceException : public CImgException { + CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + }; // struct CImgInstanceException { ... + + // The CImgIOException class is used to throw an exception related + // to input/output file problems encountered in a library function call. + struct CImgIOException : public CImgException { + CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); } + }; // struct CImgIOException { ... + + // The CImgWarningException class is used to throw an exception for warnings + // encountered in a library function call. + struct CImgWarningException : public CImgException { + CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); } + }; // struct CImgWarningException { ... + + /*------------------------------------- + # + # Define cimg:: namespace + # + -----------------------------------*/ + //! Contains \a low-level functions and variables of the \CImg Library. + /** + Most of the functions and variables within this namespace are used by the \CImg library for low-level operations. + You may use them to access specific const values or environment variables internally used by \CImg. + \warning Never write using namespace cimg_library::cimg; in your source code. Lot of functions in the + cimg:: namespace have the same names as standard C functions that may be defined in the global + namespace ::. + **/ + namespace cimg { + + // Define traits that will be used to determine the best data type to work in CImg functions. + // + template struct type { + static const char* string() { + static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", + "unknown32", "unknown40", "unknown48", "unknown56", + "unknown64", "unknown72", "unknown80", "unknown88", + "unknown96", "unknown104", "unknown112", "unknown120", + "unknown128" }; + return s[(sizeof(T)<17)?sizeof(T):0]; + } + static bool is_float() { return false; } + static bool is_inf(const T) { return false; } + static bool is_nan(const T) { return false; } + static bool is_finite(const T) { return true; } + static T min() { return ~max(); } + static T max() { return (T)1<<(8*sizeof(T) - 1); } + static T inf() { return max(); } + static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } + static const char* format() { return "%s"; } + static const char* format_s() { return "%s"; } + static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "bool"; return s; } + static bool is_float() { return false; } + static bool is_inf(const bool) { return false; } + static bool is_nan(const bool) { return false; } + static bool is_finite(const bool) { return true; } + static bool min() { return false; } + static bool max() { return true; } + static bool inf() { return max(); } + static bool is_inf() { return false; } + static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } + static const char* format() { return "%s"; } + static const char* format_s() { return "%s"; } + static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned char) { return false; } + static bool is_nan(const unsigned char) { return false; } + static bool is_finite(const unsigned char) { return true; } + static unsigned char min() { return 0; } + static unsigned char max() { return (unsigned char)-1; } + static unsigned char inf() { return max(); } + static unsigned char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned char val) { return (unsigned int)val; } + }; + +#if defined(CHAR_MAX) && CHAR_MAX==255 + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static bool is_finite(const char) { return true; } + static char min() { return 0; } + static char max() { return (char)-1; } + static char inf() { return max(); } + static char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const char val) { return (unsigned int)val; } + }; +#else + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static bool is_finite(const char) { return true; } + static char min() { return ~max(); } + static char max() { return (char)((unsigned char)-1>>1); } + static char inf() { return max(); } + static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const char val) { return (int)val; } + }; +#endif + + template<> struct type { + static const char* string() { static const char *const s = "signed char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const signed char) { return false; } + static bool is_nan(const signed char) { return false; } + static bool is_finite(const signed char) { return true; } + static signed char min() { return ~max(); } + static signed char max() { return (signed char)((unsigned char)-1>>1); } + static signed char inf() { return max(); } + static signed char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(signed char)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const signed char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned short) { return false; } + static bool is_nan(const unsigned short) { return false; } + static bool is_finite(const unsigned short) { return true; } + static unsigned short min() { return 0; } + static unsigned short max() { return (unsigned short)-1; } + static unsigned short inf() { return max(); } + static unsigned short cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned short val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const short) { return false; } + static bool is_nan(const short) { return false; } + static bool is_finite(const short) { return true; } + static short min() { return ~max(); } + static short max() { return (short)((unsigned short)-1>>1); } + static short inf() { return max(); } + static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const short val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned int) { return false; } + static bool is_nan(const unsigned int) { return false; } + static bool is_finite(const unsigned int) { return true; } + static unsigned int min() { return 0; } + static unsigned int max() { return (unsigned int)-1; } + static unsigned int inf() { return max(); } + static unsigned int cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const int) { return false; } + static bool is_nan(const int) { return false; } + static bool is_finite(const int) { return true; } + static int min() { return ~max(); } + static int max() { return (int)((unsigned int)-1>>1); } + static int inf() { return max(); } + static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_uint64) { return false; } + static bool is_nan(const cimg_uint64) { return false; } + static bool is_finite(const cimg_uint64) { return true; } + static cimg_uint64 min() { return 0; } + static cimg_uint64 max() { return (cimg_uint64)-1; } + static cimg_uint64 inf() { return max(); } + static cimg_uint64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } + static const char* format() { return cimg_fuint64; } + static const char* format_s() { return cimg_fuint64; } + static cimg_uint64 format(const cimg_uint64 val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_int64) { return false; } + static bool is_nan(const cimg_int64) { return false; } + static bool is_finite(const cimg_int64) { return true; } + static cimg_int64 min() { return ~max(); } + static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } + static cimg_int64 inf() { return max(); } + static cimg_int64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; + } + static const char* format() { return cimg_fint64; } + static const char* format_s() { return cimg_fint64; } + static long format(const long val) { return (long)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const double val) { // Custom version that works with '-ffast-math' + if (sizeof(double)==8) { + cimg_uint64 u; + std::memcpy(&u,&val,sizeof(double)); + return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000; + } +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const double val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static double min() { return -DBL_MAX; } + static double max() { return DBL_MAX; } + static double inf() { +#ifdef INFINITY + return (double)INFINITY; +#else + return max()*max(); +#endif + } + static double nan() { +#ifdef NAN + return (double)NAN; +#else + const double val_nan = -std::sqrt(-1.); return val_nan; +#endif + } + static double cut(const double val) { return val; } + static const char* format() { return "%.17g"; } + static const char* format_s() { return "%g"; } + static double format(const double val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "float"; return s; } + static bool is_float() { return true; } + static bool is_inf(const float val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const float val) { // Custom version that works with '-ffast-math' + if (sizeof(float)==4) { + unsigned int u; + std::memcpy(&u,&val,sizeof(float)); + return (u&0x7fffffff)>0x7f800000; + } +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const float val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static float min() { return -FLT_MAX; } + static float max() { return FLT_MAX; } + static float inf() { return (float)cimg::type::inf(); } + static float nan() { return (float)cimg::type::nan(); } + static float cut(const double val) { return (float)val; } + static float cut(const float val) { return (float)val; } + static const char* format() { return "%.9g"; } + static const char* format_s() { return "%g"; } + static double format(const float val) { return (double)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "long double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const long double val) { +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const long double val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static long double min() { return -LDBL_MAX; } + static long double max() { return LDBL_MAX; } + static long double inf() { return max()*max(); } + static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; } + static long double cut(const long double val) { return val; } + static const char* format() { return "%.17g"; } + static const char* format_s() { return "%g"; } + static double format(const long double val) { return (double)val; } + }; + +#ifdef cimg_use_half + template<> struct type { + static const char* string() { static const char *const s = "half"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const half val) { // Custom version that works with '-ffast-math' + if (sizeof(half)==2) { + short u; + std::memcpy(&u,&val,sizeof(short)); + return (bool)((u&0x7fff)>0x7c00); + } + return cimg::type::is_nan((float)val); + } + static bool is_finite(const half val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static half min() { return (half)-65504; } + static half max() { return (half)65504; } + static half inf() { return max()*max(); } + static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; } + static half cut(const double val) { return (half)val; } + static const char* format() { return "%.9g"; } + static const char* format_s() { return "%g"; } + static double format(const half val) { return (double)val; } + }; +#endif + + template struct superset { typedef T type; }; + template<> struct superset { typedef unsigned char type; }; + template<> struct superset { typedef char type; }; + template<> struct superset { typedef signed char type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + +#ifdef cimg_use_half + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; +#endif + + template struct superset2 { + typedef typename superset::type>::type type; + }; + + template struct superset3 { + typedef typename superset::type>::type type; + }; + + template struct last { typedef t2 type; }; + +#define _cimg_Tt typename cimg::superset::type +#define _cimg_Tfloat typename cimg::superset::type +#define _cimg_tfloat typename cimg::superset::type +#define _cimg_Ttfloat typename cimg::superset2::type +#define _cimg_Ttdouble typename cimg::superset2::type + + // Define variables used internally by CImg. +#if cimg_display==1 + struct X11_static { + unsigned int nb_wins; + pthread_t *events_thread; + pthread_cond_t wait_event; + pthread_mutex_t wait_event_mutex; + CImgDisplay **wins; + Display *display; + unsigned int nb_bits; + bool is_blue_first; + bool is_shm_enabled; + bool byte_order; + +#ifdef cimg_use_xrandr + XRRScreenSize *resolutions; + Rotation curr_rotation; + unsigned int curr_resolution; + unsigned int nb_resolutions; +#endif + X11_static():nb_wins(0),events_thread(0),display(0), + nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) { +#ifdef __FreeBSD__ + XInitThreads(); +#endif + wins = new CImgDisplay*[1024]; + pthread_mutex_init(&wait_event_mutex,0); + pthread_cond_init(&wait_event,0); + +#ifdef cimg_use_xrandr + resolutions = 0; + curr_rotation = 0; + curr_resolution = nb_resolutions = 0; +#endif + } + + ~X11_static() { + delete[] wins; + /* + if (events_thread) { + pthread_cancel(*events_thread); + delete events_thread; + } + if (display) { } // XCloseDisplay(display); } + pthread_cond_destroy(&wait_event); + pthread_mutex_unlock(&wait_event_mutex); + pthread_mutex_destroy(&wait_event_mutex); + */ + } + }; // struct X11_static { ... +#if defined(cimg_module) + X11_static& X11_attr(); +#elif defined(cimg_main) + X11_static& X11_attr() { static X11_static val; return val; } +#else + inline X11_static& X11_attr() { static X11_static val; return val; } +#endif + +#elif cimg_display==2 + struct Win32_static { + HANDLE wait_event; + Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); } + }; // struct Win32_static { ... +#if defined(cimg_module) + Win32_static& Win32_attr(); +#elif defined(cimg_main) + Win32_static& Win32_attr() { static Win32_static val; return val; } +#else + inline Win32_static& Win32_attr() { static Win32_static val; return val; } +#endif +#endif +#define cimg_lock_display() cimg::mutex(15) +#define cimg_unlock_display() cimg::mutex(15,0) + + struct Mutex_static { +#if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1) + pthread_mutex_t mutex[32]; + Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); } + void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); } + void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); } + int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); } +#elif cimg_OS==2 + HANDLE mutex[32]; + Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); } + void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); } + void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); } + int trylock(const unsigned int) { return 0; } +#else + Mutex_static() {} + void lock(const unsigned int) {} + void unlock(const unsigned int) {} + int trylock(const unsigned int) { return 0; } +#endif + }; // struct Mutex_static { ... +#if defined(cimg_module) + Mutex_static& Mutex_attr(); +#elif defined(cimg_main) + Mutex_static& Mutex_attr() { static Mutex_static val; return val; } +#else + inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; } +#endif + +#if defined(cimg_use_magick) + struct Magick_static { + Magick_static() { + Magick::InitializeMagick(""); + } + }; // struct Magick_static { ... + static Magick_static _Magick_static; +#endif + +#if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread) + struct FFTW3_static { + FFTW3_static() { + fftw_init_threads(); + } + }; // struct FFTW3_static { ... + static FFTW3_static _FFTW3_static; +#endif + +#if cimg_display==1 + // Define keycodes for X11-based graphical systems. + const unsigned int keyESC = XK_Escape; + const unsigned int keyF1 = XK_F1; + const unsigned int keyF2 = XK_F2; + const unsigned int keyF3 = XK_F3; + const unsigned int keyF4 = XK_F4; + const unsigned int keyF5 = XK_F5; + const unsigned int keyF6 = XK_F6; + const unsigned int keyF7 = XK_F7; + const unsigned int keyF8 = XK_F8; + const unsigned int keyF9 = XK_F9; + const unsigned int keyF10 = XK_F10; + const unsigned int keyF11 = XK_F11; + const unsigned int keyF12 = XK_F12; + const unsigned int keyPAUSE = XK_Pause; + const unsigned int key1 = XK_1; + const unsigned int key2 = XK_2; + const unsigned int key3 = XK_3; + const unsigned int key4 = XK_4; + const unsigned int key5 = XK_5; + const unsigned int key6 = XK_6; + const unsigned int key7 = XK_7; + const unsigned int key8 = XK_8; + const unsigned int key9 = XK_9; + const unsigned int key0 = XK_0; + const unsigned int keyBACKSPACE = XK_BackSpace; + const unsigned int keyINSERT = XK_Insert; + const unsigned int keyHOME = XK_Home; + const unsigned int keyPAGEUP = XK_Page_Up; + const unsigned int keyTAB = XK_Tab; + const unsigned int keyQ = XK_q; + const unsigned int keyW = XK_w; + const unsigned int keyE = XK_e; + const unsigned int keyR = XK_r; + const unsigned int keyT = XK_t; + const unsigned int keyY = XK_y; + const unsigned int keyU = XK_u; + const unsigned int keyI = XK_i; + const unsigned int keyO = XK_o; + const unsigned int keyP = XK_p; + const unsigned int keyDELETE = XK_Delete; + const unsigned int keyEND = XK_End; + const unsigned int keyPAGEDOWN = XK_Page_Down; + const unsigned int keyCAPSLOCK = XK_Caps_Lock; + const unsigned int keyA = XK_a; + const unsigned int keyS = XK_s; + const unsigned int keyD = XK_d; + const unsigned int keyF = XK_f; + const unsigned int keyG = XK_g; + const unsigned int keyH = XK_h; + const unsigned int keyJ = XK_j; + const unsigned int keyK = XK_k; + const unsigned int keyL = XK_l; + const unsigned int keyENTER = XK_Return; + const unsigned int keySHIFTLEFT = XK_Shift_L; + const unsigned int keyZ = XK_z; + const unsigned int keyX = XK_x; + const unsigned int keyC = XK_c; + const unsigned int keyV = XK_v; + const unsigned int keyB = XK_b; + const unsigned int keyN = XK_n; + const unsigned int keyM = XK_m; + const unsigned int keySHIFTRIGHT = XK_Shift_R; + const unsigned int keyARROWUP = XK_Up; + const unsigned int keyCTRLLEFT = XK_Control_L; + const unsigned int keyAPPLEFT = XK_Super_L; + const unsigned int keyALT = XK_Alt_L; + const unsigned int keySPACE = XK_space; + const unsigned int keyALTGR = XK_Alt_R; + const unsigned int keyAPPRIGHT = XK_Super_R; + const unsigned int keyMENU = XK_Menu; + const unsigned int keyCTRLRIGHT = XK_Control_R; + const unsigned int keyARROWLEFT = XK_Left; + const unsigned int keyARROWDOWN = XK_Down; + const unsigned int keyARROWRIGHT = XK_Right; + const unsigned int keyPAD0 = XK_KP_0; + const unsigned int keyPAD1 = XK_KP_1; + const unsigned int keyPAD2 = XK_KP_2; + const unsigned int keyPAD3 = XK_KP_3; + const unsigned int keyPAD4 = XK_KP_4; + const unsigned int keyPAD5 = XK_KP_5; + const unsigned int keyPAD6 = XK_KP_6; + const unsigned int keyPAD7 = XK_KP_7; + const unsigned int keyPAD8 = XK_KP_8; + const unsigned int keyPAD9 = XK_KP_9; + const unsigned int keyPADADD = XK_KP_Add; + const unsigned int keyPADSUB = XK_KP_Subtract; + const unsigned int keyPADMUL = XK_KP_Multiply; + const unsigned int keyPADDIV = XK_KP_Divide; + +#elif cimg_display==2 + // Define keycodes for Windows. + const unsigned int keyESC = VK_ESCAPE; + const unsigned int keyF1 = VK_F1; + const unsigned int keyF2 = VK_F2; + const unsigned int keyF3 = VK_F3; + const unsigned int keyF4 = VK_F4; + const unsigned int keyF5 = VK_F5; + const unsigned int keyF6 = VK_F6; + const unsigned int keyF7 = VK_F7; + const unsigned int keyF8 = VK_F8; + const unsigned int keyF9 = VK_F9; + const unsigned int keyF10 = VK_F10; + const unsigned int keyF11 = VK_F11; + const unsigned int keyF12 = VK_F12; + const unsigned int keyPAUSE = VK_PAUSE; + const unsigned int key1 = '1'; + const unsigned int key2 = '2'; + const unsigned int key3 = '3'; + const unsigned int key4 = '4'; + const unsigned int key5 = '5'; + const unsigned int key6 = '6'; + const unsigned int key7 = '7'; + const unsigned int key8 = '8'; + const unsigned int key9 = '9'; + const unsigned int key0 = '0'; + const unsigned int keyBACKSPACE = VK_BACK; + const unsigned int keyINSERT = VK_INSERT; + const unsigned int keyHOME = VK_HOME; + const unsigned int keyPAGEUP = VK_PRIOR; + const unsigned int keyTAB = VK_TAB; + const unsigned int keyQ = 'Q'; + const unsigned int keyW = 'W'; + const unsigned int keyE = 'E'; + const unsigned int keyR = 'R'; + const unsigned int keyT = 'T'; + const unsigned int keyY = 'Y'; + const unsigned int keyU = 'U'; + const unsigned int keyI = 'I'; + const unsigned int keyO = 'O'; + const unsigned int keyP = 'P'; + const unsigned int keyDELETE = VK_DELETE; + const unsigned int keyEND = VK_END; + const unsigned int keyPAGEDOWN = VK_NEXT; + const unsigned int keyCAPSLOCK = VK_CAPITAL; + const unsigned int keyA = 'A'; + const unsigned int keyS = 'S'; + const unsigned int keyD = 'D'; + const unsigned int keyF = 'F'; + const unsigned int keyG = 'G'; + const unsigned int keyH = 'H'; + const unsigned int keyJ = 'J'; + const unsigned int keyK = 'K'; + const unsigned int keyL = 'L'; + const unsigned int keyENTER = VK_RETURN; + const unsigned int keySHIFTLEFT = VK_SHIFT; + const unsigned int keyZ = 'Z'; + const unsigned int keyX = 'X'; + const unsigned int keyC = 'C'; + const unsigned int keyV = 'V'; + const unsigned int keyB = 'B'; + const unsigned int keyN = 'N'; + const unsigned int keyM = 'M'; + const unsigned int keySHIFTRIGHT = VK_SHIFT; + const unsigned int keyARROWUP = VK_UP; + const unsigned int keyCTRLLEFT = VK_CONTROL; + const unsigned int keyAPPLEFT = VK_LWIN; + const unsigned int keyALT = VK_LMENU; + const unsigned int keySPACE = VK_SPACE; + const unsigned int keyALTGR = VK_CONTROL; + const unsigned int keyAPPRIGHT = VK_RWIN; + const unsigned int keyMENU = VK_APPS; + const unsigned int keyCTRLRIGHT = VK_CONTROL; + const unsigned int keyARROWLEFT = VK_LEFT; + const unsigned int keyARROWDOWN = VK_DOWN; + const unsigned int keyARROWRIGHT = VK_RIGHT; + const unsigned int keyPAD0 = 0x60; + const unsigned int keyPAD1 = 0x61; + const unsigned int keyPAD2 = 0x62; + const unsigned int keyPAD3 = 0x63; + const unsigned int keyPAD4 = 0x64; + const unsigned int keyPAD5 = 0x65; + const unsigned int keyPAD6 = 0x66; + const unsigned int keyPAD7 = 0x67; + const unsigned int keyPAD8 = 0x68; + const unsigned int keyPAD9 = 0x69; + const unsigned int keyPADADD = VK_ADD; + const unsigned int keyPADSUB = VK_SUBTRACT; + const unsigned int keyPADMUL = VK_MULTIPLY; + const unsigned int keyPADDIV = VK_DIVIDE; + +#else + // Define random keycodes when no display is available. + // (should rarely be used then!). + const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent) + const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent) + const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent) + const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent) + const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent) + const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent) + const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent) + const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent) + const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent) + const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent) + const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent) + const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent) + const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent) + const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent) + const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent) + const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent) + const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent) + const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent) + const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent) + const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent) + const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent) + const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent) + const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent) + const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent) + const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent) + const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent) + const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent) + const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent) + const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent) + const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent) + const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent) + const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent) + const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent) + const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent) + const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent) + const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent) + const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent) + const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent) + const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent) + const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent) + const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent) + const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent) + const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent) + const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent) + const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent) + const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent) + const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent) + const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent) + const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent) + const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent) + const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent) + const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent) + const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent) + const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent) + const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent) + const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent) + const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent) + const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent) + const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent) + const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent) + const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent) + const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent) + const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent) + const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent) + const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent) + const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent) + const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent) + const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent) + const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent) + const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent) + const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent) + const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent) + const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent) + const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent) + const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent) + const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent) + const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent) + const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent) + const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent) + const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent) + const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent) + const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent) + const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent) + const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent) + const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent) + const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent) + const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent) + const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent) +#endif + + const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI + + // Define a 10x13 binary font (small sans). + static const char *const data_font_small[] = { + " UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw Mw (wnwnwuwpwuypwuwoy" + "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y Hw cwnw >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w " + "ry qw %wuw !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow" + "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq" + "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd" + "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp" + "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw " + "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwswy~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~" + "}y|xy|}w~| s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x" + "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w" + "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f" + "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|" + "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{" + "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~} Pw~}y{|x~}cY~ i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~| ;{}`~} -" + "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~ +{|w~Xs~y}s~|\\m~}X{}" + "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~ hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{" + "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|" + "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~ " + " Ov~ !{}w~}Mw~|N{|v~ :{}v~|s{|v~V{|t}|V{|t~s}w~| p{|v~ {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~" + "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~} t{}" + "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{" + "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_" + "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{" + "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}" + "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{" + "|t}|Lw~|xw~c{|[~} iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~ ={|^~} .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x" + "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{} 4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{" + "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~} iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y" + "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}" + "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc" + "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~ Ov~ !{}w~|Mw~|M{}w~ :v~|q{}w~|Xp~}X{}v~|p{|" + "}| o{}w~| v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p" + "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~| w{|w~}|o{|}w~|(x~}tw~ rw~K{}x" + "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|" + "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~" + "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`" + "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|" + "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~} j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~} b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|" + "P{|w~|xx|av~|fv~| j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~} ?{}t~}y| u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{" + "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}| `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~" + "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~| jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n" + "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|" + "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a" + "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{|w~" + "}p{|w~}Xn~|Zv~ _{|v~ !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|" + "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~| x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}" + "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w" + "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|" + "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v" + "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v" + "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy| g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~} d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}" + "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~| A{}u~} q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^" + "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~ a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o" + "{}v~|a{|v~}p{}v~ j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|" + "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|" + "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~| vw~} `{|w~|$" + "w~} w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{}w~|ow~}Xm~|[v~ ^v~| \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}" + "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~} y{|" + "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~" + "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|" + "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" + "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|" + "x}rx}| 6w|Nw|Ox|rw| Nw~} e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|" + "Oy|Uw|jw|Vu|Wv|kw|b{}v~} p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}" + "w~}xx~x{}w~}|U{}w~ a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~| \\{}w~}" + "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}" + "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}" + "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~| ww~} `{|w~|$w~} w~} >w~}Dv~ " + "Ov~ !v~Lw~|M{|w~| <{}w~|ow~}Xy~}w|}t~[v~| _{}w~} #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~" + "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\" + "{|}p~} {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}" + "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~" + "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}" + "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~ Py~}|u{|v~} 2w~} f{" + "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}" + "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~| ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{" + "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~ aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}" + "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}" + "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v" + "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~| xw~} `{|w~|$w" + "~} w~} >w~}Dv~ Ov~ !w~}Lw~|M{|w~| {|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{" + "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v" + "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|" + "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|" + "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|" + "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~| n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{" + "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|" + "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|" + "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~" + "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv" + "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z" + "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{" + "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|" + "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|" + "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}" + "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}" + "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~ lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~" + "}H{}w~|Q{|t~|N{}w~ c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v" + "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w" + "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~" + "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m" + "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}" + "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w" + "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c" + "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}" + "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|" + "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]" + "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}" + "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}" + "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}[" + "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~" + "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a" + "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}" + "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~} pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v" + "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~ c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v" + "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m" + "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~" + "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~" + "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}" + "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}" + "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u" + "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t" + "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r" + "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w" + "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~" + "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv" + "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d" + "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v" + "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}" + "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~" + "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~" + "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o" + "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~ pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v" + "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~ c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|" + "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{" + "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n" + "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{" + "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|" + "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|" + "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w" + "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w" + "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v" + "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~" + "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]" + "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}" + "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{" + "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r" + "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|" + "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v" + "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{" + "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}" + "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~} q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw" + "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~ cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|" + "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{" + "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{" + "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w" + "~|H{|w~| s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}" + "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|" + "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s" + "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{" + "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}" + "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}" + "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t" + "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w" + "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|" + "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m" + "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~" + "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~" + "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~" + "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~" + "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~| q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x" + "x~x{|}w~}U{}w~ d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{" + "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{" + "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~" + "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~| bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U" + "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w" + "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_" + "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}" + "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|" + "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v" + "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} " + "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~" + "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v" + "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~" + "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}" + "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|" + "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}" + "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~" + "r{}w~}dw~| lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~ d{}x~}Y{|v~k{}w~}W{}w" + "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}" + "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|" + "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}" + "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~| b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T" + "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~" + "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o" + "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a" + "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{" + "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~" + "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v" + "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}" + "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|" + "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|" + "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}" + "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`" + "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u" + "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~" + "|yv~|X{}w~|sv~|dV~} 2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ " + " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}" + "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}" + "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u" + "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~| aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~" + "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{" + "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`" + "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~" + "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|" + "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~" + "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}" + "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w" + "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v" + "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}[" + "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V" + "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{" + "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}" + "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~} 2v~| k{}w~| {{|x~" + "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~ e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~" + "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w" + "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b" + "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~| a{}w~^v~|m{|" + "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c" + "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}" + "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m" + "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}" + "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|" + "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|" + "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\" + "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w" + "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}" + "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~" + "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}" + "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}" + "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~} 2v~| k{}w~| {{}" + "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~} t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{" + "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}" + "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv" + "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I" + "{}w~I{|w~| a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{" + "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K" + "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~" + "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|" + "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X" + "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}" + "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o", + "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}" + "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|" + "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~" + "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}" + "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|" + "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{" + "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~} 2v~| k{}w~| {{w~|tw~|W{|}m~}T{" + "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{" + "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|" + "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~" + "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~| " + "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v" + "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|" + "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~" + "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~" + "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~" + "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~ p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~| jv~}" + "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|" + "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}" + "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w" + "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}" + "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}" + "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd" + "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw" + "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z| 5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox" + "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}" + "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{" + "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w" + "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~| p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~" + "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~" + "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~" + "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{" + "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c" + "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~" + "}8{|y~|sw~vw~}q{|y~| Q{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{" + "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}" + "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}" + "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~" + "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v" + "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j" + "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{" + "|w~}b{}x~} pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k" + "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}" + "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u" + "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~" + "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~| r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~" + "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|" + "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}" + "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv" + "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]" + "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{" + "|w~|r{}y~ P{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|" + "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}" + "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_" + "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|" + "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}" + "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~" + "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{" + "}w~|b{}x~} q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w" + "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv" + "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|" + "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|" + "v~|Ru~|M{|w~}H{|w~J{|w~| s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}" + "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|" + "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{" + "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v" + "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}" + "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~ p{|" + "w~|m{}w~|Ux~}w{|x~} w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|" + "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{" + "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{" + "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|" + "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m" + "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|" + "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~| " + " r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}" + "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k" + "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~" + "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|" + "v~|S{}v~|L{|w~}Gw~|K{|w~| t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~" + "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~" + "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~" + "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y" + "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\" + "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~" + "} P{}w~ p{|w~|m{}w~|Ux~}w{|x~} w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b" + "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v" + "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" + "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u" + "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{" + "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}" + "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x" + "{|v~`w~} m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~ g{}w~Uv~|lv~|W{}w~}P{}v~" + "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{" + "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv" + "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{" + "|w~}G{}x~}K{|w~| tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~" + "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L" + "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~" + "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v" + "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|" + "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~ " + "p{|w~|m{}w~|Ux~}w{|x~} w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{" + "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k" + "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V" + "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~" + "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}" + "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~" + "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|" + " mv~| o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~ gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~" + "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}" + "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}" + "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w" + "~| u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~" + "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|" + "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{" + "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}" + "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W" + "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~ p{|w~|m{}w" + "~|Ux~}w{|x~} B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~" + "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}" + "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v" + "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{" + "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v" + "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|" + "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~} mv~} g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw" + "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~ h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|" + " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}" + "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v" + "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~| u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv" + "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~" + "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{" + "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~" + "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}" + "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|" + "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~ p{|w~|m{}w~|Ux~}w{|x~} C{}w" + "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~" + "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~" + "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv" + "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m" + "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w" + "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}" + "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X" + "{|v~{|v~^{}w~} n{}v~ gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~ h{|w~T{|v~m{}w~}V{}w~}" + "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}" + "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g" + "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w" + "~}F{}x~}L{|w~| u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv" + "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w" + "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~" + "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~" + "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{" + "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|" + "w~|y{|y~} N{}w~ p{|w~}m{}w~|Ux~}w{|x~} Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|" + "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{" + "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v" + "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{" + "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w" + "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y" + "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w" + "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~| o{|v~| hw" + "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~ h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~" + "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~" + "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y" + "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~| u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}" + "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv" + "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a" + "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u" + "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}" + "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x" + "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~| >{|w~}mv~|Ux~}w{|x~} Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w" + "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w" + "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}" + "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m" + "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv" + "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}" + "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y" + "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~| o{}v~j{} {|x~}t{|" + "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~ hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|" + "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~" + "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}" + "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~| u{}w~|nu~|_" + "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv" + "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~" + "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|" + "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|" + "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x" + "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~| ={|v~n{|v~|Ux~}w{|x~} Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}" + "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w" + "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{" + "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n" + "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v" + "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}" + "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|" + "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~} p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~" + "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{" + "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a" + "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}" + "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~| u{}w~|o{}u~|_t~|q{|v~}_" + "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u" + "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~" + "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_" + "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y" + "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x" + "~|g{|x~} <{|v~|o{}v~|Ux~}w{|x~} Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f" + "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~" + "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~" + "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{" + "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{" + "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~" + "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p" + "{|v~X{|r~}Z{}u~} q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{" + "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|" + "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~" + "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u" + "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~| u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{" + "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~" + "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~| r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w" + "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}" + "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U" + "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~} ;{|u~p{|u~|Ux~}w{|x~} Ey~|s{|~}T" + "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u" + "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u" + "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v" + "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{" + "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{" + "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} " + "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y| tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|" + "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}" + "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}" + "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~" + "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|" + "v~|[{|u~}b|^{|w~}E{|w~|N{|w~| tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}" + "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|" + "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~| qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~" + "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}" + "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#" + "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{" + "|t~}Ux~}w{|x~} E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f" + "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}" + "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~" + "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}" + "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}" + "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v" + "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M" + "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~} -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o" + "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z" + "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y" + "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~" + "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~| t{}u~y" + "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv" + "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{" + "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~| q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}" + "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{" + "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~" + "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~| E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~" + "} l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|" + "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}" + "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}" + "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}" + "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{" + "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{" + "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~" + "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~} -{|h~|$v~}#{}x~}t{}x" + "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~" + "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv" + "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}" + "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~| sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw" + "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d" + "~Uw~}Lw~|M{|w~| p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~" + "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~" + "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~} A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~} m{|x~}b{}x~hw~lk~k{|x~}b{}x~" + "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}" + "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}" + "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~" + "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|" + "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~} +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~" + "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~| " + "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{" + "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~" + "| s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{" + "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~| o{|n~|w{}u~b" + "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|" + "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${" + "}m~} ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~} mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d" + "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}[" + "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~" + "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~" + "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V" + "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~| i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~} *{|}p~" + "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}" + "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~ h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w" + "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}" + "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~| q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}" + "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|" + "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~| n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~" + "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V" + "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y| 9y|}u~}y| " + "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}| q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|" + "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{" + "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}" + "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U" + "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\" + "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~} 3{|~} ;f| '{|y}w~}y| 8{|y~|X{|x~}" + "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|" + "w~| B{|v~| 1{|y}u~y}| o{|x}u~y}y| Fv~| 7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y| a{|w~}C{}x~}O{|w~| oy}" + "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}" + "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~| ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~" + "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}| Ry|y}v~y}|" + " Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~ 5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~} r{|y}|Kw~|L{|y}|Hv~| E" + "{|y}u~y}| qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y" + "}y|S{|y}v~y}y| oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}" + "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~" + "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~| Mw~| K{|y~| e{|w~Nw~" + "| ?{}w~ Cw~} .{}w~ @{|v~|d{}| Kv~| !u~| J{|w~}C{|w~O{|w~| 9w~} Iv~ bw~}9{|w~| X{|v~ rv" + "~Lw~|M{}w~| w~| D{|w~| .w~| ?{|v~}g{|x~| M{|v~ {|u~| K{|w~}Bw~|P{|w~| :{}w~} Iw~} bw~}9{" + "|w~| X{}w~| r{}w~|Mw~|Mv~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|l~| 4{|w~" + "|Ax~}w{|x~} {{}y~} /v~| ?x~| f{|x~ M{} %{}w~|Uw~}D{}w~| Lw~| K" + "{|y~| d{|w~Pw~| ?{|w~ C{}w~ .{|w~ ={|u~}|l{|u~| N{}v~ {{|u~| L{|q~}H{}x~}V{}q~| :v~| Iw~}" + " bw~}9{|w~| Xv~ q{}w~}Mw~|N{|v~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|}o~}| " + " 3{|w~|Ax~}w{|x~} {{|x~| 0v~}m{} N{|x~ e{}y~} Rv~Tw~}Dv~ S{}x~x{|w~| " + " K{|y~| c{}x~}R{}x~} >{|x~| Cw~} .{|x~| ;{}t~}|sy|}t~| N{|v~} y{|u~| M{|q~}H{|w~V" + "{}q~| ;{}v~ I{|w~} bw~}9{|w~| Y{}w~} q{|v~}|Ow~|P{|}v~} ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} " + " )v~}Iy~} gw~|Q{|y}v~y}| 1{|w~|Ax~}w{|x~} yx~| 0{}v~|p{|~} N{|x~| f{|x~ " + " S{}w~}Tw~}E{}w~} S{}x~|y{|w~ J{|y~| bw~|Sw~| >{}y~} K{}y~} 9{|p~x}q~}| N{|u~" + "| x{|u~ M{|q~} y{}q~| K{|}|p{|u~| I{}w~| bw~}9{|w~| Z{|v~ o{}q~}Tw~|U{|p~ :v~ S{|w~}W{|w~|#{|" + "w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~|Aw|vx| y{|x~} 0{|u~|s{}x~} N{|x~| " + " f{|x~| U{|v~Sw~}F{|v~ R{|x~}y{}w~ J{|y~| b{|x}|T{|x}| w{}g~}| Q" + "x|y}u~} v{|u~ N{|p} yp}| K{|x~}y|wy|}u~} J{|}v~ aw~}9{|w~| \\{|}v~} nq~}Tw~|U{|q~| :v~ S{|w~}" + "W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~| :{|}w|}w~| /t~y}x|y}v~} U{|}|x{|w~| " + " f{}x~| W{|}v~}Sw~}H{|}v~} Qq~| J{|y} *{|}l~}| O{}q" + "~ tt| `{|i~} Lr~| aw~}9{|w~| `{}q~ l{}s~}Tw~|U{|s~}| 9v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~" + "} )v~}Iy~} gw~| W{|w~| :{|q~ .{|i~} U{|q~ ly}w|}w~| [{}q~Rw~}" + "L{}q~ P{}r~ M{|y}u~y}y| L{}r~| R{|j~} Ks~} `w~}9{|w~| " + " `{}r~| jy|v}|Tw~|U{|u}| 6v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy}| gw~| W{|w~| :{|r~| " + " -{|k~}| U{|r~} l{}r~} Z{}r~|Rw~}L{}r~| O{}t~ " + " k{}t~} -{|`}| `{|}m~}| Jt~} _w~}9{|w~| `{}s~| :w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}" + "w~Uw~} )v~} d{|w~| 9y}w~y} ){}o~}| S{|}u~}| k{}r~ Y{}s~|Qw~" + "}L{}s~| M{}w~} j{}w~}| +{}`~} ]{|x}v~y}| Gw~y} ]w~}9{|w~" + "| `{}v~}| 8w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} g{|w~| 8{|}v~y}| Ly| " + " g{|y}w~}| X{}v~}|Ow~}L{}v~}| Iy| " + "l{}`~} Ww~| " + " L{}`~} Ww}| " + " r{" }; + + // Define a 104x128 binary font (huge sans). + static const char *const data_font_huge[] = { + " " + " " + " " + " " + " " + " " + " " + " " + " FY AY " + "'Z ;W @Y @Y 'Z Y @Y (Z :Y ?Y (Z 0Y ?Y (Z >X " + " " + " " + " " + " " + " )X AX '\\ )XAV 7YDY -] BY BY '[ +YEY 2X AY (\\ -YDY 'XAU 3Y AY (\\ )XAV 8YD" + "Y LY AY (\\ ,YEY #Y " + " " + " " + " " + " (X CX '^ +[CU 6ZEY .` C" + "X CY '] -ZEZ 2X CY (^ .ZEZ )[CU 2Y CY (] *[CU 7ZEZ LY CY (] -ZEZ %Y " + " " + " " + " " + " " + " 'Y EY '^ ,^FV 6ZEY /b CX DX '_ .ZEZ 2Y DX '_ /ZEZ +_FV 1X CX (_ ,^FV 7ZEZ " + " KX CX (_ .ZEZ &Y " + " " + " " + " " + " %Y GY '` .aHV 6ZEY 1e DY FX" + " 'a /ZEZ 1Y FX '` /ZEZ +aHV 0X EX '` .aHV 7ZEZ JX EX (a /ZEZ &X " + " " + " " + " " + " " + " #X GX 'XNX 0dKW 6ZEY 1f DY HX &WMX 0ZEZ 0X GX 'XMW 0ZEZ ,dLX /X GX 'WMX 0dLX 7ZEZ" + " IX GX 'WMX 0ZEZ 'X :T " + " " + " " + " " + " ;X IX 'XLX 1o 5ZEY 2ZLY " + " CX IX &WKW 0ZEZ /X HX (XLX 1ZEZ ,o .Y HX (WKX 1o 6ZEZ IY IY (WKW 0ZEZ (X X MX &WH" + "W 3VHa 4ZEY 3WDW CX LX 'WGW 2ZEZ -X LX 'WHW 2ZEZ -VHa +X KX (XHW 3VHa 5ZEZ GX KX (WGW 2ZEZ )X " + " ?b " + " " + " " + " " + " ?W MW &WFW 4VF^ 3ZEY 4WBV BW MX 'WEW 3ZEZ ,W M" + "X 'WFW 3ZEZ -VF^ )X MX 'WFW 4VF^ 4ZEZ FX MX 'WFW 3ZEZ *X ?d " + " " + " " + " " + " " + " ?W X 'WDW 5UC[ 2ZEY 4VAV AW X &WDW 4ZEZ +W NW 'WDW 4ZEZ -UC[ 'W MW 'WDW 5UC[ 3ZEZ " + "EW MW 'WDW 4ZEZ +X ?f " + " " + " " + " " + " @X \"X 'WBW 6UAW 0ZEY 4V@V B" + "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ .VAW $W W 'WBW 6UAW 1ZEZ DW W 'WBV 4ZEZ +W >f " + " " + " " + " " + " " + " ?X #W 'W@W U?V AX #W &W@V NX #W &V@W 9W \"W 'W@V .W " + "\"W 'W@V !W >XHX " + " 3Y " + " " + " " + " 6W $W &V>V U?V @W $W &W>V " + " NW $X 'V>V 8W $X (W>V /X $W 'W>V #W >XFX " + " 5Z " + " " + " ,Z " + " GZ " + " #U?V NY 7Z ,X CVCW MY " + " 7Z ,X $Z 7Z ,X >Z 6Y ,X 4Z 7Y +W 7Y @Z " + " " + " +Z " + " " + " HY \"U?V " + " MY 8Y ,Y CVBV LY 9Z ,Y #Z 9Z ,Z >Z 8Y ,Y 3Y 8Z ,Y 9Y " + " ?Z " + " *Y " + " " + " IY !U?V " + " LY :Y ,[ $R>U ,V@V MZ :Y +Z #Y 9Y +Z ?R" + ">U 8Y 9Y +Z %S?U HY :Z ,[ ;Y ?[ " + " " + " )Y " + " 8U " + " 9Y V@U JY Y @Y /X 0Y K` .X " + " ^ =ZEY @Y " + " NVAV

Y E^ /X 0_ %f 1] 'c " + " @ZEZ AY MV" + "CW X *^ +]DU 7ZEZ 5U>U JY ?Y *^ -YEZ 4Y " + " ?Y *^ .ZEZ 5[ ]DU 5Y >Y +^ ,]DU 6ZEZ Y ?Y +_ .ZEZ \"Y Z G[ G\\ @e !f JX !Y " + "LY %d :Y Y Ha /X 0b *j L] D_ " + " +g A[ LY 8Z -ZEZ \"Y 1o )V FX NZ FY " + "%Y ,X NX*Z NW 3WEW H\\ #[ !Z \"[ \"[ \"[ G[7T 8g 0Y " + "@Y +_ ,_FV 7ZEZ 5U>U IY @Y +` .YEZ 3X ?X *` /ZEZ 4[:P 8_FV 4X ?Y +` ._EU 6ZEZ NX @Y *_ .ZEZ #Y ;Y" + " FYEZ ;] GU W ,X " + " FV a \"d -g >d (d +b %b 4f Bg Ie \"e \"h " + " Ge !f IX \"Y LY &e :Y Y Jc /X 0c " + " -n $g I` .j >a ;e HU .U +b Ac 2ZEZ 'b " + " 5o -] Na (c KY .Y #_ 8Y!W'Y\"X.c$X 3XGX Mf -e +d " + ",e ,e ,e \"e=V ;k 1Y BY +XNW .aGV 7ZEZ 5V@V HX AY +XNW .YEZ 3Y AY *WNW /ZEZ 4\\>T 9`GV 3" + "X AY +XNW .`GV 6ZEZ NY AX *XNW /ZEZ $Y :Y FYEZ <_ IU (Q LZ 4Z2Z 1Q " + " &g %Z +XCX MT Y Kd /X 0e 0p " + " (m Lb 1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik LW DX DW /i ?Y(Y 4h 5ZEZ" + " ,\\ ,h 7\\ -o .` $f -h NY No %_ %c @_\"X-_\"W0h&W .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S " + "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ Im 1X CY *WMX /bHV 7ZEZ 5V@V G" + "X CY *WLW /YEZ 2Y CY *WLW 0ZEZ 3[AW :bHV 3Y BX *WLW 0bHV 6ZEZ MY CX *XMX 0ZEZ $X 9Y FYEZ " + " =a M~i 7U (Q N_ 9_8_ 3R )k 'Z +XCX +X@X 4T >e,X Cl &X IX *X GV " + " GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j /l N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z" + " Ep =t 5o Au 1u N~d'Z(Z)Z MZY " + " Le /X 0e 1r +r c 3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko " + " Y EX EY 2m @Y)Y 6l 7ZEZ 0e 2k >e 1o 0c 'j /i X !r (b 'g Eb\"W0c#X0i(W -" + "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ Kp" + " 1X DX *WKW /WMYJV 6ZEZ 5V@V GY EY *WKX 0YEZ 1Y EY *XKW 1ZEZ 2[EZ :WMZKV 1Y DX *WKX 1WLYKW 6ZEZ L" + "Y EY *WKW 0ZEZ %X 8Y FYEZ >c M~h 7T (S !a Y >X 8f /X 0f 3t -s c " + " 4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms #[ FX F[ 4n @Y*Y 6m 7ZEZ 3k 5l Bk 4o 1f )k 0k #" + "X #u (b (i Fb#X0c#W/k+X .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t " + " 0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ Ls 2X FX *WIW 1WJc 6ZEZ 4VBV EY FX *XJW 0YEZ 0X EX )WJW 1ZEZ 1[I^ x %_ ?y 5r F~S Ct :p" + " 6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ " + "@e 2X Gf +a MX %Y LY *i :Y Y >Y 9f /X 0g 5v " + " 0u d 6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w &] GX G] 6U &o ?Y+Y 7X )n 7ZEZ " + "6p 7m Eo 6o 2h *l 1l %X #v (b )k Gb$X/c$X/l,W -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z " + "!Z \"Z :~ D_-Y Hw =v >w >w >w 4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ Mt 1X HX )WHW 2VHb 6ZEZ 4WDW DX GX )WHW 1YE" + "Z /X GX )WHW 2ZEZ 0[M` ;VHb /X GY *WHW 3VHb 5ZEZ JX GX )WHW 2ZEZ 'Y 7Y FYEZ ?e M~f " + " 7U )U %g Bh@g :W .~T 't +Z +XCX ,X@X 3T Ak1X Er (X JX 'X IV HX 8q" + " =m 7y ?y '` ?y 6s F~S Dv Y >Y " + " :] %X &] 5]C\\ 1v Nc 7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y (_ H" + "X H_ 7U 'p ?Y,Y 6X *o 7ZEZ 8t 9YH] Ht 9o 3i *XG[ 1VE[ &Y %x (b *[I[ Hb$W.c%X.VE[-X " + " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az 7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ Na" + "J_ 2X IX )WGW 2VG` 5ZEZ 4XFX CX IX )WFW 2YEZ .X IX )WFW 3ZEZ /j 8VG` -X HX *WFW 4VG` 4ZEZ IX IX " + ")WGW 2ZEZ 'X 6Y FYEZ ?XKX M~f 7T )W 'i DiAi ;X 1~V (w -Z " + "+XCX ,X@X 3T AZI[2W Es (X KX &X IV HX 9s >m 7z @z )a ?y 7t F~R Dx >t 9v 8s 2` :~P <~Q&~S" + " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X ;Y -e MX 'Y " + "LY +[ +Y Y >Y :[ #X #Z 6\\?[ 2v F\\ " + " 8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#} +` HX Ia 8U (q >Y-Y 6X +p 7ZEZ 9bMb ;U@Y JbMb :" + "n 3ZIZ +T@Y 2R>Y 'X %y (XLV +ZEZ IXMW%X.YMW%W-R>Y.W -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z " + "!Z \"Z :~S Ha/Y K| B| C| D} D| 9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ N]B\\ 2X JX *WEW 3UE_ 5ZEZ 3YJY AX JW )WE" + "W 2YEZ -X KX (WFW 3ZEZ .f 5UE_ ,X JX )WFW 4VF_ 4ZEZ HX KX )WEW 3ZEZ (X 5Y FYEZ @YJW M~" + "e 7U *X (j EkCk =Y 3~X )x -Z +XCX ,W?X 3T BYEY3X Ft (X KX %X JV " + " IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D" + "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X ;Y .g MW 'Y LY +Y )Y Y " + " >Y :Z \"X \"Z 7[=Z 3aE[ E[ 9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$" + "~P -b IX Jc 9U )r >Y.Y 5X ,]DX 7ZEZ ;\\>\\ \\ 0XDX ,R=Y MX (X %hEW (SG" + "V ,YAY JSHW%W-SGW&X GX/W ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P" + " <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ \\>Z 1X LX )VCW 4UD] 4ZEZ 2f ?X LX )WDW 3YEZ ,W KX )WDW 4ZEZ -b 2UD] *W" + " KX )WDW 5UD] 3ZEZ GW LX (VCW 4ZEZ )X 4Y FYEZ @XIX M~d 7U *Y *l GmDl ?[ " + " 6~Z *`C\\ -Z +XCX ,W?W 2T CYCY5X E]CZ (X LX $X JV IX 9]E^ @m 7aGb B^Ec ,b ?y " + "9aF[ F~R E_C_ B_E^ ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa" + "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X ;Y /i MW (Y LY ,Y (Y Y >Y " + " :Y !X !Y 8[;Z 1\\ 0\\:U D[ ;ZbCh%Z(Z" + "#Z(Z$Z(Z$Y'Y![.Z HZ Z;Z 1X NX )WBV 5VBZ $e >W MX )WBW !X MX )WBW #` /UBZ (W MX )WBW 6UBZ " + " 9X MW (WCW MX 3Y GXHW M~d 8U *[ +m HnFn A] 9~\\ +^=Y" + " -Z +XCX -X@X 2U DXAX5W E\\=V (X LX #X .R@V?Q ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@] " + " j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`" + "2Z0[4[ LZ/[\"~^ @X #X Y >Y ;Z " + "!X !Y 8Z9Y 6d 4[5R CZ ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=` 1f KX Lg " + " ;U *\\=T =Y0Y 4X ,Z;R 5Z3Y &W !Y3Y 3W@W EW LX *W %jEW KV -X=X @W'X W'X EX1W ,b " + "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=` @`=e%Z(Z#Z(Z$Z(Z$Y'Y" + " Z/[ HZ !Z9Y 0W X )WAW 6VAW \"d Y >Y ;Y X !Y " + " 8Y8Y 6f 6Z2P BY j BZ(Z+[;Z;Z'_9_ 3h LX Mi <" + "U *[:R V EW KW +W %kEW KV .X;W @W'W NW(X CW2X -c *c )b " + "*c )c +c 7ZHZ 2_5[ NZ !Z Z !Z >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_ B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z" + "8Y 0W !W (V?W I` :X !W (V?W X \"X (W@W *d EX !W (W@W 0X \"X (V?W !W 1Y #d ," + "e +d +d ,e #XHW LZ#Z 7U +] -o KqHp C_ X #X " + " Y >Y ;Y X X 9Z7X 6g 7Y" + " #Z =Y8Z 7d 7[ Z )_7_ Bp EZ(Z+[;Z;Z(^5^ 5j MX Nk =U +[7P Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5] C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V H^" + " 8X #W (W>V NW \"W (W>W .h EW \"X )W>W 0W #X (V=V \"W 0Y &j 1i 0j 1j 1i &X ` .\\5U -Z +XCX -W?W =r'X>W8X EZ ;X NY !X 1XDVDX 2X " + " &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y L\\ 2Z E[7[ G\\9[ >S5[ F`7` ?YNY Y >Y ;Y X Y :Y6Y 7i 9Y \"Y " + " >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2] 6l NX m >U +Z !Y4Z 3X -Y NW(W (W " + " &X)X 8VZ !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2] E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'VR4[ G^1^ AZNY Y >Y ;Y X Y :Y6Y 7j :Y \"Y " + " >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\ 8n X !o ?U ,[ Y5Y 2X -Y W&W )W 'W%W 9V" + "Z " + "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0] F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X +P " + " %_K[ CY *r 9q 8r 9r 9q *X ;Z%Z >Q JT ,b 0q MsKs Ge " + "C^ *[0R -Z +XCX .X@X @v)X=X:W CY :X Y NX 1[HVH[ 1X 'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y M[" + " 1Z EZ4[ I[5Z ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5" + "\\!\\-\\ Z4[ GZ ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z [ ;X \"X =Y 5\\C[ #Y LY -Y 'Y 8X >Y " + " >Y ;Y X Y :Y6Y 7k ;Y \"Z @Z5Y 9YLY 5[ #Z +\\.] J| KZ" + "(Z+[;Z;Z*\\-\\ :p !X \"q @U ,Z NY6Y 1X -X W#V *W (W#W :U;V +X DW LW )mEW KV" + " /X9X BW*X LW*X BW3W +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z Z !Z >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\" + "#\\-\\#\\-\\#\\-\\ H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y /[G[ " + " DY +u =u S LU ,c 1q MtLt Hf E] )[.Q " + " -Z +XCX .W?X Bx)X=X;X DZ :X X MY 0ZIVIZ /X 'X ;Z7[ 1Z AZ ![ 4XKZ ?Y MZ 0Z EZ3Z I[5Z " + "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[ ;Y 9Z(Z$Z" + "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X =Y 6\\A[ $Y LY -Y 'Y 8X >Y >Y " + " ;Y X Y :Y6Y 7l Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP" + " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X /XC[ EY " + " -x @x >x ?x @x -X :Z'Z ?U MU -e 2q MtLt Ig E[ 'Z,P -Z +XCX .W?W By)" + "XZ0Z" + " J\\#Q'Z*\\ KZ MZ +[ Q$Z(Z :Z \"Z 4Z<] Y 7[>[ %Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" + "5Y 7UH_ Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z" + "(Z$Z(Z$Y'Y K[9[ Ct =Y3X /U@[ \"Q EY .z B{ " + "B{ Az B{ /X :Z'Y >V U -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[ '[ 8Z +XCX /X@X C`MTL_)W;" + "WZ0Z " + "J[ 'Z)\\ LZ MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !" + "\\ 9X !X >Y 8[<[ &Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y5Y " + "7RB] =\\ $Z BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\ ?\\MXL[ $X %\\LXM\\ CU" + " ,Y *Q\"R DY9Y 0X -Y #V=_?V Cm *V LV Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z(" + "Z$Z(Z$Y'Y J[:Z Bw @Y6[ .Q<[ #S GY /`Da E`C" + "` DaD` C`Da E`C` 0X 9Y(Z ?X !U .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[ &Z 7Z +XCX /X@X C\\" + "ITFY)W;W=X BY 9X !X KY +YNVNZ *X (X ;Z4Z 2Z @Z !Z 6YJZ ?Y Z /Z DY2Z JZ1Y ,T T MZ N[ NZ HZJ" + "Y >Z0Z K[ &Z(\\ MZ MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;" + "[ ![ 8X !X >Y 9[:[ 'Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" + "5Y %\\ =] %Y BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[ @\\LXK[ %X &\\KXL\\ " + " DU -Z +S$T EY:Y /X -Z %V?fBU Eo +VEg=V =VZ !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$" + "Z(Z$Y'Y IZ;Z Ay BY9^ G[ %U HY 0]<^ G^=^ F" + "^<] E]<^ G^=^ 1X 9Z)Z @Z \"U .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[ &[ 7Z +XCX /W?X D[GTC" + "V)W;W=W AZ :X \"Y KY *j (X (X ZY .Y3Y 3Z '\\ MZ )Z ;Z 2^ +Y ;Y " + "X Y 6Y /Y5Y $[ =` G^ !Z IZ M\\ #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[ B\\KXJ[" + " &X '\\JXK\\ H\\ 1Z ,U&V EY;Y /X ,Z 'V@jDV Gp +UDj?V >VZ !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)" + "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[ \"[ &Z &[ ![" + " #\\ #[ ![ G[@W IYBZ J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z @Z \"U .k 5q N~o Mm 4l =X" + ".X 7^ 7Z3Z NZ %Z 6Z +XCX /W?W D[FT@S)W;W>X AZ :X \"Y JX (f &X )X ;Z3Z 2Z @Z !Z 7" + "XHZ ?Y !Z /Z CY1Y JZ1Z 2Y Y $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ MZ -[ Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'[" + "$[ NZ2Z%[%[#Z2[ JZ :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X X ?Y ;[6[ (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X NX " + "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y X Y 9_>W KY5Y #[ =c h >XD` " + "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[ C\\JXI[ 'X (\\IXJ\\ " + " (Y d 5Z -W(X FYV=W +X HX )^ ,Y1Y HnEW KV 0X7W BW-W HW.X M^/X )" + "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#" + "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y NX NX X E[ >XD` -c )c *b *c )c '\\ &bDX L" + "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y 8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z AZ !U /m 6q N~o No 6o ?X.X 8_ " + "6Y3Z Z $Z 6Z +XCX 0X@X DZET>Q)W;W>W ?Y :X \"X IY 'b $X )X ;Z2Y 2Z @Z !Z 8YHZ ?Y " + "!Z 0[ CY1Y JZ1Z 5\\ \\ 'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ" + "2Z&[#Z#Z2[ JZ :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z C[IXH[ (X ([HXI[ (" + "Z $k 9Z .Y*Z FY=Y .X ,\\ *UAnCU J^CW -VCmAV ?W>V *X IX (a /Y1Y HnEW KV 0X7W BW.X HW.W La3X " + "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z(" + "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y Y NX Y E[ ?XFd 1g .h /h /h /h )\\ )hHX " + "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y 9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y A[ !T /n 6q N~o q 8q @X.X 8` 7" + "Y3Y Z $Z 5Z +XCX 0X@X DYDT EW;W?X ?Y :X #Y IY %^ \"X )X k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[ CYHXGY 'X 'YGXHY 'Z &o" + " ;Z /[,[ FZ?Y -X +\\ +UBoBU LZ>W -UBnAU >W@W *X JX 'c 1Y1Y HnEW KV /W7W BW.W GW/X Lc5W 'Y ," + "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#" + "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y Y NX Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0" + "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z AZ !U /o 7p M~n s :s AX.X 8` 7Z4Y Y" + " #Z 5Z +XCX 0W?X EYCT EW;W@X >Z ;X #Y HX #Z X *X ;Z1Z 3Z @Z !Z 9XFZ ?Y \"Z /Z " + "BY2Z KZ0[ [/Z 4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y NX 0X5\\ 6Y FY" + "JiBi$YJk 8o ?YJj 9kJX ;YJc Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\" + "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t t TAU NX;W )P9P =UAWAYAU >XDX )X LX HY 3Y1Y HnEW KV /W7W " + "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-" + "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u oLX ;YLe ?u VAW?XAU ?ZHY (X MX EX 4Y1Y HnE" + "W KV /W7W AQ:Q :W0W EW1X Z !Z !Z \"Z :Z%['YHZ" + "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y Y N" + "X Y *r BXKn qMY GYMp 0Y NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u U@W?XAU >j (X " + " NX CX 5Y1Y HnEW KV /W7W AR;R ;W1X EW1W :XZ " + "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ w ?x >x ?w >w#wKv Nu ?v" + " =v =v =v 0Y Y NX Y +s BXLp >u \\ DX.X :c 7Z7Z!Y \"Z 4Z +XCX C~d&XBT DW=XB" + "X :[ >X $Y FY +f &X +X ;Z/Z 4Z AZ !Z ;YDZ ?YFP -Z?Q BZ ?Z5Z JZ/Z 5Z \"[ Gj Ii ;[\"X1Q,W\"YCZ BZ1" + "Z MZ \"Z$[!Z MZ /Z LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[ ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\" + " 1X MX AY BZ&Z 8^Ga AYN[H_ " + "YDY *X )b 6UDY%U V9W ,SU@W>W@T =h 'X X AW 5Y1Y HnEW KV /X9X ASZ !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z(" + "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y Y NX Y ,u CXM^Nb" + " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0" + "Z CZ ;P4U 1w 9l J~m#z B[;[ EX.X :d 7Y7Y X )~Q #Z +XCX C~d&XBT DW=XCX 9\\ ?X $Y FY " + "-j (X +X ;Z/Z 4Z AZ \"Z :XCZ ?YM_ 5ZE^ IZ >Y6Z IZ0[ 5Z \"[ Jj Ci ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z " + "MZ 0[ LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[ ^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\" + ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y X Y M];\\ F]E[JX IY9[ LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^" + " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[ W@U =f &X !X @W 5Y1Y HnEW KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC" + "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z" + "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y Y NX Y -w DXNY" + "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1" + "Z DZ =S4U 2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y *~R #Z +XCX C~d'YBT DX?XBW 7\\ @X $Y FY " + "/ZNVNZ *X ,X :Z/Z 4Z AZ #Z :XBZ ?o 9ZGc MZ =Z8[ HY0\\ 6Z \"[ Li >j C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!" + "Z MZ 0[ LZ(Z :Z \"Z 4ZJ] .Z 2YHXY 9Z(Z NZ2Z,Z\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6" + "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y X Y `>` I\\B[KX IY:\\ LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L" + "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\ XAU V ?W3X CW3X 8X>W #Y /Z" + "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*" + "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y Y " + "NX Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z ~d W5T 2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X )~R #Z 0~d&XBT DX?XCX 6\\ " + " =Y EY 0ZMVMZ +X ,X :Z/Z 4Z B[ %\\ :XBZ ?q ;YHg Z \\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;ZW>X@T ;a #X #X =W 6Y1Y GmEW KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :" + "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z" + "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y Y NX Y /_B] E_<" + "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y ;X~d#Z6U 3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X *~R \"Z 0~d&YCT CXAXBW 5] " + " >Y EY 2ZKVKZ -X ,X :Z/Z 4Z BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki J\\!X:hKVAW%Y@Y CZ5\\ L" + "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F` BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X" + " LX BY JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y EY .Y1Y " + "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y X Y \"eCd L[>YLX HY>^ IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G" + "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.ZUDX!T\"XW>X@U :] !X $X Z !Z !Z \"Z :Z#Z(YEZ~d&^7U 4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X )~R \"Z NW?W BYCT CYBXCX 6_ ?Y EZ 5ZI" + "VIZ /X ,X :Z.Y 4Z C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z " + " MZ 0Z KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X KX CY " + " )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y EY .Y1Y GY3Y#Y=WNX=Y M" + "ZAZ AY3Y %[ /Y X Y #gEf N[W>W?U 7W <~d BX ;W 6Y1Y GmEW KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y" + "?Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZY D~P JZ !Z#[\"~Q Dy Z K~] :Z \"Z 4ZN] *Z 2YFX?XF" + "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X KX CY (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] " + "G]7Z 4Y NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X NX Y $iGh Z:XNX" + " GYHg HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[W>W?U K~d CX ;X " + " 6Y1Y FlEW KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y Y Y ;Y?Z JZ>~Q3[ I~Q G~Q F~Q G~Q 5Z !Z !Z " + "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY @Y Y !Z Y Y Y 4_4Y)[ %Z3" + "Y GZ3Y FZ4Y FZ4Y 4Y Y NX Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[ Y D~ IZ !Z#[\"~Q Dy![ K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck Y 9Z(Z LZ6Z(" + "Z?XDX?Z G` *d #[ +X KX CY 'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I" + "Z5\\ ;] -X DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y NX Y $XIZHZIY!Z:XNX GYHf GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#" + "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[Z EiKh 6X /XC^ BTDX U\"YA\\ 4ZCZ N~d &U>W?X>T K~d EY :W 5Y1Y EkEW KV ,YAY =ZCZ DW6X @W6" + "X 5W@W 'Z>Y Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L" + "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY @Y Y Y NY Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y Y NX Y 2[6Z G" + "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z Z6Y &[ .Y NX Y %WEYJYEX#Z8a GYHe FY DX4[ DY $\\5Y CY8Z 5d Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$" + "Y'Y >c 6Z2Z KYDY ?Y X NX NY Y Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y Y NX Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ" + "4[ >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z <~d'RKY:U 5~U J" + "~T$~g'~X KY1X GX.X Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)YZ=Z =YZ=Z =YZ=Z LZ=~Q3Z H~Q G~Q F~Q G~Q" + " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY ?Y Y X MX Y Y" + " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y Y NX Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z >Z:a IY0X HY0X GX0X GX0Y FZ7Y E[" + "3Z GY6Y ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z <~d NX:U 5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W " + " NZ 3Y X@X ?]IT ?hCW 7h2X ;Y CY 7TAVAT 1X .X 8Z.Y 4Z G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5" + "^ 4i ;~d :i 1[ LWr *Y " + "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X JX DY &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y NX 0XE\\ &Y FZ4[5Y*[4Z IZ" + "2Z H[2Y KY2[ ;[ +X DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y NX NY *Q NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y" + " 5YMY [/[IuI[.\\ 4X 4\\ =X =\\$\\" + " =X MZAU -Z &X8Y G~W 6X 0W<\\ FUEX MT iNW 8[D[ K~d &T=WE\\QZZeBX] ,Z 1j <[7_ 7i 8~d 7i 5[ KW=Z=" + "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z[/[IuI[/\\ 3X 3\\ >X >\\\"\\ >X MZAU -Z 'X6X 5c " + "%X 1X;\\ GUEX MT NgMW 9[D[ J~d &T=m;T K~d In 4TA[ 4Y1Y BhEW 3Z DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z KY" + ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8" + "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[ @X NX Y NY X NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y Y NX Y 4Z2Z HZ3Y IZ1Z I" + "Z1Z JY1Z JZ1Z IZ1Z @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z I" + "[ 7YY ~m 4Z 3Y W?X >g =cAW?]'[K\\5Y ;Y CZ %V M" + "X /X 7Y-Z 5Z H[ 4l ;XZ>Z.[IuI[0\\ 2X 2\\ ?X ?\\ \\ ?X MY@U 8y ;X6X 4a $X 1X9[ HUEX MT MeLW :[D[ I~d &T=l:T " + "K~d Io 5m 3Y1Y AgEW 3Z Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[ LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z H~Q G~Q F~Q G" + "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[ AX NX Y NY Y X" + " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y Y NX Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D" + "Z0Y GY9Z ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y HZ 5X_@XAa*[I\\6Y ;Y CZ %V MX /X 7Y-Z 5Z I[ 3n >X;Z ] G`9\\ .Z 4s @[9` " + " =i /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z" + "$ZAW@WAZ F_ %\\ $[ &X IX EY &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0" + "Z ;Z *Z FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z MX N[ 7g1U U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BYY1Y%Z" + " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\ 1X 1\\ @X @\\ M\\ @X NZ" + "@U 8y ;W4X 5` #X 1X8Z HUEX MT LbJW ;ZC[ H~d &T=j8U L~d Io 5l 2Y1Y @fEW 3Z Nl 0c 0[CZ&lDW5[>mEXE\\N^" + "AlAX6\\ LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K" + "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\ BY X NX NY Y X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y Y NX Y 5Z0Y HY" + "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /" + "Y:Z IZ 4Y=T 6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m 4Z 3Y !X@X ;l @[>WBe,ZG\\7Y ;Y" + " CZ %V ;~c LX 7Y-Z 5Z J\\ 2n @Y;Z N\\ G`8\\ /Z 5u A\\V+Y8Y G~R LZ !Z\"Z\"~Q" + " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZY1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z " + "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX NZ@U 8y dEW 3Z Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6] LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z H~Q " + "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\ CY X NX" + " NY Y Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y Y NX Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$ZX /Z;Z JZ 2X>U 6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y " + "N~m 4Z 3Y !X@X :n 'WBg.ZE\\8X :Y CZ %V <~e NX 6Y-Y 4Z K\\ #a AX:Z M\\ H_6[ 0Z" + " 6aI` A]?c ?f $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z " + "K['v +o 2Y 9Z(Z IZq:X !U:[9U&Y5] DY?d =jLX FY/Z C[ " + ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX" + " B~o BX NZ@U 8y mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ" + "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y Y NX Y 5Y/" + "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CYX .Y;Y JZ 1Y?U 6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m 4Z 3Y !W?X 9p +XCi0ZC\\9X " + " :Y CZ %V <~e NX 6Z.Y 4Z L\\ M^ CY:Z L[ H^4Z 0Z 7^A^ C_Ce ?c Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\"" + "Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X HX FY " + " >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^ KY .Y1Y CY;Y KYCXIXCY " + "Bc 4Y\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ" + "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX NY?U 8y mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7" + "Y CY7Z#Z:Z Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ " + "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y Y NX Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X" + " HY0X GX0X GX0Y CYX .YW-Y6Y HZ2\\ Z !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z" + "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X HX FY At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y NX" + " 0XM\\ MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e JX Ke KzF^ !U9Y7T'Z4[ CY7] @[E" + "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L[" + "4~p BX B~o BX C~q CX NY?U 8y Z !Z !Z \"Z :Z#[)Y>ZCY*Z K" + "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z 9WK~KW/WJ}JW.W:\\:W.W" + "9[:W/W9[9W >X .Z=Y JZ /X@U 6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l 4Z 3Y \"X@X 3n /X" + "CZIZ2Z@\\W.Z6" + "Z IZ1[ Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ " + "\"X GX GY Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0XN\\ LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z" + " KYDXGWDY @a 3Z>Y +[ 5d IX Ic L~d !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y " + "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX NY?U 8y Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?" + "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0" + "X GX0Y BY>Z BY.Y EY>Y 8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z KZ .YAU 6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z" + "CZ L~k :y KY \"X@X 0m 1WCYEY3Y>\\=X 9Y BY %V <~e =l X 5Z.Y 4Z \\ E[ GY8Z JZ I]" + "2Z 2Z 8[7[ BqMZ ?^ C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s" + " I[ LZ&[Cc Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X GX GY Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0e KY" + " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b GX Ga L~c T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f " + "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[WEY9T -X EY1Y 1WEW 3Z 6ZCZ 7X7" + "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z " + "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` Y 8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z " + " LZ -YBU 5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW 'y JY \"W?X ,j 3WCYCY4Y=\\>X 9Y CZ" + " %V <~e =l X 5Z.Y 4Z !\\ C[ IY7Z JZ I]2Z 3[ 9[5[ BoLZ ?a Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z MZ 0" + "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^ M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X GX GY " + "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ " + "3` EX E_ L\\Cx NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ" + "4Y 4\\ 1Z 1[ NZ.[" + "WDX:U -X EY1Y 1WEW 3Z 5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[ IZ !Z " + " Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ " + "B{'~x/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y " + "DY@Z 8WK~KW/WJ}JW.W=aX ,Y?Y LZ +XBU 6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX " + " (y JY \"W?W (h 5XCXAX5Z<\\@Y 9Y CZ $T ;~e =l X 5Z/Z 4Z \"\\ AZ IX6Z JZ I\\1[ 4Z 8Z3Z AmKZ" + " ?d d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(YWCX;U -X EY1Y 1WEW 3Z Is 0YAX 8W6UJV IW)W" + " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Yc=W.W=[6W/X:[:X >X ,Y@Z M[ " + "+YCT 5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX )y HX #X@X (TNc 6WCX@X5Y:\\AX 8Y CZ :~e" + " =l !X 4Z/Z 4Z #\\ @[ KY6Z IZ I[0Z 4Z 9Z2[ @jJZ ?f %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z MZ 0Z Z'Z(Z" + " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(YWBXZ !Z !Z \"Z :Z#Z(YW.W>[5W.W:[:W =W +ZAY LZ *YDU 5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX " + ")y HX 6~e 9TJ_ 7XCX?X6Y9\\BX 8Y CZ KX Nl !X 4Z/Z 4Z $\\ >Z LY5Z IZ I[0Z 5Z 8Z1Z >fHY =h " + " +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[ K\\ 6Y 9Z(Z FZ" + "BZ MYFXY FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0e KY FY3Y2Y+Y1Y KZ-Y JY.Y" + " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e JX Ke LU4k IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&" + "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1] 1X 1\\ @X @\\ L\\ AX DX 4" + "Z?U -Z (X4X H~W ;\\;W GTDX\"U s A[D[ 6X %T>WBXZ !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC" + "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY" + "-Y LZ-Y MY-Z MY-Y LZ-Y CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY 6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ " + " MZ (XDU 5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX +y GX 6~e 9TG] 8WBW>X6Y8\\DY 8Y CZ " + " KX Nl !X 4Z/Z 4Z %\\ =Z LX4Z IZ I[0Z 5Z 9Z0Z X3W?W/~S KZ-Z\"Z \"Z#Z!Z MZ 0[!Z" + "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[ K\\ 7Y 9Z(Z FZCZ LZGX^ .YCZ ." + "[ )_ KX L_ ES/e FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J" + "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\ 1X 2\\ ?X ?[ M\\ @X DX 4Z?U -Z 'W4W G~W :]>X GTDY#U s @[D[ 7" + "X %U?WAX>U ,X EY1Y 1WEW \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z IZ !Z Z" + " !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8" + "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y BYDXAY KY0X HY0X GX0X GX0Y" + " @ZCY ?Y.Y CYBY 5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ 6~d IYET 4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX " + " Z 3X 6~e 9TF\\ 9WBX=W7Z7\\EX 7Y CZ KX Nl \"X 3Z/Z 4Z &\\ ;Z M~Z %Z I[0Z 6[ 9Z/" + "Y 8ZCZ 8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6[" + " J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\ LX EX IY L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y NX 0XM\\ MY " + "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\ LX M\\ AR+` CT9[:U'Z3X AY0Y FY9Z FY-Z " + "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\ 2X 3" + "\\ >X >[ \\ ?X DX 4Z?U -Z 'X6X G~W 9^@X GUDY$T Ns ?[CZ 8X %U?WAY?U ,X EY1Y 1WEW \"s 4" + "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N" + "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2ZX )YCY 5~d IYFU 4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX Z 3X 6~e 9TD[ ;XBX=X8" + "Z6\\GY 7Y CY JX Nl \"X 2Y/Z 4Z '\\ :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z 5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ " + "#Z$[!Z MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[ K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\ KX EX" + " IY LZ4Y FY.Y KZ %Z.Y KZ X DX 4Z?U -Z 'X6X G~W " + "8^BX FUDY%U Ns =ZCZ 9X $U@W@X?T +X EY1Y 1WEW \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~" + "T J~U K~T*~ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ " + "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y Y Y Y 9Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYEW?Y" + " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY 4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ 5~d HXFU 4~_+~i z @w DX.Y" + " IX.X BZ@Y 6YGZ IY Y @~e 9TCZ ;WAX=X8Y4\\HX 6Y CY JX Mj !X 2Y/Y 3Z (\\ 9Z" + " M~Z %Z I[0Z 6Z 8Z/Z \"Z 2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ " + "Z Z .Z [%Z4Z JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\ JX EX IY MZ3Y FY.Y JY %Z/Z JY Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z" + "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y Y Y Y 9Y Y " + "NX Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ 4X:X9W,W JW+WE\\H[EX,X" + "E[1W,W JW =X )ZEY 4~d HYHU 2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX Z A~e 9TCZ ~d >i 2Z GV>X3W@W0~V LZ-[\"Z " + "#Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[ KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[ IX DX J" + "Y MY2Y FY.Y JY %Z/Z JY Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N" + "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\ 5X 6\\ JTEXET J[&\\ KSDXES $Y 3Y?U -Z &Y:Y F~W 5_GX DU" + "CZ9QAU DZCZ ;X $VAW?YBU +X EY1Y 1WEW DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V" + ",~P :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y " + "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y Y Y Y 9Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z BYGX?Z KY1Y HY0X" + " GX0X GX0Y >YFZ >Y.Y AYFY 2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY 4~d GXHU 2kNRMk*tNq Mv Y 7ZIZ GY !Z A~e 9TBY `=Y(Y8ZJZ([\"[ Z " + ".[!Z$Z3Z KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\ IX DX JY MY2Y FY.Y JY $Y/Z JY YEY CYIWBXIX @f 0YGZ 0[ LZ NX NY 'U>WMW?V&Z4Y AY/Y HY8Y" + " EZ.Y FZ %Y1Y Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\ 6" + "X 7\\ JVFXFV J[(\\ KUEXFU %Y 3Y?U -Z %YXCU *X EY1Y 1WEW" + " F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+[" + "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y Y Y Z :Y Y" + " NX Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY 2X;X:W+X LX*WH\\D[HX" + "*WG[0W+X LX =X (YFZ 4~d GYIU 2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX !Z A~e 9T" + "BZ >W?W;W8Z2\\MY 4Y DY JX 4X 1Z1Z 3Z +\\ 6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z )i D~d Ci -Z GV=W4XAW/~W M" + "Z-[\"[ $Z&[ NZ MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[ L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\" + " HX DX JY NY1Y FZ0Z JY $Y/Z JY YEY BXJXAWJY A[N[ 1YGY 0[ JY NX NY 'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y Z " + "/Z K_MZ BUC]BVBU A[D[ >X #VBW=XDU *X EY1Y 1WEW G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M" + "~W N~X M~W N~X.~Q :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z" + " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z :Y Y NX Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z " + " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY 1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW Z 7ZJY EY !Z 1X@X &TAY ?X?W;W8Z1\\NX 3Y DY JX 5Y 0" + "Y1Z 3Z ,\\ 5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2" + "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[ M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\ GX CX KY NY1Y FZ0Z JZ %Y/Z JZ =Y 4" + "Y0Z GY1Y 5Y NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[ IY NX Z &VB" + "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y Z !Z !Z \"Z :Z&[&" + "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z" + " !Z :Y Y NX Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y AYIWW;W8Z0e 3Y EZ JX 5X /Z2Y 2Z -\\ 4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z #j J~d Ii CW>X6Y" + "BX0~Y NZ-[![ %Z'\\ NZ MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<][ 0" + "Z 3\\ FX CX KY NY2Z FZ0Y IZ %Y/Z JZ =Y 4Y0Z GY1Y 5Y NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 7Z 8Y" + " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[ HY NX Y %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ " + "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\ 9X :\\ J\\IXI[ I\\/\\ K[HXI[ (Y 3Z@U -Z " + "%^F^ /Z X \"f >VBnCU >[D[ @X \"VCWZ !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v " + "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y Y Y Y :Y Y NX Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW" + ";Y IX1Y GX1Y GY2Y GY2Z YJX(XJY/X)X Y W;W7Y/c 2Y EY IX 5X /Z3Z 2Z .\\" + " 3Z M~Z &Z FY1Z 8[ 6Z/Z $Z i L~d Li @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ MZ -[$Z'Z(Z :Z \"Z 4Z<] Z !Z !Z \"Z :Z(\\%" + "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s YJY .X=X=Y(" + "X!X'YJWX.Y HY2Y CZW=X8ZC" + "W/Z*Z Z-Z N[ &Z(\\ MZ MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]Y 4Z2[ GY1Y 5Y NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y 6Y" + " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[ EX NX Y $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB" + "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\ ;X <\\ F\\KXK\\ F\\3\\ H\\JXK\\ 'Y " + "2ZAU -Z 'z 1Z X Na ;V@jDV :ZCZ BX UDW;XIU 'X EY2Z 1WEW KZCZ 3X9U@V\"W*X LX9VAW H[ " + "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!" + "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z ;Y Y NX Y 5Z0Y HX0X IZ1Z IY0Z KZ0" + "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ #Z JXLU" + " -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ #Z 3X@X %TAX @WZ 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[ Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\ DX BX LY NY3[ F[2Z HZ %Y1" + "[ IZ >Y 3Y2[ GY1Y 5Y NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y 6Z 9Y .Y2Z Z !Z" + " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2" + "[.Y2\\ 2Z $Z !Z !Z Y ;Y Y NX Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z =" + "YLY ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY #Z IYNU ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ " + " $[,P )W?X %TBY AXXMY DZDZ 2YLY 3[ DY X Y \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y " + "FY2Z%[<\\a@V 7YBY CX NV LV BZ3Z 1WEW LYBY 2W8U?V#W+X KX9U" + "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z Z !Z >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8" + "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z ZMZ DZMW4WMZ![7Z 3Z 7\\ BX AX MY NY3" + "[ F\\4Z FZ &Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y 5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ " + "3YMY 3[ CY X Y !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3" + "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z i i 2WZ4" + "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW :X :" + "V >r >V/V @s #Z 2[CU -Z +[MeL[ 5Z X G\\ :W!V 3W@W 7V!W AZ4[ 1WEW LW@W 1W7s,X-" + "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z Z !Z >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\," + "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z =Y Y NX Y " + "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8" + "X $YMY %[ F^ '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[ %[1S -Y 'X@X ;Q:TCZ CX:X=X" + "5[.] /Y HY HX NZ GZ 'X +[8Z 0Z 4\\ 0[ 'Z M\\ CZ6[ 9Z 2[3[ '[ 0Y Y ?f f BX DW=\\C_J[.Z&Z\"Z0\\ " + "J\\(T'Z._ JZ MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[ @X" + " AX MY NY5] F]6Z DZ &Z5] G[ AY 2[8^ GY1Y 5Y NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y 6Y 8Y -Z6\\ ;Z" + "MZ =b=b EZ@Z 3d 5[ AY X Y L[:\\ IZ;` D[4Z FXZ5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z " + "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU 9X 9T

q \"Z 1ZCU -Z ,[JaI[ 6Z X F\\ :W#V 1" + "V?V 7W#W @[5[ 1WEW LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z Z " + "!Z >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY" + "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[ >Y Y NX Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ " + "FY6] 9c 9]6Z :d )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ %Z D] $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\ " + " '\\3T -Z (W?X ;Sd c @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-" + "Z NS*\\ 6Y 6[1[ Z 4c 5[ @Y X Y HS3V FZZ%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS 8X 8R :n :R+R U 6W%W ?[6\\ 1WEW LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z" + "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z Z !Z >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\" + "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[ ?Y Y NX Y 3[7[ FX0X G[7[ E[7[ FZ7[ F" + "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d %Z C\\ ?S 2\\ETD" + "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\ '\\5U -Z (W?W :U>TE[ CX8X?X3\\3b 1Y IY GX NZ GZ (" + "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[ ;a ` =Z EX[ 4b 6[ ?Y X Y " + "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ" + "(Z :Z 8]3] FQ 7X 7P 8l 8P)P :m Z 0[EU -Z .[?P?[ 8Z X D[ 9W(W -T\\8] 1WEW " + " LSZ !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3" + "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q " + "G\\-Q G\\-Q 5Y Y NX Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b &[2[" + " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c &Z B\\ ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :] (\\7V " + "-Z )X@X :W@TF[ BW7X?X3]6e 1X IY GX NZ GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[ 8^ " + " ^ ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a" + "\"Z0[ 7Z ;\\ >X @X NY MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ " + "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[ >Y X Y F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#[" + "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\ Z #^A] :n DZ(Z :Z 7]5] +X Mj (k NZ 0\\FUBP ;Z /[,[ " + "9Z X CZ 8X+W *R;R 4X+X =]:^ 1WEW LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z" + "8Z/Z 3_2Y NZ !Z Z !Z >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #" + "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y Y NX Y 1[:[ EX0X F\\;\\ C\\;[ C[:" + "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c 'Z " + "@[ @T JT _ %] 7X.Y D^D^ BZ6Y 6b 9_ *];X -Z )X@X :ZCTH] CX7YAX1^:h 2Y JY GX NZ" + " GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[ 5[ [ 8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ MZ &`4^" + "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[ =X @X NY M[\\ @]7R" + " D\\=a E]4U 7Y /]Bc GY1Y 5Y NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[ >Y " + "X Y E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J" + "Z$Z N[ NZ \"^C^ 7g @Z(Z :Z 7_9_ +X Lh &i MZ /]HUDR ;Z .Y*Y 8Z X BZ 8Y/X (Q:Q 2X/Y " + " <^<` 2WEW LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z Z !Z >Z !Z !Z \"Z :" + "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[=" + "` E[=a 8a 8`=\\ 6` #]:] H]9] G]:] G]:] H]9] 4W !a 'Z ?Z ?U KT N] $] 7X.Y Cv AZ6Z 7a 7a " + " -_?Z -Z )W?X :^GTK_ CX5XAX0_>k 3Y JX FX NZ GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX" + "@_ ,Z \"[ 3Y X 5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^_ 8b @a2a$[.[ 8Z <~` AX ?X Y L\\@c Fb@] ?^` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>" + "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[ 0V U 2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H" + "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX ?X Y L^DZNY FYNZF_ =`CY B^EZNY CaB] 7" + "Y .qMY GY1Y 5Y NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y X Y " + " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :" + "Z 5dGd )X Jd \"e KZ -`MUKY H~U IU&U 6Z X AY 5Z7Z LZ7Z ;~d 3cFk 8WEW " + " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd" + " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]" + " DaA] DaA] DaA] 5Y Y NX Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_ 7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^" + " dLd CdMd BdLd CdLd DeMd 2X !` %X =Y ?U LV MZ !Y 5X.Y As AZ4Y 6` 5~] )x -Z " + "*X@X 9} BX3YFZ-{L] 4Y LY FX NZ GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[ -S S 0Z BW8aG[%[\"Z$~R" + " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX ?X Y KsN" + "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y X " + " Y CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~ 'X " + " Ib c JZ ,u H~U HS$S 5Z X AY 4\\>\\ I]>\\ :~d 3~Y 8WEW CW KV)W0X FX LW" + "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} " + "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y Y NX Y .v BX0X Cw =w >v >w =w 8{ ?qMX CqMX C" + "qMX CqMY 6] 6YNr 3^ My Ay @y @z Ay 1X _ $V X !" + "Y JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6" + "] 5~P 2Y X Y CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z" + " :Z 2{ &X H` Ma IZ +t H~U GQ\"Q 4Z X AY 2aLb FaKa 8~d 3YNlN_ 8WEW " + "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM" + "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y Y NX Y -t AX0X Bu ;u pLX CpLX CpLX BoLY 6] 6YMp 1] Lv >w =v =v >w 0X _ #T ;X ?W MV LW LV 4X.Y ?n >Y3Z 7_ 1~Z " + " 't +Z *W?X 8y @X1j)vG] 5X MY EX NZ GZ *X !p <} I~S Iz Z By 6r 5Z )w As (Z \"[ " + " 5Z AX HZ Z%~ 9|$~P >~S FZ ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX " + ">X !Y JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y Y NX Y ,r @X0X As 9s :r :s 9s 7z <" + "nKX BnKX BnKX BnKY 6] 6YLn 0\\ Jt ;s :t ;t ;s .X N] !R 9V >W NX LU KU 3X.Y >l =Y2Y 7_ /~X " + " %p )Z *W?W 4u @X/i(tE] 6Y NX DX NZ GZ *X m :} I~S Iy NZ Bw 2o 5Z 'u @r 'Z \"Z " + " 4Z AY J[ Z%} 6x\"} <~S FZ N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX >X" + " !Y InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2" + "[ 7\\ 5~P 2Y X Y AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z " + ":Z(Z :Z /u #X F\\ I] GZ )r H~U *Z X AY /p >o 4~d 3YMiK^ 8WEW EX " + "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv p %Z \"Z " + " 4Z @X JZ MZ&{ 3u z 9~S FZ Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z Lr BZ(Z!y -Y -s /] <^.]&[&[ m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7" + "\\ K[0Z 6Z 4~P 2Y X Y ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o" + "Do @Z :Z(Z :Z .s \"X EZ G[ FZ 'p H~U *Z X AY ,k :k 2~d 3YLgJ^ 8WEW " + " EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r " + " AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y Y NX Y *m =X0X >m 3m 5n 5m" + " 3m 6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z El 3k 2l 3l 4l *X N\\ 5U ?Y Y KR HQ 1X.Y 9b 9Y1Z 7" + "] )~S \"j &Z +X@X -h ;X+c!l?\\ 6X Y DX Z FZ +X Kh 8} I~S Fr JZ As ,i 3[ $n ;m " + "#Z \"Y 3Z ?X KZ MZ&x -p Mu 4~S FZ Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i L^ 4e " + "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n NX DX EY EZ %m G~U *Z X BZ )e 4e /~d 3YKeH] 8" + "WEW FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4" + "n 5n 6o 6n @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y Y NX Y (i ;X0X " + "fGX >fGX >fGY 4Y 4YHf +Z Bg /g .g -g /g (X M[ 5T ?Z !Z JP 'X.Y 5" + "[ 6Y0Y 7] &~P Ne $Z +W?X '] 6W)a Mh<\\ 7Y !X CX Y EZ +X Id 6} I~S Cm HZ =l 'e " + "1Z i 6h !Z #Z 3Z ?Y M[ M['s &k Jo .~S FZ Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z Gl AZ'Z Jm (Y (i )\\ " + ";].]'[#Z =Z =~a AX =X \"Y DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y" + " $k ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y X Y ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e " + "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i LX CV CW DZ #h D~U *Z X -R9Z #[ *[ *~d 3" + "YIaE\\ 8WEW GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o " + "5Y,^ Ai /h 0i 0i 0i >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y Y NX Y" + " &e 9X0X :e ,f -f -e ,f 4XFe 0cEX a NU CZ N` 9X -T<[ " + " LYG]BX 5WEW %U HW NX MX GZ (d +b (b )b )a )b 9V;a " + ")c *c *c *c =a 4_ &^ %^ $^ &_ &_ :_/c RM] !R Z 5\\ " + " 9X ;X $Y HY NY 0Y 'X NY BY X !Y " + ":Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X -p " + " IY 8WEW #V &Z MV " + " 0U 'P ;Y 2Y >Z 8X " + " MT *X &X 9X DX " + " 5X ?\\%W ?Z 4\\ :X ;X $Y " + " IZ NY 0Y 'X NY BZ !X !Y :Y 8Y 4Y *Y 1Y EX 3Y " + " CZ IU 3X -o HY 8WEW \"V " + " 'Z LU 0V " + " CZ 2Y >Y 7X " + " MT )X 'X 9X DX 5W <\\(X ?" + "Z 3\\ ;Y e GX 2f KZ LY 0Y 'X !Y >" + "\\ %X &] 9Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3" + "X $^ @Y 8WEW !V '\\:V ;V " + " 1W GZ 0Y @Z " + " FWHX LT 'X +W 7W " + " V 5b?c A[ -\\ ?e !f " + " f /X 0g 9Y 8Y 4Y *Y " + " 1Y EX 3Y CZ IU 3X 5Y " + " NV &\\=X ;V " + "1W GY /Y AZ EWHX " + " LT &W ,X 7V V 3~T " + " A] ,\\ @e !f d " + " %e -Y Nd @c " + " (m @c " + " +u $b -Y 'X 0d 2^ /X 0_ 1Y 8Y 4Y *Y " + " 1Y EX 3Y CZ IT 2X 5Y " + "-c !q Hd >c " + " $d ,Y Nd ?b " + " %g =" + "b *t #a ,Y 'X 0d " + " ,X /X 0Y +Y 8Y 4Y *Y 1Y EX 3Y CZ '" + "X 5Y -c Nm Fc " + " =c $c +Y Nc " + " >a " + " M\\ 8a \"~Y 1" + "r !` +Y 'X 0c 1X 1Y 8Y 4Y *Y 1Y EX 3Y " + " CZ &W 5Y -b Lj " + " Db std::printf(). + \note If configuration macro \c cimg_strict_warnings is set, this function throws a + \c CImgWarningException instead. + \warning As the first argument is a format string, it is highly recommended to write + \code + cimg::warn("%s",warning_message); + \endcode + instead of + \code + cimg::warn(warning_message); + \endcode + if \c warning_message can be arbitrary, to prevent nasty memory access. + **/ + inline void warn(const char *const format, ...) { + if (cimg::exception_mode()>=1) { + char *const message = new char[16384]; + std::va_list ap; + va_start(ap,format); + cimg_vsnprintf(message,16384,format,ap); + va_end(ap); +#ifdef cimg_strict_warnings + throw CImgWarningException(message); +#else + std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message); +#endif + delete[] message; + } + } + + // Execute an external system command. + /** + \param command C-string containing the command line to execute. + \param module_name Module name. + \return Status value of the executed command, whose meaning is OS-dependent. + \note This function is similar to std::system() + but it does not open an extra console windows + on Windows-based systems. + **/ + inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) { + cimg::unused(module_name); +#ifdef cimg_no_system_calls + return -1; +#else + if (is_verbose) return std::system(command); +#if cimg_OS==1 + const unsigned int l = (unsigned int)std::strlen(command); + if (l) { + char *const ncommand = new char[l + 24]; + std::memcpy(ncommand,command,l); + std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent + const int out_val = std::system(ncommand); + delete[] ncommand; + return out_val; + } else return -1; +#elif cimg_OS==2 + PROCESS_INFORMATION pi; + STARTUPINFOA si; + std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); + std::memset(&si,0,sizeof(STARTUPINFO)); + GetStartupInfoA(&si); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW; + const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi); + if (res) { + WaitForSingleObject(pi.hProcess,INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return 0; + } else { + char* lpMsgBuf; + + // Get the error message. + DWORD errorCode = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0); + cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s", + module_name==0?"(null)":module_name, + command==0?"(null)":command, + errorCode,lpMsgBuf); + return -1; + } +#else + return std::system(command); +#endif +#endif + } + + //! Return a reference to a temporary variable of type T. + template + inline T& temporary(const T&) { + static T temp; + return temp; + } + + //! Exchange values of variables \c a and \c b. + template + inline void swap(T& a, T& b) { T t = a; a = b; b = t; } + + //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { + cimg::swap(a1,b1); cimg::swap(a2,b2); + } + + //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { + cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { + cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7, T8& a8, T8& b8) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); + } + + //! Return the endianness of the current architecture. + /** + \return \c false for Little Endian or \c true for Big Endian. + **/ + inline bool endianness() { + const int x = 1; + return ((unsigned char*)&x)[0]?false:true; + } + + //! Reverse endianness of all elements in a memory buffer. + /** + \param[in,out] buffer Memory buffer whose endianness must be reversed. + \param size Number of buffer elements to reverse. + **/ + template + inline void invert_endianness(T* const buffer, const cimg_ulong size) { + if (size) switch (sizeof(T)) { + case 1 : break; + case 2 : { + for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) { + const unsigned short val = *(--ptr); + *ptr = (unsigned short)((val>>8) | ((val<<8))); + } + } break; + case 4 : { + for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) { + const unsigned int val = *(--ptr); + *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24); + } + } break; + case 8 : { + const cimg_uint64 + m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24, + m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56; + for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) { + const cimg_uint64 val = *(--ptr); + *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) | + ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56)); + } + } break; + default : { + for (T* ptr = buffer + size; ptr>buffer; ) { + unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); + for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); + } + } + } + } + inline void invert_endianness(bool* const, const cimg_ulong) {} + inline void invert_endianness(unsigned char* const, const cimg_ulong) {} + inline void invert_endianness(char* const, const cimg_ulong) {} + + //! Reverse endianness of a single variable. + /** + \param[in,out] a Variable to reverse. + \return Reference to reversed variable. + **/ + template + inline T& invert_endianness(T& a) { + invert_endianness(&a,1); + return a; + } + + // Conversion functions to get more precision when trying to store unsigned ints values as floats. + inline unsigned int float2uint(const float f) { + int tmp = 0; + std::memcpy(&tmp,&f,sizeof(float)); + if (tmp>=0) return (unsigned int)f; + unsigned int u; + // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&u,&f,sizeof(float)); + return ((u)<<2)>>2; // set sign & exponent bit to 0 + } + + inline float uint2float(const unsigned int u) { + if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287) + float f; + const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1 + // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&f,&v,sizeof(float)); + return f; + } + + //! Return the value of a system timer, with a millisecond precision. + /** + \note The timer does not necessarily starts from \c 0. + **/ + inline cimg_uint64 time() { +#if cimg_OS==1 + struct timeval st_time; + gettimeofday(&st_time,0); + return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000; +#elif cimg_OS==2 + ULARGE_INTEGER ul; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + return (cimg_uint64)ul.QuadPart/10000; +#else + return 0; +#endif + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline cimg_uint64 tictoc(const bool is_tic); + + //! Start tic/toc timer for time measurement between code instructions. + /** + \return Current value of the timer (same value as time()). + **/ + inline cimg_uint64 tic() { + return cimg::tictoc(true); + } + + //! End tic/toc timer and displays elapsed time from last call to tic(). + /** + \return Time elapsed (in ms) since last call to tic(). + **/ + inline cimg_uint64 toc() { + return cimg::tictoc(false); + } + + //! Sleep for a given numbers of milliseconds. + /** + \param milliseconds Number of milliseconds to wait for. + \note This function frees the CPU resources during the sleeping time. + It can be used to temporize your program properly, without wasting CPU time. + **/ + inline void sleep(const unsigned int milliseconds) { +#if cimg_OS==1 + struct timespec tv; + tv.tv_sec = milliseconds/1000; + tv.tv_nsec = (milliseconds%1000)*1000000; + nanosleep(&tv,0); +#elif cimg_OS==2 + Sleep(milliseconds); +#else + cimg::unused(milliseconds); +#endif + } + + inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) { + if (!*p_timer) *p_timer = cimg::time(); + const cimg_uint64 current_time = cimg::time(); + if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; } + const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time); + *p_timer = current_time + time_diff; + cimg::sleep(time_diff); + return time_diff; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \return Number of milliseconds elapsed since the last call to wait(). + \note Same as sleep() with a waiting time computed with regard to the last call + of wait(). It may be used to temporize your program properly, without wasting CPU time. + **/ + inline unsigned int wait(const unsigned int milliseconds) { + cimg::mutex(3); + static cimg_uint64 timer = cimg::time(); + cimg::mutex(3,0); + return cimg::wait(milliseconds,&timer); + } + + // Custom random number generator (allow re-entrance). + inline cimg_uint64& rng() { // Used as a shared global number for rng + static cimg_uint64 rng = 0xB16B00B5U; + return rng; + } + + inline unsigned int _rand(cimg_uint64 *const p_rng) { + *p_rng = *p_rng*1103515245 + 12345U; + return (unsigned int)*p_rng; + } + + inline unsigned int _rand() { + cimg::mutex(4); + const unsigned int res = cimg::_rand(&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline void srand(cimg_uint64 *const p_rng) { +#if cimg_OS==1 + *p_rng = cimg::time() + (cimg_uint64)getpid(); +#elif cimg_OS==2 + *p_rng = cimg::time() + (cimg_uint64)_getpid(); +#endif + } + + inline void srand() { + cimg::mutex(4); + cimg::srand(&cimg::rng()); + cimg::mutex(4,0); + } + + inline void srand(const cimg_uint64 seed) { + cimg::mutex(4); + cimg::rng() = seed; + cimg::mutex(4,0); + } + + inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) { + const double val = cimg::_rand(p_rng)/(double)~0U; + return val_min + (val_max - val_min)*val; + } + + inline double rand(const double val_min, const double val_max) { + cimg::mutex(4); + const double res = cimg::rand(val_min,val_max,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline double rand(const double val_max, cimg_uint64 *const p_rng) { + const double val = cimg::_rand(p_rng)/(double)~0U; + return val_max*val; + } + + inline double rand(const double val_max=1) { + cimg::mutex(4); + const double res = cimg::rand(val_max,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline double grand(cimg_uint64 *const p_rng) { + double x1, w; + do { + const double x2 = cimg::rand(-1,1,p_rng); + x1 = cimg::rand(-1,1,p_rng); + w = x1*x1 + x2*x2; + } while (w<=0 || w>=1.); + return x1*std::sqrt((-2*std::log(w))/w); + } + + inline double grand() { + cimg::mutex(4); + const double res = cimg::grand(&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline unsigned int prand(const double z, cimg_uint64 *const p_rng) { + if (z<=1.e-10) return 0; + if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z); + unsigned int k = 0; + const double y = std::exp(-z); + for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng); + return k - 1; + } + + inline unsigned int prand(const double z) { + cimg::mutex(4); + const unsigned int res = cimg::prand(z,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + //! Cut (i.e. clamp) value in specified interval. + template + inline T cut(const T& val, const t& val_min, const t& val_max) { + return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val; + } + + //! Bitwise-rotate value on the left. + template + inline T rol(const T& a, const unsigned int n=1) { + return n?(T)((a<>((sizeof(T)<<3) - n))):a; + } + + inline float rol(const float a, const unsigned int n=1) { + return (float)rol((int)a,n); + } + + inline double rol(const double a, const unsigned int n=1) { + return (double)rol((cimg_long)a,n); + } + + inline double rol(const long double a, const unsigned int n=1) { + return (double)rol((cimg_long)a,n); + } + +#ifdef cimg_use_half + inline half rol(const half a, const unsigned int n=1) { + return (half)rol((int)a,n); + } +#endif + + //! Bitwise-rotate value on the right. + template + inline T ror(const T& a, const unsigned int n=1) { + return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a; + } + + inline float ror(const float a, const unsigned int n=1) { + return (float)ror((int)a,n); + } + + inline double ror(const double a, const unsigned int n=1) { + return (double)ror((cimg_long)a,n); + } + + inline double ror(const long double a, const unsigned int n=1) { + return (double)ror((cimg_long)a,n); + } + +#ifdef cimg_use_half + inline half ror(const half a, const unsigned int n=1) { + return (half)ror((int)a,n); + } +#endif + + //! Return absolute value of a value. + template + inline T abs(const T& a) { + return a>=0?a:-a; + } + inline bool abs(const bool a) { + return a; + } + inline int abs(const unsigned char a) { + return (int)a; + } + inline int abs(const unsigned short a) { + return (int)a; + } + inline int abs(const unsigned int a) { + return (int)a; + } + inline int abs(const int a) { + return std::abs(a); + } + inline cimg_int64 abs(const cimg_uint64 a) { + return (cimg_int64)a; + } + inline double abs(const double a) { + return std::fabs(a); + } + inline float abs(const float a) { + return (float)std::fabs((double)a); + } + + //! Return hyperbolic arcosine of a value. + inline double acosh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::acosh(x); +#else + return std::log(x + std::sqrt(x*x - 1)); +#endif + } + + //! Return hyperbolic arcsine of a value. + inline double asinh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::asinh(x); +#else + return std::log(x + std::sqrt(x*x + 1)); +#endif + } + + //! Return hyperbolic arctangent of a value. + inline double atanh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::atanh(x); +#else + return 0.5*std::log((1. + x)/(1. - x)); +#endif + } + + //! Return the sinc of a given value. + inline double sinc(const double x) { + return x?std::sin(x)/x:1; + } + + //! Return base-2 logarithm of a value. + inline double log2(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::log2(x); +#else + const double base2 = std::log(2.); + return std::log(x)/base2; +#endif + } + + //! Return square of a value. + template + inline T sqr(const T& val) { + return val*val; + } + + // Return inverse of error function. + template + inline T erfinv(const T& val) { + const T + sgn = val<0?-1:1, + x = (1 - val)*(1 + val), + lnx = std::log(x), + tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx), + tt2 = lnx/(T)0.147; + return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2)); + } + + //! Return cubic root of a value. + template + inline double cbrt(const T& x) { +#if cimg_use_cpp11==1 + return std::cbrt(x); +#else + return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3); +#endif + } + + template + inline T pow3(const T& val) { + return val*val*val; + } + template + inline T pow4(const T& val) { + return val*val*val*val; + } + + //! Return the minimum between three values. + template + inline t min(const t& a, const t& b, const t& c) { + return std::min(std::min(a,b),c); + } + + //! Return the minimum between four values. + template + inline t min(const t& a, const t& b, const t& c, const t& d) { + return std::min(std::min(a,b),std::min(c,d)); + } + + //! Return the minabs between two values. + template + inline t minabs(const t& a, const t& b) { + return cimg::abs(b) + inline t minabs(const t& a, const t& b, const t& abs_b) { + return abs_b + inline t max(const t& a, const t& b, const t& c) { + return std::max(std::max(a,b),c); + } + + //! Return the maximum between four values. + template + inline t max(const t& a, const t& b, const t& c, const t& d) { + return std::max(std::max(a,b),std::max(c,d)); + } + + //! Return the maxabs between two values. + template + inline t maxabs(const t& a, const t& b) { + return cimg::abs(b)>cimg::abs(a)?b:a; + } + + template + inline t maxabs(const t& a, const t& b, const t& abs_b) { + return abs_b>cimg::abs(a)?b:a; + } + + //! Return the sign of a value. + template + inline T sign(const T& x) { + return (T)(cimg::type::is_nan(x)?0:x<0?-1:x>0); + } + + //! Return the nearest power of 2 higher than given value. + template + inline cimg_uint64 nearest_pow2(const T& x) { + cimg_uint64 i = 1; + while (x>i) i<<=1; + return i; + } + + //! Return the modulo of a value. + /** + \param x Input value. + \param m Modulo value. + \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type. + **/ + template + inline T mod(const T& x, const T& m) { + const double dx = (double)x, dm = (double)m; + if (!cimg::type::is_finite(dm)) return x; + if (cimg::type::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm)); + return (T)0; + } + inline int mod(const bool x, const bool m) { + return m?(x?1:0):0; + } + inline int mod(const unsigned char x, const unsigned char m) { + return x%m; + } + inline int mod(const char x, const char m) { +#if defined(CHAR_MAX) && CHAR_MAX==255 + return x%m; +#else + return x>=0?x%m:(x%m?m + x%m:0); +#endif + } + inline int mod(const unsigned short x, const unsigned short m) { + return (int)(x%m); + } + inline int mod(const short x, const short m) { + return (int)(x>=0?x%m:(x%m?m + x%m:0)); + } + inline int mod(const unsigned int x, const unsigned int m) { + return (int)(x%m); + } + inline int mod(const int x, const int m) { + return (int)(x>=0?x%m:(x%m?m + x%m:0)); + } + inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) { + return (cimg_int64)(x%m); + } + inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) { + return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0)); + } + + //! Return the min-mod of two values. + /** + \note minmod(\p a,\p b) is defined to be: + - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. + - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. + **/ + template + inline T minmod(const T& a, const T& b) { + return a*b<=0?0:(a>0?(a + inline T round(const T& x) { + return (T)std::floor((_cimg_Tfloat)x + 0.5f); + } + + template + inline int uiround(const T x) { + return cimg::type::is_float()?(int)(x + 0.5f):(int)x; + } + + //! Return rounded value. + /** + \param x Value to be rounded. + \param y Rounding precision. + \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward). + \return Rounded value, having the same type as input value \c x. + **/ + template + inline T round(const T& x, const double y, const int rounding_type=0) { + if (y<=0) return x; + if (y==1) switch (rounding_type) { + case 0 : return cimg::round(x); + case 1 : return (T)std::ceil((_cimg_Tfloat)x); + default : return (T)std::floor((_cimg_Tfloat)x); + } + const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor; + return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx))); + } + + // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values. + // (contribution by RawTherapee: http://rawtherapee.com/). + template + inline T median(T val0, T val1) { + return (val0 + val1)/2; + } + + template + inline T median(T val0, T val1, T val2) { + return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1))); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4) { + T tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); + val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2); + val1 = tmp; tmp = std::min(val2,val3); + return std::max(val1,tmp); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) { + T tmp = std::min(val0,val5); + val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp; + tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4); + val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5); + val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); + val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp); + tmp = std::min(val1,tmp); val3 = std::max(tmp,val3); + return std::min(val3,val4); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) { + T tmp = std::min(val1,val2); + val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); + val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); + val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); + val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7); + val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2); + val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); + val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); + val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8); + val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6); + val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7); + tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp); + return std::min(val4,val2); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8, T val9, T val10, T val11, + T val12) { + T tmp = std::min(val1,val7); + val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; + tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8); + val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12); + val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp; + tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11); + val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp; + tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2); + val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; + tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4); + val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; + tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12); + tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10); + val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; + tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9); + val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp; + tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3); + tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10); + val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8); + val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp); + val5 = std::max(tmp,val5); val6 = std::min(val6,val7); + return std::max(val5,val6); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, + T val5, T val6, T val7, T val8, T val9, + T val10, T val11, T val12, T val13, T val14, + T val15, T val16, T val17, T val18, T val19, + T val20, T val21, T val22, T val23, T val24) { + T tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); + val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3); + val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; + tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6); + tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10); + val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9); + tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13); + val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12); + tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16); + val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15); + tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19); + val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18); + tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22); + val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21); + tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5); + val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp; + tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3); + tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7); + val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14); + val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14); + val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15); + val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15); + val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16); + val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16); + val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23); + val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23); + val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24); + val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24); + val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22); + val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18); + val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18); + val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp; + tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10); + val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp; + tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11); + tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21); + val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12); + tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22); + val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23); + val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23); + val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24); + val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15); + val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19); + tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp); + val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11); + val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17); + tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12); + val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14); + val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7); + tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14); + tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12); + val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp); + val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp; + val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp; + tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp); + val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp; + tmp = std::min(val10,val20); + return std::max(tmp,val12); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, + T val7, T val8, T val9, T val10, T val11, T val12, T val13, + T val14, T val15, T val16, T val17, T val18, T val19, T val20, + T val21, T val22, T val23, T val24, T val25, T val26, T val27, + T val28, T val29, T val30, T val31, T val32, T val33, T val34, + T val35, T val36, T val37, T val38, T val39, T val40, T val41, + T val42, T val43, T val44, T val45, T val46, T val47, T val48) { + T tmp = std::min(val0,val32); + val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp; + tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35); + val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp; + tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38); + val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp; + tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41); + val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42); + val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp; + tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45); + val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46); + val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp; + tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16); + val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17); + val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19); + val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp; + tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22); + val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp; + tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25); + val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26); + val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp; + tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29); + val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30); + val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp; + tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32); + val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33); + val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp; + tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36); + val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37); + val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp; + tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40); + val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41); + val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp; + tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44); + val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45); + val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp; + tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8); + val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp; + tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11); + val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp; + tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14); + val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp; + tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25); + val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26); + val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp; + tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29); + val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30); + val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp; + tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41); + val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42); + val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp; + tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45); + val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46); + val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp; + tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33); + val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34); + val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp; + tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37); + val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38); + val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp; + tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16); + val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17); + val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp; + tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20); + val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21); + val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp; + tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32); + val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33); + val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp; + tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36); + val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37); + val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp; + tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48); + val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4); + val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6); + val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; + tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13); + val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14); + val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp; + tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21); + val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22); + val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp; + tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29); + val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30); + val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp; + tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37); + val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38); + val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp; + tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45); + val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46); + val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp; + tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33); + val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34); + val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp; + tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41); + val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42); + val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp; + tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16); + val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17); + val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp; + tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24); + val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25); + val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp; + tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32); + val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33); + val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp; + tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40); + val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41); + val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp; + tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48); + val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8); + val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10); + val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp; + tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17); + val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18); + val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp; + tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25); + val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26); + val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp; + tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33); + val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34); + val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp; + tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41); + val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42); + val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp; + tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2); + val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp; + tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7); + val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp; + tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14); + val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15); + val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp; + tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22); + val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23); + val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp; + tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30); + val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31); + val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp; + tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38); + val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39); + val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp; + tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46); + val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47); + val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33); + val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp; + tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40); + val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41); + val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp; + tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48); + val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16); + val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp; + tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21); + val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24); + val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp; + tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29); + val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32); + val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp; + tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37); + val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40); + val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp; + tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45); + val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48); + val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9); + val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; + tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16); + val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17); + val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp; + tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24); + val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25); + val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp; + tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32); + val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33); + val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp; + tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40); + val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41); + val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp; + tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48); + val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); + val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8); + val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp; + tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13); + val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16); + val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp; + tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21); + val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24); + val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp; + tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29); + val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32); + val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp; + tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37); + val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40); + val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp; + tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45); + val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48); + val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5); + val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11); + val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17); + val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23); + val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29); + val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35); + val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41); + val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47); + val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36); + val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42); + val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48); + val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28); + val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34); + val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24); + val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30); + val24 = std::max(val21,val24); val23 = std::min(val23,val26); + return std::max(val23,val24); + } + + //! Return sqrt(x^2 + y^2). + template + inline T hypot(const T x, const T y) { + return std::sqrt(x*x + y*y); + } + + template + inline T hypot(const T x, const T y, const T z) { + return std::sqrt(x*x + y*y + z*z); + } + + template + inline T _hypot(const T x, const T y) { // Slower but more precise version + T nx = cimg::abs(x), ny = cimg::abs(y), t; + if (nx0) { t/=nx; return nx*std::sqrt(1 + t*t); } + return 0; + } + + //! Return the factorial of n + inline double factorial(const int n) { + if (n<0) return cimg::type::nan(); + if (n<2) return 1; + double res = 2; + for (int i = 3; i<=n; ++i) res*=i; + return res; + } + + //! Return the number of permutations of k objects in a set of n objects. + inline double permutations(const int k, const int n, const bool with_order) { + if (n<0 || k<0) return cimg::type::nan(); + if (k>n) return 0; + double res = 1; + for (int i = n; i>=n - k + 1; --i) res*=i; + return with_order?res:res/cimg::factorial(k); + } + + inline double _fibonacci(int exp) { + double + base = (1 + std::sqrt(5.))/2, + result = 1/std::sqrt(5.); + while (exp) { + if (exp&1) result*=base; + exp>>=1; + base*=base; + } + return result; + } + + //! Calculate fibonacci number. + // (Precise up to n = 78, less precise for n>78). + inline double fibonacci(const int n) { + if (n<0) return cimg::type::nan(); + if (n<3) return 1; + if (n<11) { + cimg_uint64 fn1 = 1, fn2 = 1, fn = 0; + for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } + return (double)fn; + } + if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10 + return (double)((cimg_uint64)(_fibonacci(n) + 0.5)); + + if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93 + cimg_uint64 + fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL) + fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL + fn = 0; + for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } + return (double)fn; + } + return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation + } + + //! Calculate greatest common divisor. + inline long gcd(long a, long b) { + while (a) { const long c = a; a = b%a; b = c; } + return b; + } + + //! Convert character to lower case. + inline char lowercase(const char x) { + return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a'); + } + inline double lowercase(const double x) { + return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a'); + } + + //! Convert C-string to lower case. + inline void lowercase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr); + } + + //! Convert character to upper case. + inline char uppercase(const char x) { + return (char)((x<'a'||x>'z')?x:x - 'a' + 'A'); + } + + inline double uppercase(const double x) { + return (double)((x<'a'||x>'z')?x:x - 'a' + 'A'); + } + + //! Convert C-string to upper case. + inline void uppercase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr); + } + + //! Return \c true if input character is blank (space, tab, or non-printable character). + inline bool is_blank(const char c) { + return c>=0 && (unsigned char)c<=' '; + } + + //! Read value in a C-string. + /** + \param str C-string containing the float value to read. + \return Read value. + \note Same as std::atof() extended to manage the retrieval of fractions from C-strings, + as in "1/2". + **/ + inline double atof(const char *const str) { + double x = 0, y = 1; + return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; + } + + //! Compare the first \p l characters of two C-strings, ignoring the case. + /** + \param str1 C-string. + \param str2 C-string. + \param l Number of characters to compare. + \return \c 0 if the two strings are equal, something else otherwise. + \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). + **/ + inline int strncasecmp(const char *const str1, const char *const str2, const int l) { + if (!l) return 0; + if (!str1) return str2?-1:0; + const char *nstr1 = str1, *nstr2 = str2; + int k, diff = 0; for (k = 0; kp && str[q]==delimiter; ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } + return false; + } + + //! Remove white spaces on the start and/or end of a C-string. + inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) { + if (!str) return false; + const int l = (int)std::strlen(str); + int p, q; + if (is_symmetric) for (p = 0, q = l - 1; pp && is_blank(str[q]); ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } + return false; + } + + //! Replace reserved characters (for Windows filename) by another character. + /** + \param[in,out] str C-string to work with (modified at output). + \param[in] c Replacement character. + **/ + inline void strwindows_reserved(char *const str, const char c='_') { + for (char *s = str; *s; ++s) { + const char i = *s; + if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c; + } + } + + //! Replace escape sequences in C-strings by character values. + /** + \param[in,out] str C-string to work with (modified at output). + **/ + inline void strunescape(char *const str) { +#define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break; + + unsigned char val = 0; + for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) { + cimg_strunescape('a','\a'); + cimg_strunescape('b','\b'); + cimg_strunescape('e',0x1B); + cimg_strunescape('f','\f'); + cimg_strunescape('n','\n'); + cimg_strunescape('r','\r'); + cimg_strunescape('t','\t'); + cimg_strunescape('v','\v'); + cimg_strunescape('\\','\\'); + cimg_strunescape('\'','\''); + cimg_strunescape('\"','\"'); + cimg_strunescape('\?','\?'); + case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : + val = (unsigned char)(*(ns++) - '0'); + if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; + if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; + *nd = (char)val; + break; + case 'x' : { + char c = lowercase(*(++ns)); + if ((c>='0' && c<='9') || (c>='a' && c<='f')) { + val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10); + c = lowercase(*(++ns)); + if ((c>='0' && c<='9') || (c>='a' && c<='f')) { + (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10); + ++ns; + } + *nd = (char)val; + } else *nd = c; + } break; + case 'u' : { // UTF-8 BMP + char c1, c2, c3, c4; + if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) && + (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) && + (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) && + (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) { + c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10); + c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10); + c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10); + c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10); + const unsigned int ival = + ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4; + if (ival<=0x007f) *nd = (char)ival; + else if (ival<=0x07ff) { + *(nd++) = (char)((ival>>6)|0xc0); + *nd = (char)((ival&0x3f)|0x80); + } else { + *(nd++) = (char)((ival>>12)|0xe0); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } + ns+=5; + } else *nd = *(ns++); + } break; + case 'U' : { // UTF-8 astral planes + char c1, c2, c3, c4, c5, c6, c7, c8; + if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) && + (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) && + (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) && + (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) && + (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) && + (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) && + (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) && + (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) { + c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10); + c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10); + c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10); + c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10); + c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10); + c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10); + c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10); + c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10); + const unsigned int ival = + ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) | + ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8; + if (ival<=0x007f) *nd = (char)ival; + else if (ival<=0x07ff) { + *(nd++) = (char)((ival>>6)|0xc0); + *nd = (char)((ival&0x3f)|0x80); + } else if (ival<=0xffff) { + *(nd++) = (char)((ival>>12)|0xe0); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } else { + *(nd++) = (char)((ival>>18)|0xf0); + *(nd++) = (char)(((ival>>12)&0x3f)|0x80); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } + ns+=9; + } else *nd = *(ns++); + } break; + default : if (*ns) *nd = *(ns++); + } + else *nd = *(ns++); + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const cimg_ulong size); + + // Return string that identifies the running OS. + inline const char *stros() { +#if defined(linux) || defined(__linux) || defined(__linux__) + static const char *const str = "Linux"; +#elif defined(sun) || defined(__sun) + static const char *const str = "Sun OS"; +#elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) + static const char *const str = "BSD"; +#elif defined(sgi) || defined(__sgi) + static const char *const str = "Irix"; +#elif defined(__MACOSX__) || defined(__APPLE__) + static const char *const str = "Mac OS"; +#elif defined(unix) || defined(__unix) || defined(__unix__) + static const char *const str = "Generic Unix"; +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ + defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + static const char *const str = "Windows"; +#else + const char + *const _str1 = std::getenv("OSTYPE"), + *const _str2 = _str1?_str1:std::getenv("OS"), + *const str = _str2?_str2:"Unknown OS"; +#endif + return str; + } + + //! Return the basename of a filename. + inline const char* basename(const char *const s, const char separator=cimg_file_separator) { + const char *p = 0, *np = s; + while (np>=s && (p=np)) np = std::strchr(np,separator) + 1; + return p; + } + + // Return a random filename. + inline const char* filenamerand() { + cimg::mutex(6); + static char randomid[9]; + for (unsigned int k = 0; k<8; ++k) { + const int v = (int)cimg::rand(65535)%3; + randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)): + (v==1?('a' + ((int)cimg::rand(65535)%26)): + ('A' + ((int)cimg::rand(65535)%26)))); + } + cimg::mutex(6,0); + return randomid; + } + + // Convert filename as a Windows-style filename (short path name). + inline void winformat_string(char *const str) { + if (str && *str) { +#if cimg_OS==2 + char *const nstr = new char[MAX_PATH]; + if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr); + delete[] nstr; +#endif + } + } + + // Open a file (similar to std:: fopen(), but with wide character support on Windows). + inline std::FILE *std_fopen(const char *const path, const char *const mode); + + + //! Open a file. + /** + \param path Path of the filename to open. + \param mode C-string describing the opening mode. + \return Opened file. + \note Same as std::fopen() but throw a \c CImgIOException when + the specified file cannot be opened, instead of returning \c 0. + **/ + inline std::FILE *fopen(const char *const path, const char *const mode) { + if (!path) + throw CImgArgumentException("cimg::fopen(): Specified file path is (null)."); + if (!mode) + throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).", + path); + std::FILE *res = 0; + if (*path=='-' && (!path[1] || path[1]=='.')) { + res = (*mode=='r')?cimg::_stdin():cimg::_stdout(); +#if cimg_OS==2 + if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode +#ifdef __BORLANDC__ + if (setmode(_fileno(res),0x8000)==-1) res = 0; +#else + if (_setmode(_fileno(res),0x8000)==-1) res = 0; +#endif + } +#endif + } else res = cimg::std_fopen(path,mode); + if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.", + path,mode); + return res; + } + + //! Close a file. + /** + \param file File to close. + \return \c 0 if file has been closed properly, something else otherwise. + \note Same as std::fclose() but display a warning message if + the file has not been closed properly. + **/ + inline int fclose(std::FILE *file) { + if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; } + if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0; + const int errn = std::fclose(file); + if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.", + errn); + return errn; + } + + //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows). + inline int fseek(FILE *stream, cimg_long offset, int origin) { +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + return _fseeki64(stream,(__int64)offset,origin); +#else + return std::fseek(stream,offset,origin); +#endif + } + + //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows). + inline cimg_long ftell(FILE *stream) { +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + return (cimg_long)_ftelli64(stream); +#else + return (cimg_long)std::ftell(stream); +#endif + } + + // Get the file or directory attributes with support for UTF-8 paths (Windows only). +#if cimg_OS==2 + inline DWORD win_getfileattributes(const char *const path); +#endif + + //! Check if a path is a directory. + /** + \param path Specified path to test. + **/ + inline bool is_directory(const char *const path) { + if (!path || !*path) return false; +#if cimg_OS==1 + struct stat st_buf; + return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)); +#elif cimg_OS==2 + const DWORD res = win_getfileattributes(path); + return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY); +#else + return false; +#endif + } + + //! Check if a path is a file. + /** + \param path Specified path to test. + **/ + inline bool is_file(const char *const path) { + if (!path || !*path) return false; +#if cimg_OS==2 + const DWORD res = cimg::win_getfileattributes(path); + return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY); +#else + std::FILE *const file = cimg::std_fopen(path,"rb"); + if (!file) return false; + cimg::fclose(file); + return !is_directory(path); +#endif + } + + //! Get file size. + /** + \param filename Specified filename to get size from. + \return File size or '-1' if file does not exist. + **/ + inline cimg_int64 fsize(const char *const filename) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + if (!file) return (cimg_int64)-1; + std::fseek(file,0,SEEK_END); + const cimg_int64 siz = (cimg_int64)std::ftell(file); + cimg::fclose(file); + return siz; + } + + //! Get last write time of a given file or directory (multiple-attributes version). + /** + \param path Specified path to get attributes from. + \param[in,out] attr Type of requested time attributes. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } + Replaced by read attributes after return (or -1 if an error occurred). + \param nb_attr Number of attributes to read/write. + \return Latest read attribute. + **/ + template + inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) { +#define _cimg_fdate_err() for (unsigned int i = 0; i + inline int date(T *attr, const unsigned int nb_attr) { + int res = -1; + cimg::mutex(6); +#if cimg_OS==2 + SYSTEMTIME st; + GetLocalTime(&st); + for (unsigned int i = 0; itm_year + 1900: + attr[i]==1?st->tm_mon + 1: + attr[i]==2?st->tm_mday: + attr[i]==3?st->tm_wday: + attr[i]==4?st->tm_hour: + attr[i]==5?st->tm_min: + attr[i]==6?st->tm_sec: + attr[i]==7?_st.tv_usec/1000:-1); + attr[i] = (T)res; + } +#endif + cimg::mutex(6,0); + return res; + } + + //! Get current local time (single-attribute version). + /** + \param attr Type of requested time attribute. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second | + 7=millisecond } + \return Specified attribute or -1 if an error occurred. + **/ + inline int date(unsigned int attr) { + int out = (int)attr; + return date(&out,1); + } + + // Get/set path to the \c curl binary. + inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c dcraw binary. + inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the custom's \c custom binary. + inline const char *custom_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the GraphicsMagick's \c gm binary. + inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c gunzip binary. + inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c gzip binary. + inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the ImageMagick's \c convert binary. + inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the Medcon's \c medcon binary. + inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to store temporary files. + inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c wget binary. + inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false); + + //! Split filename into two C-strings \c body and \c extension. + /** + filename and body must not overlap! + **/ + inline const char *split_filename(const char *const filename, char *const body=0) { + if (!filename) { if (body) *body = 0; return ""; } + const char * p = std::strrchr(filename,'.'); + if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension. + if (body) std::strcpy(body,filename); + return filename + std::strlen(filename); + } + const unsigned int l = (unsigned int)(p - filename); + if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; } + return p + 1; + } + + // Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, + const unsigned int digits, char *const str); + + //! Read data from file. + /** + \param[out] ptr Pointer to memory buffer that will contain the binary data read from file. + \param nmemb Number of elements to read. + \param stream File to read data from. + \return Number of read elements. + \note Same as std::fread() but may display warning message if all elements could not be read. + **/ + template + inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",stream,ptr); + if (!nmemb) return 0; + const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0; + do { + l_to_read = (to_read*sizeof(T))0); + if (to_read>0) + warn("cimg::fread(): Only %lu/%lu elements could be read from file.", + (unsigned long)al_read,(unsigned long)nmemb); + return al_read; + } + + //! Write data to file. + /** + \param ptr Pointer to memory buffer containing the binary data to write on file. + \param nmemb Number of elements to write. + \param[out] stream File to write data on. + \return Number of written elements. + \note Similar to std::fwrite but may display warning messages if all elements could not be written. + **/ + template + inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",ptr,stream); + if (!nmemb) return 0; + const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0; + do { + l_to_write = (to_write*sizeof(T))0); + if (to_write>0) + warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.", + (unsigned long)al_write,(unsigned long)nmemb); + return al_write; + } + + //! Create an empty file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + **/ + inline void fempty(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::fempty(): Specified filename is (null)."); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!file) cimg::fclose(nfile); + } + + // Try to guess format from an image file. + inline const char *ftype(std::FILE *const file, const char *const filename); + + // Get or set load from network mode (can be { 0=disabled | 1=enabled }). + inline bool& network_mode(const bool value, const bool is_set) { + static bool mode = true; + if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); } + return mode; + } + + inline bool& network_mode() { + return network_mode(false,false); + } + + // Load file from network as a local temporary file. + inline char *load_network(const char *const url, char *const filename_local, + const unsigned int timeout=0, const bool try_fallback=false, + const char *const referer=0); + + //! Return options specified on the command line. + inline const char* option(const char *const name, const int argc, const char *const *const argv, + const char *const _default, const char *const usage, const bool reset_static) { + static bool first = true, visu = false; + if (reset_static) { first = true; return 0; } + const char *res = 0; + if (first) { + first = false; + visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0; + } + if (!name && visu) { + if (usage) { + std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); + std::fprintf(cimg::output(),": %s",usage); + std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time); + } + if (_default) std::fprintf(cimg::output(),"%s\n",_default); + } + if (name) { + if (argc>0) { + int k = 0; + while (k Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n", + cimg::t_bold, + cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"), + cimg::t_normal,cimg::t_green, + cimg_OS, + cimg::t_normal); + + std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n", + cimg::t_bold, + cimg::endianness()?"Big":"Little", + cimg::t_normal); + + std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n", + cimg::t_bold, + cimg_verbosity==0?"Quiet": + cimg_verbosity==1?"Console": + cimg_verbosity==2?"Dialog": + cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings", + cimg::t_normal,cimg::t_green, + cimg_verbosity, + cimg::t_normal); + + std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n", + cimg::t_bold, +#ifdef cimg_strict_warnings + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n", + cimg::t_bold, + cimg_use_cpp11?"Yes":"No", + cimg::t_normal,cimg::t_green, + (int)cimg_use_cpp11, + cimg::t_normal); + + std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_vt100 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n", + cimg::t_bold, + cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown", + cimg::t_normal,cimg::t_green, + (int)cimg_display, + cimg::t_normal); + +#if cimg_display==1 + std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xshm + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xrandr + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); +#endif + std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n", + cimg::t_bold, +#if cimg_use_openmp!=0 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_png + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_jpeg + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_tiff + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_magick + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_fftw3 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_lapack + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + char *const tmp = new char[1024]; + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path()); + std::fprintf(cimg::output()," > Path of 'curl': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path()); + std::fprintf(cimg::output()," > Path of 'dcraw': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::custom_path()); + std::fprintf(cimg::output()," > Path of 'custom': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path()); + std::fprintf(cimg::output()," > Path of 'graphicsmagick': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path()); + std::fprintf(cimg::output()," > Path of 'gunzip': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path()); + std::fprintf(cimg::output()," > Path of 'gzip': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path()); + std::fprintf(cimg::output()," > Path of 'imagemagick': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path()); + std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path()); + std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path()); + std::fprintf(cimg::output()," > Path of 'wget': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + std::fprintf(cimg::output(),"\n"); + delete[] tmp; + } + + // Declare LAPACK function signatures if LAPACK support is enabled. +#ifdef cimg_use_lapack + template + inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { + dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { + sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + template + inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { + dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { + sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + template + inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, + T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { + dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, + float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { + sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + template + inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { + int one = 1; + dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { + int one = 1; + sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + template + inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { + dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { + ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + template + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA, + T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) { + dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA, + float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) { + sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + +#endif + + } // namespace cimg { ... + + /*------------------------------------------------ + # + # + # Definition of mathematical operators and + # external functions. + # + # + -------------------------------------------------*/ + +#define _cimg_create_operator(typ) \ + template \ + inline CImg::type> operator+(const typ val, const CImg& img) { \ + return img + val; \ + } \ + template \ + inline CImg::type> operator-(const typ val, const CImg& img) { \ + typedef typename cimg::superset::type Tt; \ + return CImg(img._width,img._height,img._depth,img._spectrum,val)-=img; \ + } \ + template \ + inline CImg::type> operator*(const typ val, const CImg& img) { \ + return img*val; \ + } \ + template \ + inline CImg::type> operator/(const typ val, const CImg& img) { \ + return val*img.get_invert(); \ + } \ + template \ + inline CImg::type> operator&(const typ val, const CImg& img) { \ + return img & val; \ + } \ + template \ + inline CImg::type> operator|(const typ val, const CImg& img) { \ + return img | val; \ + } \ + template \ + inline CImg::type> operator^(const typ val, const CImg& img) { \ + return img ^ val; \ + } \ + template \ + inline bool operator==(const typ val, const CImg& img) { \ + return img == val; \ + } \ + template \ + inline bool operator!=(const typ val, const CImg& img) { \ + return img != val; \ + } + + _cimg_create_operator(bool) + _cimg_create_operator(unsigned char) + _cimg_create_operator(char) + _cimg_create_operator(signed char) + _cimg_create_operator(unsigned short) + _cimg_create_operator(short) + _cimg_create_operator(unsigned int) + _cimg_create_operator(int) + _cimg_create_operator(cimg_uint64) + _cimg_create_operator(cimg_int64) + _cimg_create_operator(float) + _cimg_create_operator(double) + _cimg_create_operator(long double) + + template + inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg& img) { + return img + expression; + } + + template + inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg& img) { + return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img; + } + + template + inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg& img) { + return img*expression; + } + + template + inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg& img) { + return expression*img.get_invert(); + } + + template + inline CImg operator&(const char *const expression, const CImg& img) { + return img & expression; + } + + template + inline CImg operator|(const char *const expression, const CImg& img) { + return img | expression; + } + + template + inline CImg operator^(const char *const expression, const CImg& img) { + return img ^ expression; + } + + template + inline bool operator==(const char *const expression, const CImg& img) { + return img==expression; + } + + template + inline bool operator!=(const char *const expression, const CImg& img) { + return img!=expression; + } + + template + inline CImg transpose(const CImg& instance) { + return instance.get_transpose(); + } + + template + inline CImg<_cimg_Tfloat> invert(const CImg& instance, const bool use_LU=true) { + return instance.get_invert(use_LU); + } + + template + inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance, const bool use_LU=false) { + return instance.get_pseudoinvert(use_LU); + } + +#define _cimg_create_pointwise_function(name) \ + template \ + inline CImg<_cimg_Tfloat> name(const CImg& instance) { \ + return instance.get_##name(); \ + } + + _cimg_create_pointwise_function(sqr) + _cimg_create_pointwise_function(sqrt) + _cimg_create_pointwise_function(exp) + _cimg_create_pointwise_function(log) + _cimg_create_pointwise_function(log2) + _cimg_create_pointwise_function(log10) + _cimg_create_pointwise_function(abs) + _cimg_create_pointwise_function(sign) + _cimg_create_pointwise_function(cos) + _cimg_create_pointwise_function(sin) + _cimg_create_pointwise_function(sinc) + _cimg_create_pointwise_function(tan) + _cimg_create_pointwise_function(acos) + _cimg_create_pointwise_function(asin) + _cimg_create_pointwise_function(atan) + _cimg_create_pointwise_function(cosh) + _cimg_create_pointwise_function(sinh) + _cimg_create_pointwise_function(tanh) + _cimg_create_pointwise_function(acosh) + _cimg_create_pointwise_function(asinh) + _cimg_create_pointwise_function(atanh) + + /*----------------------------------- + # + # Define the CImgDisplay structure + # + ----------------------------------*/ + //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events). + /** + CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window + (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems). + If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter + a minimal mode where warning messages will be outputted each time the program is trying to call one of the + CImgDisplay method. + + The configuration variable \c cimg_display tells about the graphic library used. + It is set automatically by \CImg when one of these graphic libraries has been detected. + But, you can override its value if necessary. Valid choices are: + - 0: Disable display capabilities. + - 1: Use \b X-Window (X11) library. + - 2: Use \b GDI32 library. + + Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay. + **/ + struct CImgDisplay { + cimg_uint64 _timer, _fps_frames, _fps_timer; + unsigned int _width, _height, _normalization; + float _fps_fps, _min, _max; + bool _is_fullscreen; + char *_title; + unsigned int _window_width, _window_height, _button, *_keys, *_released_keys; + int _window_x, _window_y, _mouse_x, _mouse_y, _wheel; + bool _is_closed, _is_resized, _is_moved, _is_event, + _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7, + _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2, + _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0, + _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE, + _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE, + _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG, + _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX, + _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT, + _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT, + _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3, + _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB, + _is_keyPADMUL, _is_keyPADDIV; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- + +#ifdef cimgdisplay_plugin +#include cimgdisplay_plugin +#endif +#ifdef cimgdisplay_plugin1 +#include cimgdisplay_plugin1 +#endif +#ifdef cimgdisplay_plugin2 +#include cimgdisplay_plugin2 +#endif +#ifdef cimgdisplay_plugin3 +#include cimgdisplay_plugin3 +#endif +#ifdef cimgdisplay_plugin4 +#include cimgdisplay_plugin4 +#endif +#ifdef cimgdisplay_plugin5 +#include cimgdisplay_plugin5 +#endif +#ifdef cimgdisplay_plugin6 +#include cimgdisplay_plugin6 +#endif +#ifdef cimgdisplay_plugin7 +#include cimgdisplay_plugin7 +#endif +#ifdef cimgdisplay_plugin8 +#include cimgdisplay_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + \note If the associated window is visible on the screen, it is closed by the call to the destructor. + **/ + ~CImgDisplay() { + assign(); + delete[] _keys; + delete[] _released_keys; + } + + //! Construct an empty display. + /** + \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until + display of valid data is performed. + \par Example + \code + CImgDisplay disp; // Does actually nothing + ... + disp.display(img); // Construct new window and display image in it + \endcode + **/ + CImgDisplay(): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(); + } + + //! Construct a display with specified dimensions. + /** \param width Window width. + \param height Window height. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note A black background is initially displayed on the associated window. + **/ + CImgDisplay(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(width,height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image. + /** \param img Image used as a model to create the window. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note The pixels of the input image are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(img,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list. + /** \param list The images list to display. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note All images of the list, appended along the X-axis, are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(list,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of an existing one. + /** + \param disp Display instance to copy. + \note The pixel buffer of the input window is initially displayed on the associated window. + **/ + CImgDisplay(const CImgDisplay& disp): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(disp); + } + + //! Take a screenshot. + /** + \param[out] img Output screenshot. Can be empty on input + **/ + template + static void screenshot(CImg& img) { + return screenshot(0,0,cimg::type::max(),cimg::type::max(),img); + } + +#if cimg_display==0 + + static void _no_display_exception() { + throw CImgDisplayException("CImgDisplay(): No display available."); + } + + //! Destructor - Empty constructor \inplace. + /** + \note Replace the current instance by an empty display. + **/ + CImgDisplay& assign() { + return flush(); + } + + //! Construct a display with specified dimensions \inplace. + /** + **/ + CImgDisplay& assign(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + cimg::unused(width,height,title,normalization,is_fullscreen,is_closed); + _no_display_exception(); + return assign(); + } + + //! Construct a display from an image \inplace. + /** + **/ + template + CImgDisplay& assign(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list \inplace. + /** + **/ + template + CImgDisplay& assign(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of another one \inplace. + /** + **/ + CImgDisplay& assign(const CImgDisplay &disp) { + _no_display_exception(); + return assign(disp._width,disp._height); + } + +#endif + + //! Return a reference to an empty display. + /** + \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&) + must have a default value. + \par Example + \code + void foo(CImgDisplay& disp=CImgDisplay::empty()); + \endcode + **/ + static CImgDisplay& empty() { + static CImgDisplay _empty; + return _empty.assign(); + } + + //! Return a reference to an empty display \const. + static const CImgDisplay& const_empty() { + static const CImgDisplay _empty; + return _empty; + } + +#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \ + CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true) + static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz, + const int dmin, const int dmax, const bool return_y) { + const int + u = CImgDisplay::screen_width(), + v = CImgDisplay::screen_height(); + const float + mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin, + mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin, + Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax, + Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax; + float + w = (float)std::max(1U,dx), + h = (float)std::max(1U,dy); + if (dz>1) { w+=dz; h+=dz; } + if (wMw) { h = h*Mw/w; w = Mw; } + if (h>Mh) { w = w*Mh/h; h = Mh; } + if (wdisp = img is equivalent to disp.display(img). + **/ + template + CImgDisplay& operator=(const CImg& img) { + return display(img); + } + + //! Display list of images on associated window. + /** + \note disp = list is equivalent to disp.display(list). + **/ + template + CImgDisplay& operator=(const CImgList& list) { + return display(list); + } + + //! Construct a display as a copy of another one \inplace. + /** + \note Equivalent to assign(const CImgDisplay&). + **/ + CImgDisplay& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return \c false if display is empty, \c true otherwise. + /** + \note if (disp) { ... } is equivalent to if (!disp.is_empty()) { ... }. + **/ + operator bool() const { + return !is_empty(); + } + + //@} + //------------------------------------------ + // + //! \name Instance Checking + //@{ + //------------------------------------------ + + //! Return \c true if display is empty, \c false otherwise. + /** + **/ + bool is_empty() const { + return !(_width && _height); + } + + //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise. + /** + \note + - When a user physically closes the associated window, the display is set to closed. + - A closed display is not destroyed. Its associated window can be show again on the screen using show(). + **/ + bool is_closed() const { + return _is_closed; + } + + //! Return \c true if associated window has been resized on the screen, \c false otherwise. + /** + **/ + bool is_resized() const { + return _is_resized; + } + + //! Return \c true if associated window has been moved on the screen, \c false otherwise. + /** + **/ + bool is_moved() const { + return _is_moved; + } + + //! Return \c true if any event has occurred on the associated window, \c false otherwise. + /** + **/ + bool is_event() const { + return _is_event; + } + + //! Return \c true if current display is in fullscreen mode, \c false otherwise. + /** + **/ + bool is_fullscreen() const { + return _is_fullscreen; + } + + //! Return \c true if any key is being pressed on the associated window, \c false otherwise. + /** + \note The methods below do the same only for specific keys. + **/ + bool is_key() const { + return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 || + _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 || + _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 || + _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 || + _is_key3 || _is_key4 || _is_key5 || _is_key6 || + _is_key7 || _is_key8 || _is_key9 || _is_key0 || + _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME || + _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW || + _is_keyE || _is_keyR || _is_keyT || _is_keyY || + _is_keyU || _is_keyI || _is_keyO || _is_keyP || + _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN || + _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD || + _is_keyF || _is_keyG || _is_keyH || _is_keyJ || + _is_keyK || _is_keyL || _is_keyENTER || + _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC || + _is_keyV || _is_keyB || _is_keyN || _is_keyM || + _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT || + _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR || + _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT || + _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT || + _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 || + _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 || + _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 || + _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB || + _is_keyPADMUL || _is_keyPADDIV; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode Keycode to test. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())' + disp.wait(); + } + \endcode + **/ + bool is_key(const unsigned int keycode) const { +#define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k; + _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3); + _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7); + _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11); + _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2); + _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6); + _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0); + _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME); + _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W); + _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y); + _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P); + _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN); + _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D); + _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J); + _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER); + _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C); + _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M); + _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT); + _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR); + _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT); + _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT); + _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2); + _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5); + _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8); + _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB); + _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV); + return false; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode C-string containing the keycode label of the key to test. + \note Use it when the key you want to test can be dynamically set by the user. + \par Example + \code + CImgDisplay disp(400,400); + const char *const keycode = "TAB"; + while (!disp.is_closed()) { + if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())' + disp.wait(); + } + \endcode + **/ + bool& is_key(const char *const keycode) { + static bool f = false; + f = false; +#define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k; + _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3); + _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7); + _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11); + _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2); + _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6); + _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0); + _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME); + _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W); + _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y); + _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P); + _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN); + _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D); + _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J); + _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER); + _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C); + _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M); + _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT); + _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR); + _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT); + _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT); + _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2); + _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5); + _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8); + _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB); + _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV); + return f; + } + + //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise. + /** + \param keycodes_sequence Buffer of keycodes to test. + \param length Number of keys in the \c keycodes_sequence buffer. + \param remove_sequence Tells if the key sequence must be removed from the key history, if found. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD }; + while (!disp.is_closed()) { + if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event + disp.wait(); + } + \endcode + **/ + bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, + const bool remove_sequence=false) { + if (keycodes_sequence && length) { + const unsigned int + *const ps_end = keycodes_sequence + length - 1, + *const pk_end = (unsigned int*)_keys + 1 + 128 - length, + k = *ps_end; + for (unsigned int *pk = (unsigned int*)_keys; pk[0,255]. + If the range of values of the data to display is different, a normalization may be required for displaying + the data in a correct way. The normalization type can be one of: + - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the + CImgDisplay instance have values in range [0,255]. + - \c 1: Value normalization is always performed (this is the default behavior). + Before displaying an input image, its values will be (virtually) stretched + in range [0,255], so that the contrast of the displayed pixels will be maximum. + Use this mode for images whose minimum and maximum values are not prescribed to known values + (e.g. float-valued images). + Note that when normalized versions of images are computed for display purposes, the actual values of these + images are not modified. + - \c 2: Value normalization is performed once (on the first image display), then the same normalization + coefficients are kept for next displayed frames. + - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types, + the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then + for unsigned char). + For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image + data instead. + **/ + unsigned int normalization() const { + return _normalization; + } + + //! Return title of the associated window as a C-string. + /** + \note Window title may be not visible, depending on the used window manager or if the current display is + in fullscreen mode. + **/ + const char *title() const { + return _title?_title:""; + } + + //! Return width of the associated window. + /** + \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual width of the associated window. + **/ + int window_width() const { + return (int)_window_width; + } + + //! Return height of the associated window. + /** + \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual height of the associated window. + **/ + int window_height() const { + return (int)_window_height; + } + + //! Return X-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_x() const { + return _window_x; + } + + //! Return Y-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_y() const { + return _window_y; + } + + //! Return X-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,width()-1]. + **/ + int mouse_x() const { + return _mouse_x; + } + + //! Return Y-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,height()-1]. + **/ + int mouse_y() const { + return _mouse_y; + } + + //! Return current state of the mouse buttons. + /** + \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned + value is set: + - bit \c 0 (value \c 0x1): State of the left mouse button. + - bit \c 1 (value \c 0x2): State of the right mouse button. + - bit \c 2 (value \c 0x4): State of the middle mouse button. + + Several bits can be activated if more than one button are pressed at the same time. + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.button()&1) { // Left button clicked + ... + } + if (disp.button()&2) { // Right button clicked + ... + } + if (disp.button()&4) { // Middle button clicked + ... + } + disp.wait(); + } + \endcode + **/ + unsigned int button() const { + return _button; + } + + //! Return current state of the mouse wheel. + /** + \note + - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled + forward or backward. + - Scrolling the wheel forward add \c 1 to the wheel value. + - Scrolling the wheel backward subtract \c 1 to the wheel value. + - The returned value cumulates the number of forward of backward scrolls since the creation of the display, + or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset + the wheel counter when an action has been performed regarding the current wheel value. + Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done + (as many in forward as in backward directions). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.wheel()) { + int counter = disp.wheel(); // Read the state of the mouse wheel + ... // Do what you want with 'counter' + disp.set_wheel(); // Reset the wheel value to 0 + } + disp.wait(); + } + \endcode + **/ + int wheel() const { + return _wheel; + } + + //! Return one entry from the pressed keys history. + /** + \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry). + \return Keycode of a pressed key or \c 0 for a released key. + \note + - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed, + its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead. + This means that up to the 64 last pressed keys may be read from the pressed keys history. + When a new value is stored, the pressed keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int& key(const unsigned int pos=0) const { + static unsigned int key0; + return pos<128?_keys[pos]:(key0 = 0); + + } + + //! Return one entry from the released keys history. + /** + \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry). + \return Keycode of a released key or \c 0 for a pressed key. + \note + - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released, + its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead. + This means that up to the 64 last released keys may be read from the released keys history. + When a new value is stored, the released keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int& released_key(const unsigned int pos=0) const { + static unsigned int key0; + return pos<128?_released_keys[pos]:(key0 = 0); + } + + //! Return keycode corresponding to the specified string. + /** + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB + \endcode + **/ + static unsigned int keycode(const char *const keycode) { +#define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k; + _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3); + _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7); + _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11); + _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2); + _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6); + _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0); + _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME); + _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W); + _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y); + _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P); + _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN); + _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D); + _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J); + _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER); + _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C); + _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M); + _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT); + _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR); + _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT); + _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT); + _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2); + _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5); + _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8); + _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB); + _cimg_keycode(PADMUL); _cimg_keycode(PADDIV); + return 0; + } + + //! Return the current refresh rate, in frames per second. + /** + \note Returns a significant value when the current instance is used to display successive frames. + It measures the delay between successive calls to frames_per_second(). + **/ + float frames_per_second() { + if (!_fps_timer) _fps_timer = cimg::time(); + const float delta = (float)((cimg::time() - _fps_timer)/1000.f); + ++_fps_frames; + if (delta>=1) { + _fps_fps = _fps_frames/delta; + _fps_frames = 0; + _fps_timer = cimg::time(); + } + return _fps_fps; + } + + // Move current display window so that its content stays inside the current screen. + CImgDisplay& move_inside_screen() { + if (is_empty()) return *this; + const int + x0 = window_x(), + y0 = window_y(), + x1 = x0 + window_width() - 1, + y1 = y0 + window_height() - 1, + sw = CImgDisplay::screen_width(), + sh = CImgDisplay::screen_height(); + if (x0<0 || y0<0 || x1>=sw || y1>=sh) + move(std::max(0,std::min(x0,sw - x1 + x0)), + std::max(0,std::min(y0,sh - y1 + y0))); + return *this; + } + + //@} + //--------------------------------------- + // + //! \name Window Manipulation + //@{ + //--------------------------------------- + +#if cimg_display==0 + + //! Display image on associated window. + /** + \param img Input image to display. + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImg& img) { + return assign(img); + } + +#endif + + //! Display list of images on associated window. + /** + \param list List of images to display. + \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c). + \param align Relative position of aligned images when displaying lists with images of different sizes + (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right). + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImgList& list, const char axis='x', const float align=0) { + if (list._width==1) { + const CImg& img = list[0]; + if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img); + } + CImgList::ucharT> visu(list._width); + unsigned int dims = 0; + cimglist_for(list,l) { + const CImg& img = list._data[l]; + img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2).move_to(visu[l]); + dims = std::max(dims,visu[l]._spectrum); + } + cimglist_for(list,l) if (visu[l]._spectrumimg.width() become equal, as well as height() and + img.height(). + - The associated window is also resized to specified dimensions. + **/ + template + CImgDisplay& resize(const CImg& img, const bool force_redraw=true) { + return resize(img._width,img._height,force_redraw); + } + + //! Resize display to the size of another CImgDisplay instance. + /** + \param disp Input display to take size from. + \param force_redraw Tells if the previous window content must be resized and updated as well. + \note + - Calling this method ensures that width() and disp.width() become equal, as well as height() and + disp.height(). + - The associated window is also resized to specified dimensions. + **/ + CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) { + return resize(disp.width(),disp.height(),force_redraw); + } + + // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs). + template + static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, + t *ptrd, const unsigned int wd, const unsigned int hd) { + typedef typename cimg::last::type ulongT; + const ulongT one = (ulongT)1; + CImg off_x(wd), off_y(hd + 1); + if (wd==ws) off_x.fill(1); + else { + ulongT *poff_x = off_x._data, curr = 0; + for (unsigned int x = 0; xstd::printf(). + \warning As the first argument is a format string, it is highly recommended to write + \code + disp.set_title("%s",window_title); + \endcode + instead of + \code + disp.set_title(window_title); + \endcode + if \c window_title can be arbitrary, to prevent nasty memory access. + **/ + CImgDisplay& set_title(const char *const format, ...) { + return assign(0,0,format); + } + +#endif + + //! Enable or disable fullscreen mode. + /** + \param is_fullscreen Tells is the fullscreen mode must be activated or not. + \param force_redraw Tells if the previous window content must be displayed as well. + \note + - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the + current display is not modified. + - The screen resolution may be switched to fit the associated window size and ensure it appears the largest + as possible. + For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen + resolution change (requires the X11 extensions to be enabled). + **/ + CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) { + if (is_empty() || _is_fullscreen==is_fullscreen) return *this; + return toggle_fullscreen(force_redraw); + } + +#if cimg_display==0 + + //! Toggle fullscreen mode. + /** + \param force_redraw Tells if the previous window content must be displayed as well. + \note Enable fullscreen mode if it was not enabled, and disable it otherwise. + **/ + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + return assign(_width,_height,0,3,force_redraw); + } + + //! Show mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& show_mouse() { + return assign(); + } + + //! Hide mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& hide_mouse() { + return assign(); + } + + //! Move mouse pointer to a specified location. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& set_mouse(const int pos_x, const int pos_y) { + return assign(pos_x,pos_y); + } + +#endif + + //! Simulate a mouse button release event. + /** + \note All mouse buttons are considered released at the same time. + **/ + CImgDisplay& set_button() { + _button = 0; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a mouse button press or release event. + /** + \param button Buttons event code, where each button is associated to a single bit. + \param is_pressed Tells if the mouse button is considered as pressed or released. + **/ + CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) { + const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U; + if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode; + _is_event = buttoncode?true:false; + if (buttoncode) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all mouse wheel events. + /** + \note Make wheel() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_wheel() { + _wheel = 0; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a wheel event. + /** + \param amplitude Amplitude of the wheel scrolling to simulate. + \note Make wheel() to return \c amplitude, if called afterwards. + **/ + CImgDisplay& set_wheel(const int amplitude) { + _wheel+=amplitude; + _is_event = amplitude?true:false; + if (amplitude) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all key events. + /** + \note Make key() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_key() { + std::memset((void*)_keys,0,128*sizeof(unsigned int)); + std::memset((void*)_released_keys,0,128*sizeof(unsigned int)); + _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = + _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = + _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = + _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = + _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = + _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL = + _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN = + _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = + _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = + _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = + _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL = + _is_keyPADDIV = false; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a keyboard press/release event. + /** + \param keycode Keycode of the associated key. + \param is_pressed Tells if the key is considered as pressed or released. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) { +#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed; + _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3); + _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7); + _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11); + _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2); + _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6); + _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0); + _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME); + _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W); + _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y); + _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P); + _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN); + _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D); + _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J); + _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER); + _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C); + _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M); + _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT); + _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR); + _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT); + _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT); + _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2); + _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5); + _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8); + _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB); + _cimg_set_key(PADMUL); _cimg_set_key(PADDIV); + if (is_pressed) { + if (*_keys) + std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); + *_keys = keycode; + if (*_released_keys) { + std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); + *_released_keys = 0; + } + } else { + if (*_keys) { + std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); + *_keys = 0; + } + if (*_released_keys) + std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); + *_released_keys = keycode; + } + _is_event = keycode?true:false; + if (keycode) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all display events. + /** + \note Remove all passed events from the current display. + **/ + CImgDisplay& flush() { + set_key().set_button().set_wheel(); + _is_resized = _is_moved = _is_event = false; + _fps_timer = _fps_frames = _timer = 0; + _fps_fps = 0; + return *this; + } + + //! Wait for any user event occurring on the current display. + CImgDisplay& wait() { + wait(*this); + return *this; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \note Similar to cimg::wait(). + **/ + CImgDisplay& wait(const unsigned int milliseconds) { + cimg::wait(milliseconds,&_timer); + return *this; + } + + //! Wait for any event occurring on the display \c disp1. + static void wait(CImgDisplay& disp1) { + disp1._is_event = false; + while (!disp1._is_closed && !disp1._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1 or \c disp2. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { + disp1._is_event = disp2._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed) && + !disp1._is_event && !disp2._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { + disp1._is_event = disp2._is_event = disp3._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, + CImgDisplay& disp5) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) + wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, + CImgDisplay& disp10) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) + wait_all(); + } + +#if cimg_display==0 + + //! Wait for any window event occurring in any opened CImgDisplay. + static void wait_all() { + return _no_display_exception(); + } + + //! Render image into internal display buffer. + /** + \param img Input image data to render. + \note + - Convert image data representation into the internal display buffer (architecture-dependent structure). + - The content of the associated window is not modified, until paint() is called. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + template + CImgDisplay& render(const CImg& img) { + return assign(img); + } + + //! Paint internal display buffer on associated window. + /** + \note + - Update the content of the associated window with the internal display buffer, e.g. after a render() call. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + CImgDisplay& paint() { + return assign(); + } + + + //! Take a snapshot of the current screen content. + /** + \param x0 X-coordinate of the upper left corner. + \param y0 Y-coordinate of the upper left corner. + \param x1 X-coordinate of the lower right corner. + \param y1 Y-coordinate of the lower right corner. + \param[out] img Output screenshot. Can be empty on input + **/ + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + cimg::unused(x0,y0,x1,y1,&img); + _no_display_exception(); + } + + //! Take a snapshot of the associated window content. + /** + \param[out] img Output snapshot. Can be empty on input. + **/ + template + const CImgDisplay& snapshot(CImg& img) const { + cimg::unused(img); + _no_display_exception(); + return *this; + } +#endif + + // X11-based implementation + //-------------------------- +#if cimg_display==1 + + Atom _wm_window_atom, _wm_protocol_atom; + Window _window, _background_window; + Colormap _colormap; + XImage *_image; + void *_data; + +#ifdef cimg_use_xshm + XShmSegmentInfo *_shminfo; +#endif + + static int screen_width() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display."); + res = DisplayWidth(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width; + else res = DisplayWidth(dpy,DefaultScreen(dpy)); +#else + res = DisplayWidth(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static int screen_height() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display."); + res = DisplayHeight(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height; + else res = DisplayHeight(dpy,DefaultScreen(dpy)); +#else + res = DisplayHeight(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static void wait_all() { + if (!cimg::X11_attr().display) return; + pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex); + pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex); + pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex); + } + + void _handle_events(const XEvent *const pevent) { + Display *const dpy = cimg::X11_attr().display; + XEvent event = *pevent; + switch (event.type) { + case ClientMessage : { + if ((int)event.xclient.message_type==(int)_wm_protocol_atom && + (int)event.xclient.data.l[0]==(int)_wm_window_atom) { + XUnmapWindow(cimg::X11_attr().display,_window); + _is_closed = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + } break; + case ConfigureNotify : { + while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {} + const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height; + const int nx = event.xconfigure.x, ny = event.xconfigure.y; + if (nw && nh && (nw!=_window_width || nh!=_window_height)) { + _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1; + XResizeWindow(dpy,_window,_window_width,_window_height); + _is_resized = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + if (nx!=_window_x || ny!=_window_y) { + _window_x = nx; + _window_y = ny; + _is_moved = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + } break; + case Expose : { + while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {} + _paint(false); + if (_is_fullscreen) { + XWindowAttributes attr; + do { + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + XSetInputFocus(dpy,_window,RevertToParent,CurrentTime); + } + } break; + case ButtonPress : { + do { + _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1); break; + case 3 : set_button(2); break; + case 2 : set_button(3); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event)); + } break; + case ButtonRelease : { + do { + _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1,false); break; + case 3 : set_button(2,false); break; + case 2 : set_button(3,false); break; + case 4 : set_wheel(1); break; + case 5 : set_wheel(-1); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event)); + } break; + case KeyPress : { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,true); + } break; + case KeyRelease : { + char keys_return[32]; // Check that the key has been physically unpressed + XQueryKeymap(dpy,keys_return); + const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8; + const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1; + if (!is_key_pressed) { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,false); + } + } break; + case EnterNotify: { + while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + } break; + case LeaveNotify : { + while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {} + _mouse_x = _mouse_y = -1; _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } break; + case MotionNotify : { + while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } break; + } + } + + static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows + Display *const dpy = cimg::X11_attr().display; + XEvent event; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + if (!arg) for ( ; ; ) { + cimg_lock_display(); + bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); + if (!event_flag) event_flag = XCheckMaskEvent(dpy, + ExposureMask | StructureNotifyMask | ButtonPressMask | + KeyPressMask | PointerMotionMask | EnterWindowMask | + LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event); + if (event_flag) + for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) + cimg::X11_attr().wins[i]->_handle_events(&event); + cimg_unlock_display(); + pthread_testcancel(); + cimg::sleep(8); + } + return 0; + } + + void _set_colormap(Colormap& cmap, const unsigned int dim) { + XColor *const colormap = new XColor[256]; + switch (dim) { + case 1 : { // colormap for greyscale images + for (unsigned int index = 0; index<256; ++index) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8); + colormap[index].flags = DoRed | DoGreen | DoBlue; + } + } break; + case 2 : { // colormap for RG images + for (unsigned int index = 0, r = 8; r<256; r+=16) + for (unsigned int g = 8; g<256; g+=16) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].blue = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } break; + default : { // colormap for RGB images + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + colormap[index].pixel = index; + colormap[index].red = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index].blue = (unsigned short)(b<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } + } + XStoreColors(cimg::X11_attr().display,cmap,colormap,256); + delete[] colormap; + } + + void _map_window() { + Display *const dpy = cimg::X11_attr().display; + bool is_exposed = false, is_mapped = false; + XWindowAttributes attr; + XEvent event; + XMapRaised(dpy,_window); + do { // Wait for the window to be mapped + XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event); + switch (event.type) { + case MapNotify : is_mapped = true; break; + case Expose : is_exposed = true; break; + } + } while (!is_exposed || !is_mapped); + do { // Wait for the window to be visible + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + _window_x = attr.x; + _window_y = attr.y; + } + + void _paint(const bool wait_expose=true) { + if (_is_closed || !_image) return; + Display *const dpy = cimg::X11_attr().display; + if (wait_expose) { // Send an expose event sticked to display window to force repaint + XEvent event; + event.xexpose.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = 1; + event.xexpose.display = dpy; + event.xexpose.window = _window; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = width(); + event.xexpose.height = height(); + event.xexpose.count = 0; + XSendEvent(dpy,_window,0,0,&event); + } else { // Repaint directly (may be called from the expose event) + GC gc = DefaultGC(dpy,DefaultScreen(dpy)); + +#ifdef cimg_use_xshm + if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1); + else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#else + XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#endif + } + } + + template + void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) { + Display *const dpy = cimg::X11_attr().display; + cimg::unused(pixel_type); + +#ifdef cimg_use_xshm + if (_shminfo) { + XShmSegmentInfo *const nshminfo = new XShmSegmentInfo; + XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); + if (!nimage) { delete nshminfo; return; } + else { + nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777); + if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; } + else { + nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); + if (nshminfo->shmaddr==(char*)-1) { + shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; + } else { + nshminfo->readOnly = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,nshminfo); + XFlush(dpy); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(nshminfo->shmaddr); + shmctl(nshminfo->shmid,IPC_RMID,0); + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + T *const ndata = (T*)nimage->data; + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + XShmDetach(dpy,_shminfo); + XDestroyImage(_image); + shmdt(_shminfo->shmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = nshminfo; + _image = nimage; + _data = (void*)ndata; + } + } + } + } + } else +#endif + { + T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T)); + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + _data = (void*)ndata; + XDestroyImage(_image); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0); + } + } + + void _init_fullscreen() { + if (!_is_fullscreen || _is_closed) return; + Display *const dpy = cimg::X11_attr().display; + _background_window = 0; + +#ifdef cimg_use_xrandr + int foo; + if (XRRQueryExtension(dpy,&foo,&foo)) { + XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation); + if (!cimg::X11_attr().resolutions) { + cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo); + cimg::X11_attr().nb_resolutions = (unsigned int)foo; + } + if (cimg::X11_attr().resolutions) { + cimg::X11_attr().curr_resolution = 0; + for (unsigned int i = 0; i=_width && nh>=_height && + nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) && + nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height)) + cimg::X11_attr().curr_resolution = i; + } + if (cimg::X11_attr().curr_resolution>0) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy), + cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + } + } + } + if (!cimg::X11_attr().resolutions) + cimg::warn(_cimgdisplay_instance + "init_fullscreen(): Xrandr extension not supported by the X server.", + cimgdisplay_instance); +#endif + + const unsigned int sx = screen_width(), sy = screen_height(); + if (sx==_width && sy==_height) return; + XSetWindowAttributes attr_set; + + attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy)); + attr_set.override_redirect = 1; + _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0, + InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set); + XEvent event; + XSelectInput(dpy,_background_window,StructureNotifyMask); + XMapRaised(dpy,_background_window); + do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event); + while (event.type!=MapNotify); + + XWindowAttributes attr; + do { + XGetWindowAttributes(dpy,_background_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + Display *const dpy = cimg::X11_attr().display; + XUngrabKeyboard(dpy,CurrentTime); + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + cimg::X11_attr().curr_resolution = 0; + } +#endif + if (_background_window) XDestroyWindow(dpy,_background_window); + _background_window = 0; + _is_fullscreen = false; + } + + static int _assign_xshm(Display *dpy, XErrorEvent *error) { + cimg::unused(dpy,error); + cimg::X11_attr().is_shm_enabled = false; + return 0; + } + + void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + cimg::mutex(14); + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous display window if existing + if (!is_empty()) assign(); + + // Open X11 display and retrieve graphical properties. + Display* &dpy = cimg::X11_attr().display; + if (!dpy) { + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Failed to open X11 display.", + cimgdisplay_instance); + + cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy)); + if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && + cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Invalid %u bits screen mode detected " + "(only 8, 16, 24 and 32 bits modes are managed).", + cimgdisplay_instance, + cimg::X11_attr().nb_bits); + XVisualInfo vtemplate; + vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy))); + int nb_visuals; + XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals); + if (vinfo && vinfo->red_maskblue_mask) cimg::X11_attr().is_blue_first = true; + cimg::X11_attr().byte_order = ImageByteOrder(dpy); + XFree(vinfo); + + cimg_lock_display(); + cimg::X11_attr().events_thread = new pthread_t; + pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0); + } else cimg_lock_display(); + + // Set display variables. + _width = std::min(dimw,(unsigned int)screen_width()); + _height = std::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = cimg::type::min(); + _is_closed = closed_flag; + _title = tmp_title; + flush(); + + // Create X11 window (and LUT, if 8bits display) + if (_is_fullscreen) { + if (!_is_closed) _init_fullscreen(); + const unsigned int sx = screen_width(), sy = screen_height(); + XSetWindowAttributes attr_set; + attr_set.override_redirect = 1; + _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0, + InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set); + } else + _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L); + + XSelectInput(dpy,_window, + ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); + + XStoreName(dpy,_window,_title?_title:" "); + if (cimg::X11_attr().nb_bits==8) { + _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll); + _set_colormap(_colormap,3); + XSetWindowColormap(dpy,_window,_colormap); + } + + static const char *const _window_class = cimg_appname; + XClassHint *const window_class = XAllocClassHint(); + window_class->res_name = (char*)_window_class; + window_class->res_class = (char*)_window_class; + XSetClassHint(dpy,_window,window_class); + XFree(window_class); + + _window_width = _width; + _window_height = _height; + + // Create XImage +#ifdef cimg_use_xshm + _shminfo = 0; + if (XShmQueryExtension(dpy)) { + _shminfo = new XShmSegmentInfo; + _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,_shminfo,_width,_height); + if (!_image) { delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777); + if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0)); + if (_shminfo->shmaddr==(char*)-1) { + shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; + } else { + _shminfo->readOnly = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,_shminfo); + XSync(dpy,0); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); + delete _shminfo; _shminfo = 0; + } + } + } + } + } + if (!_shminfo) +#endif + { + const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1: + (cimg::X11_attr().nb_bits==16?2:4)); + _data = std::malloc(buf_size); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,(char*)_data,_width,_height,8,0); + } + + _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0); + _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0); + XSetWMProtocols(dpy,_window,&_wm_window_atom,1); + + if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime); + cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this; + if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type::min(); + cimg_unlock_display(); + cimg::mutex(14,0); + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + + // Remove display window from event thread list. + unsigned int i; + for (i = 0; ishmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = 0; + } +#endif + + XDestroyImage(_image); + if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap); + XDestroyWindow(dpy,_window); + XSync(dpy,0); + _window = 0; _colormap = 0; _data = 0; _image = 0; + + // Reset display variables. + delete[] _title; + _width = _height = _normalization = _window_width = _window_height = 0; + _window_x = _window_y = cimg::type::min(); + _is_fullscreen = false; + _is_closed = true; + _min = _max = 0; + _title = 0; + flush(); + + cimg_unlock_display(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + _min = _max = 0; + std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))* + (size_t)_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + cimg::X11_attr().nb_bits==16?sizeof(unsigned short): + sizeof(unsigned int))*(size_t)_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + Display *const dpy = cimg::X11_attr().display; + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100), + tmpdimy = (nheight>0)?nheight:(-nheight*height()/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + show(); + cimg_lock_display(); + if (_window_width!=dimx || _window_height!=dimy) { + XWindowAttributes attr; + for (unsigned int i = 0; i<10; ++i) { + XResizeWindow(dpy,_window,dimx,dimy); + XGetWindowAttributes(dpy,_window,&attr); + if (attr.width==(int)dimx && attr.height==(int)dimy) break; + cimg::wait(5,&_timer); + } + } + if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { + case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } + } + _window_width = _width = dimx; _window_height = _height = dimy; + cimg_unlock_display(); + } + _is_resized = false; + if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const cimg_ulong buf_size = (cimg_ulong)_width*_height* + (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + void *image_data = std::malloc(buf_size); + std::memcpy(image_data,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,image_data,buf_size); + std::free(image_data); + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + cimg_lock_display(); + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + _map_window(); + cimg_unlock_display(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + if (_is_fullscreen) _desinit_fullscreen(); + XUnmapWindow(dpy,_window); + _window_x = _window_y = cimg::type::min(); + _is_closed = true; + cimg_unlock_display(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + show(); + if (_window_x!=posx || _window_y!=posy) { + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XMoveWindow(dpy,_window,posx,posy); + _window_x = posx; + _window_y = posy; + cimg_unlock_display(); + } + _is_moved = false; + return paint(); + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XUndefineCursor(dpy,_window); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + static const char pix_data[8] = { 0 }; + XColor col; + col.red = col.green = col.blue = 0; + Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); + Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0); + XFreePixmap(dpy,pix); + XDefineCursor(dpy,_window,cur); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); + _mouse_x = posx; _mouse_y = posy; + _is_moved = false; + XSync(dpy,0); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char *const tmp = new char[1024]; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,1024,format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XStoreName(dpy,_window,tmp); + cimg_unlock_display(); + delete[] tmp; + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(false); + } + + CImgDisplay& paint(const bool wait_expose=true) { + if (is_empty()) return *this; + cimg_lock_display(); + _paint(wait_expose); + cimg_unlock_display(); + return *this; + } + + template + CImgDisplay& render(const CImg& img, const bool flag8=false) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2)); + if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) + return render(img.get_resize(_width,_height,1,-100,1)); + if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) { + static const CImg::ucharT> default_colormap = CImg::ucharT>::default_LUT256(); + return render(img.get_index(default_colormap,1,false)); + } + + const T + *data1 = img._data, + *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1; + + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + cimg_lock_display(); + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, no normalization + _set_colormap(_colormap,img._spectrum); + unsigned char + *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: + new unsigned char[(size_t)img._width*img._height], + *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + (*ptrd++) = (unsigned char)*(data1++); + break; + case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++), + B = (unsigned char)*(data3++); + (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); + delete[] ndata; + } + } break; + case 16 : { // 16 bits colors, no normalization + unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: + new unsigned short[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + ptrd[0] = (val&M) | (G>>3); + ptrd[1] = (G<<5) | (G>>1); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + ptrd[0] = (G<<5) | (G>>1); + ptrd[1] = (val&M) | (G>>3); + ptrd+=2; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd[1] = (G<<5); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = (G<<5); + ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd+=2; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3); + ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd+=2; + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); + delete[] ndata; + } + } break; + default : { // 24 bits colors, no normalization + unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: + new unsigned int[(size_t)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | + (unsigned char)*(data3++); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | + ((unsigned char)*(data1++)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data1++); + ptrd[2] = 0; + ptrd[3] = 0; + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = 0; + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data2++); + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data1++); + ptrd[2] = (unsigned char)*(data2++); + ptrd[3] = (unsigned char)*(data3++); + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = (unsigned char)*(data3++); + ptrd[1] = (unsigned char)*(data2++); + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { _min = (float)cimg::type::min(); _max = (float)cimg::type::max(); } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.f); + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, with normalization + _set_colormap(_colormap,img._spectrum); + unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: + new unsigned char[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char R = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = R; + } break; + case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm), + B = (unsigned char)((*(data3++) - _min)*mm); + *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); + delete[] ndata; + } + } break; + case 16 : { // 16 bits colors, with normalization + unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: + new unsigned short[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; + ptrd[0] = (val&M) | (G>>3); + ptrd[1] = (G<<5) | (val>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; + ptrd[0] = (G<<5) | (val>>3); + ptrd[1] = (val&M) | (G>>3); + ptrd+=2; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd[1] = (G<<5); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = (G<<5); + ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd+=2; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); + ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd+=2; + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); + delete[] ndata; + } + } break; + default : { // 24 bits colors, with normalization + unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: + new unsigned int[(size_t)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (val<<24) | (val<<16) | (val<<8); + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8) | + (unsigned char)((*(data3++) - _min)*mm); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data3++) - _min)*mm)<<24) | + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + ptrd[0] = 0; + ptrd[1] = val; + ptrd[2] = val; + ptrd[3] = val; + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + ptrd[0] = val; + ptrd[1] = val; + ptrd[2] = val; + ptrd[3] = 0; + ptrd+=4; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[3] = 0; + ptrd+=4; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[3] = (unsigned char)((*(data3++) - _min)*mm); + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = (unsigned char)((*(data3++) - _min)*mm); + ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[3] = 0; + ptrd+=4; + } + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } + } + } + } + cimg_unlock_display(); + return *this; + } + + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + img.assign(); + Display *dpy = cimg::X11_attr().display; + cimg_lock_display(); + if (!dpy) { + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display."); + } + Window root = DefaultRootWindow(dpy); + XWindowAttributes gwa; + XGetWindowAttributes(dpy,root,&gwa); + const int width = gwa.width, height = gwa.height; + int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; + if (_x0>_x1) cimg::swap(_x0,_x1); + if (_y0>_y1) cimg::swap(_y0,_y1); + + XImage *image = 0; + if (_x1>=0 && _x0=0 && _y0red_mask, + green_mask = image->green_mask, + blue_mask = image->blue_mask; + img.assign(image->width,image->height,1,3); + T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2); + cimg_forXY(img,x,y) { + const unsigned long pixel = XGetPixel(image,x,y); + *(pR++) = (T)((pixel & red_mask)>>16); + *(pG++) = (T)((pixel & green_mask)>>8); + *(pB++) = (T)(pixel & blue_mask); + } + XDestroyImage(image); + } + } + if (!cimg::X11_attr().display) XCloseDisplay(dpy); + cimg_unlock_display(); + if (img.is_empty()) + throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot " + "with coordinates (%d,%d)-(%d,%d).", + x0,y0,x1,y1); + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned char *ptrs = (unsigned char*)_data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + switch (cimg::X11_attr().nb_bits) { + case 8 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = *(ptrs++); + *(data1++) = (T)(val&0xe0); + *(data2++) = (T)((val&0x1c)<<3); + *(data3++) = (T)(val<<6); + } + } break; + case 16 : { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + val0 = ptrs[0], + val1 = ptrs[1]; + ptrs+=2; + *(data1++) = (T)(val0&0xf8); + *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5)); + *(data3++) = (T)(val1<<3); + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned short + val0 = ptrs[0], + val1 = ptrs[1]; + ptrs+=2; + *(data1++) = (T)(val1&0xf8); + *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5)); + *(data3++) = (T)(val0<<3); + } + } break; + default : { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ++ptrs; + *(data1++) = (T)ptrs[0]; + *(data2++) = (T)ptrs[1]; + *(data3++) = (T)ptrs[2]; + ptrs+=3; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + *(data3++) = (T)ptrs[0]; + *(data2++) = (T)ptrs[1]; + *(data1++) = (T)ptrs[2]; + ptrs+=3; + ++ptrs; + } + } + } + return *this; + } + + // Windows-based implementation. + //------------------------------- +#elif cimg_display==2 + + bool _is_mouse_tracked, _is_cursor_visible; + HANDLE _thread, _is_created, _mutex; + HWND _window, _background_window; + CLIENTCREATESTRUCT _ccs; + unsigned int *_data; + DEVMODE _curr_mode; + BITMAPINFO _bmi; + HDC _hdc; + + static int screen_width() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return (int)mode.dmPelsWidth; + } + + static int screen_height() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return (int)mode.dmPelsHeight; + } + + static void wait_all() { + WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE); + } + + static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { +#ifdef _WIN64 + CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); +#else + CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); +#endif + MSG st_msg; + switch (msg) { + case WM_CLOSE : + disp->_mouse_x = disp->_mouse_y = -1; + disp->_window_x = disp->_window_y = cimg::type::min(); + disp->set_button().set_key(0).set_key(0,false)._is_closed = true; + ReleaseMutex(disp->_mutex); + ShowWindow(disp->_window,SW_HIDE); + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + return 0; + case WM_SIZE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->_mutex,INFINITE); + const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); + if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) { + disp->_window_width = nw; + disp->_window_height = nh; + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_resized = disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_MOVE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->_mutex,INFINITE); + const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); + if (nx!=disp->_window_x || ny!=disp->_window_y) { + disp->_window_x = nx; + disp->_window_y = ny; + disp->_is_moved = disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_PAINT : + disp->paint(); + cimg_lock_display(); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0); + cimg_unlock_display(); + break; + case WM_ERASEBKGND : + // return 0; + break; + case WM_KEYDOWN : + disp->set_key((unsigned int)wParam); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_KEYUP : + disp->set_key((unsigned int)wParam,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MOUSEMOVE : { + while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {} + disp->_mouse_x = LOWORD(lParam); + disp->_mouse_y = HIWORD(lParam); +#if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT) + if (!disp->_is_mouse_tracked) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = disp->_window; + if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true; + } +#endif + if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height()) + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + cimg_lock_display(); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0); + cimg_unlock_display(); + } break; + case WM_MOUSELEAVE : { + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_mouse_tracked = false; + cimg_lock_display(); + while (ShowCursor(TRUE)<0) {} + cimg_unlock_display(); + } break; + case WM_LBUTTONDOWN : + disp->set_button(1); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONDOWN : + disp->set_button(2); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONDOWN : + disp->set_button(3); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_LBUTTONUP : + disp->set_button(1,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONUP : + disp->set_button(2,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONUP : + disp->set_button(3,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case 0x020A : // WM_MOUSEWHEEL: + disp->set_wheel((int)((short)HIWORD(wParam))/120); + SetEvent(cimg::Win32_attr().wait_event); + } + return DefWindowProc(window,msg,wParam,lParam); + } + + static DWORD WINAPI _events_thread(void* arg) { + CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]); + const char *const title = (const char*)(((void**)arg)[1]); + MSG msg; + delete[] (void**)arg; + disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + disp->_bmi.bmiHeader.biWidth = disp->width(); + disp->_bmi.bmiHeader.biHeight = -disp->height(); + disp->_bmi.bmiHeader.biPlanes = 1; + disp->_bmi.bmiHeader.biBitCount = 32; + disp->_bmi.bmiHeader.biCompression = BI_RGB; + disp->_bmi.bmiHeader.biSizeImage = 0; + disp->_bmi.bmiHeader.biXPelsPerMeter = 1; + disp->_bmi.bmiHeader.biYPelsPerMeter = 1; + disp->_bmi.bmiHeader.biClrUsed = 0; + disp->_bmi.bmiHeader.biClrImportant = 0; + disp->_data = new unsigned int[(size_t)disp->_width*disp->_height]; + if (!disp->_is_fullscreen) { // Normal window + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int + border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2), + border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1), + ww = disp->width() + 2*border1, + wh = disp->height() + border1 + border2, + sw = CImgDisplay::screen_width(), + sh = CImgDisplay::screen_height(); + int + wx = (int)cimg::round(cimg::rand(0,sw - ww -1)), + wy = (int)cimg::round(cimg::rand(64,sh - wh - 65)); + if (wx + ww>=sw) wx = sw - ww; + if (wy + wh>=sh) wy = sh - wh; + if (wx<0) wx = 0; + if (wy<0) wy = 0; + disp->_window = CreateWindowA("MDICLIENT",title?title:" ", + (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)), + wx,wy,ww,wh,0,0,0,&(disp->_ccs)); + if (!disp->_is_closed) { + GetWindowRect(disp->_window,&rect); + disp->_window_x = rect.left; + disp->_window_y = rect.top; + } else disp->_window_x = disp->_window_y = cimg::type::min(); + } else { // Fullscreen window + const unsigned int + sx = (unsigned int)screen_width(), + sy = (unsigned int)screen_height(); + disp->_window = CreateWindowA("MDICLIENT",title?title:" ", + (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)), + (int)(sx - disp->_width)/2, + (int)(sy - disp->_height)/2, + disp->width(),disp->height(),0,0,0,&(disp->_ccs)); + disp->_window_x = disp->_window_y = 0; + } + SetForegroundWindow(disp->_window); + disp->_hdc = GetDC(disp->_window); + disp->_window_width = disp->_width; + disp->_window_height = disp->_height; + disp->flush(); +#ifdef _WIN64 + SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp); + SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events); +#else + SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp); + SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events); +#endif + SetEvent(disp->_is_created); + while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); + return 0; + } + + CImgDisplay& _update_window_pos() { + if (_is_closed) _window_x = _window_y = cimg::type::min(); + else { + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + GetWindowRect(_window,&rect); + _window_x = rect.left; + _window_y = rect.top; + } + return *this; + } + + void _init_fullscreen() { + _background_window = 0; + if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0; + else { + DEVMODE mode; + unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; + for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { + const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; + if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { + bestbpp = mode.dmBitsPerPel; + ibest = imode; + bw = nw; bh = nh; + } + } + if (bestbpp) { + _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode); + EnumDisplaySettings(0,ibest,&mode); + ChangeDisplaySettings(&mode,0); + } else _curr_mode.dmSize = 0; + + const unsigned int + sx = (unsigned int)screen_width(), + sy = (unsigned int)screen_height(); + if (sx!=_width || sy!=_height) { + CLIENTCREATESTRUCT background_ccs = { 0,0 }; + _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, + 0,0,(int)sx,(int)sy,0,0,0,&background_ccs); + SetForegroundWindow(_background_window); + } + } + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + if (_background_window) DestroyWindow(_background_window); + _background_window = 0; + if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0); + _is_fullscreen = false; + } + + CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (!is_empty()) assign(); + + // Set display variables + _width = std::min(dimw,(unsigned int)screen_width()); + _height = std::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = cimg::type::min(); + _is_closed = closed_flag; + _is_cursor_visible = true; + _is_mouse_tracked = false; + _title = tmp_title; + flush(); + if (_is_fullscreen) _init_fullscreen(); + + // Create event thread + void *const arg = (void*)(new void*[2]); + ((void**)arg)[0] = (void*)this; + ((void**)arg)[1] = (void*)_title; + _mutex = CreateMutex(0,FALSE_WIN,0); + _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); + _thread = CreateThread(0,0,_events_thread,arg,0,0); + WaitForSingleObject(_is_created,INFINITE); + return *this; + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + DestroyWindow(_window); + TerminateThread(_thread,0); + delete[] _data; + delete[] _title; + _data = 0; + _title = 0; + if (_is_fullscreen) _desinit_fullscreen(); + _width = _height = _normalization = _window_width = _window_height = 0; + _window_x = _window_y = cimg::type::min(); + _is_fullscreen = false; + _is_closed = true; + _min = _max = 0; + _title = 0; + flush(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + _min = _max = 0; + std::memset(_data,0,sizeof(unsigned int)*_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight); + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100), + tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + if (_window_width!=dimx || _window_height!=dimy) { + RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1; + SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); + } + if (_width!=dimx || _height!=dimy) { + unsigned int *const ndata = new unsigned int[dimx*dimy]; + if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); + else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + delete[] _data; + _data = ndata; + _bmi.bmiHeader.biWidth = (LONG)dimx; + _bmi.bmiHeader.biHeight = -(int)dimy; + _width = dimx; + _height = dimy; + } + _window_width = dimx; _window_height = dimy; + show(); + } + _is_resized = false; + if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const cimg_ulong buf_size = (cimg_ulong)_width*_height*4; + void *odata = std::malloc(buf_size); + if (odata) { + std::memcpy(odata,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,odata,buf_size); + std::free(odata); + } + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + ShowWindow(_window,SW_SHOW); + _update_window_pos(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + _is_closed = true; + if (_is_fullscreen) _desinit_fullscreen(); + ShowWindow(_window,SW_HIDE); + _window_x = _window_y = cimg::type::min(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + if (_window_x!=posx || _window_y!=posy) { + SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); + _window_x = posx; + _window_y = posy; + } + show(); + _is_moved = false; + return *this; + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = true; + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = false; + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed || posx<0 || posy<0) return *this; + if (!_is_closed) { + _update_window_pos(); + const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy); + if (res) { _mouse_x = posx; _mouse_y = posy; } + } + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char *const tmp = new char[1024]; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,1024,format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + SetWindowTextA(_window, tmp); + delete[] tmp; + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(); + } + + CImgDisplay& paint() { + if (_is_closed) return *this; + WaitForSingleObject(_mutex,INFINITE); + SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS); + ReleaseMutex(_mutex); + return *this; + } + + template + CImgDisplay& render(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2)); + + const T + *data1 = img._data, + *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1; + + WaitForSingleObject(_mutex,INFINITE); + unsigned int + *const ndata = (img._width==_width && img._height==_height)?_data: + new unsigned int[(size_t)img._width*img._height], + *ptrd = ndata; + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (img._spectrum) { + case 1 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); + } + } break; + case 2 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); + } + } break; + default : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++), + B = (unsigned char)*(data3++); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); + } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { + _min = (float)cimg::type::min(); + _max = (float)cimg::type::max(); + } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.f); + switch (img._spectrum) { + case 1 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); + } + } break; + case 2 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); + } + } break; + default : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm), + B = (unsigned char)((*(data3++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); + } + } + } + } + if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; } + ReleaseMutex(_mutex); + return *this; + } + + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + img.assign(); + HDC hScreen = GetDC(GetDesktopWindow()); + if (hScreen) { + const int + width = GetDeviceCaps(hScreen,HORZRES), + height = GetDeviceCaps(hScreen,VERTRES); + int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; + if (_x0>_x1) cimg::swap(_x0,_x1); + if (_y0>_y1) cimg::swap(_y0,_y1); + if (_x1>=0 && _x0=0 && _y0 + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned int *ptrs = _data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned int val = *(ptrs++); + *(data1++) = (T)(unsigned char)(val>>16); + *(data2++) = (T)(unsigned char)((val>>8)&0xFF); + *(data3++) = (T)(unsigned char)(val&0xFF); + } + return *this; + } +#endif + + //@} + }; // struct CImgDisplay { ... + + /* + #-------------------------------------- + # + # + # + # Definition of the CImg structure + # + # + # + #-------------------------------------- + */ + + //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. + /** + This is the main class of the %CImg Library. It declares and constructs + an image, allows access to its pixel values, and is able to perform various image operations. + + \par Image representation + + A %CImg image is defined as an instance of the container \c CImg, which contains a regular grid of pixels, + each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth + and number of channels. + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), + while the number of channels is rather used as a vector-valued dimension + (it may describe the R,G,B color channels for instance). + If you need a fifth dimension, you can use image lists \c CImgList rather than simple images \c CImg. + + Thus, the \c CImg class is able to represent volumetric images of vector-valued pixels, + as well as images with less dimensions (1D scalar signal, 2D color images, ...). + Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. + + Concerning the pixel value type \c T: + fully supported template types are the basic C++ types: unsigned char, char, short, unsigned int, int, + unsigned long, long, float, double, ... . + Typically, fast image display can be done using CImg images, + while complex image processing algorithms may be rather coded using CImg or CImg + images that have floating-point pixel values. The default value for the template T is \c float. + Using your own template types may be possible. However, you will certainly have to define the complete set + of arithmetic and logical operators for your class. + + \par Image structure + + The \c CImg structure contains \e six fields: + - \c _width defines the number of \a columns of the image (size along the X-axis). + - \c _height defines the number of \a rows of the image (size along the Y-axis). + - \c _depth defines the number of \a slices of the image (size along the Z-axis). + - \c _spectrum defines the number of \a channels of the image (size along the C-axis). + - \c _data defines a \a pointer to the \a pixel \a data (of type \c T). + - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with + another image. + + You can access these fields publicly although it is recommended to use the dedicated functions + width(), height(), depth(), spectrum() and ptr() to do so. + Image dimensions are not limited to a specific range (as long as you got enough available memory). + A value of \e 1 usually means that the corresponding dimension is \a flat. + If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. + Empty images should not contain any pixel data and thus, will not be processed by CImg member functions + (a CImgInstanceException will be thrown instead). + Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). + + \par Image declaration and construction + + Declaring an image can be done by using one of the several available constructors. + Here is a list of the most used: + + - Construct images from arbitrary dimensions: + - CImg img; declares an empty image. + - CImg img(128,128); declares a 128x128 greyscale image with + \c unsigned \c char pixel values. + - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. + - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image + (colors are stored as an image with three channels). + - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image + (with \c double pixel values). + - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image + (with \c float pixels, which is the default value of the template parameter \c T). + - \b Note: images pixels are not automatically initialized to 0. You may use the function \c fill() to + do it, or use the specific constructor taking 5 parameters like this: + CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. + + - Construct images from filenames: + - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the + file "analyze.hdr". + - \b Note: You need to install ImageMagick + to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). + + - Construct images from C-style arrays: + - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer + \c data_buffer (of size 256x256=65536). + - CImg img(data_buffer,256,256,1,3); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). + + The complete list of constructors can be found here. + + \par Most useful functions + + The \c CImg class contains a lot of functions that operates on images. + Some of the most useful are: + + - operator()(): Read or write pixel values. + - display(): displays the image in a new window. + **/ + template + struct CImg { + + unsigned int _width, _height, _depth, _spectrum; + bool _is_shared; + T *_data; + + //! Simple iterator type, to loop through each pixel value of an image instance. + /** + \note + - The \c CImg::iterator type is defined to be a T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + CImg img("reference.jpg"); // Load image from file + // Set all pixels to '0', with a CImg iterator. + for (CImg::iterator it = img.begin(), it::const_iterator type is defined to be a \c const \c T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + const CImg img("reference.jpg"); // Load image from file + float sum = 0; + // Compute sum of all pixel values, with a CImg iterator. + for (CImg::iterator it = img.begin(), it::value_type type of a \c CImg is defined to be a \c T. + - \c CImg::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs. + static size_t safe_size(const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) { + if (!(dx && dy && dz && dc)) return 0; + size_t siz = (size_t)dx, osiz = siz; + if ((dy==1 || (siz*=dy)>osiz) && + ((osiz = siz), dz==1 || (siz*=dz)>osiz) && + ((osiz = siz), dc==1 || (siz*=dc)>osiz) && + ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) return siz; + throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.", + pixel_type(),dx,dy,dz,dc); + } + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimg_plugin +#include cimg_plugin +#endif +#ifdef cimg_plugin1 +#include cimg_plugin1 +#endif +#ifdef cimg_plugin2 +#include cimg_plugin2 +#endif +#ifdef cimg_plugin3 +#include cimg_plugin3 +#endif +#ifdef cimg_plugin4 +#include cimg_plugin4 +#endif +#ifdef cimg_plugin5 +#include cimg_plugin5 +#endif +#ifdef cimg_plugin6 +#include cimg_plugin6 +#endif +#ifdef cimg_plugin7 +#include cimg_plugin7 +#endif +#ifdef cimg_plugin8 +#include cimg_plugin8 +#endif + + //@} + //--------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //--------------------------------------------------------- + + //! Destroy image. + /** + \note + - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances. + - Destroying an empty or shared image does nothing actually. + \warning + - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image + that shares its buffer with the destroyed instance, in order to avoid further invalid memory access + (to a deallocated buffer). + **/ + ~CImg() { + if (!_is_shared) delete[] _data; + } + + //! Construct empty image. + /** + \note + - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum() + are set to \c 0, as well as its pixel buffer pointer data(). + - An empty image may be re-assigned afterwards, e.g. with the family of + assign(unsigned int,unsigned int,unsigned int,unsigned int) methods, + or by operator=(const CImg&). In all cases, the type of pixels stays \c T. + - An empty image is never shared. + \par Example + \code + CImg img1, img2; // Construct two empty images + img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image + img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1' + img2.assign(); // Re-assign 'img2' to be an empty image again + \endcode + **/ + CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} + + //! Construct image with specified size. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \note + - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() + for each constructed image instance. + - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of + an \e empty image. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. when requested size is too big for available memory). + \warning + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values during construction (e.g. with \c 0), use constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead. + \par Example + \code + CImg img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values + CImg img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0' + \endcode + **/ + explicit CImg(const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1): + _is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value Initialization value. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), + but it also fills the pixel buffer with the specified \c value. + \warning + - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels + (e.g. RGB vector, for color images). + For this task, you may use fillC() after construction. + **/ + CImg(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const T& value): + _is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + fill(value); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, + with pixels of type \c T, and initialize pixel + values from the specified sequence of integers \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be an \e integer). + \param value1 Second value of the initialization sequence (must be an \e integer). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \warning + - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence. + Otherwise, the constructor may crash or fill your image pixels with garbage. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image + 0,255,0,255, // Set the 4 values for the red component + 0,0,255,255, // Set the 4 values for the green component + 64,64,64,64); // Set the 4 values for the blue component + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _CImg_stdarg(img,a0,a1,N,t) { \ + size_t _siz = (size_t)N; \ + if (_siz--) { \ + va_list ap; \ + va_start(ap,a1); \ + T *ptrd = (img)._data; \ + *(ptrd++) = (T)a0; \ + if (_siz--) { \ + *(ptrd++) = (T)a1; \ + for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + } \ + va_end(ap); \ + } \ + } + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int); + } + +#if cimg_use_cpp11==1 + //! Construct image with specified size and initialize pixel values from an initializer list of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, + with pixels of type \c T, and initialize pixel + values from the specified initializer list of integers { \c value0,\c value1,\c ... } + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param { value0, value1, ... } Initialization list + \param repeat_values Tells if the value filling process is repeated over the image. + + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image + { 0,255,0,255, // Set the 4 values for the red component + 0,0,255,255, // Set the 4 values for the green component + 64,64,64,64 }); // Set the 4 values for the blue component + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + template + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _cimg_constructor_cpp11(repeat_values) \ + auto it = values.begin(); \ + size_t siz = size(); \ + if (repeat_values) for (T *ptrd = _data; siz--; ) { \ + *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \ + else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); } + assign(size_x,size_y,size_z,size_c); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, + std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, const unsigned int size_y, + std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, + std::initializer_list values, + const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x); + _cimg_constructor_cpp11(repeat_values); + } + + //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers. + /** + Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1, + with pixels of type \c T, and initialize pixel + values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is + given by the size of the initializer list. + \param { value0, value1, ... } Initialization list + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1, + but it also fills the pixel buffer with a sequence of specified integer values. + \par Example + \code + const CImg img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + template + CImg(const std::initializer_list values): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(values.size(),1,1,1); + auto it = values.begin(); + unsigned int siz = _width; + for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); + } + + template + CImg& operator=(std::initializer_list values) { + _cimg_constructor_cpp11(siz>values.size()); + return *this; + } +#endif + + //! Construct image with specified size and initialize pixel values from a sequence of doubles. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be a \e double). + \param value1 Second value of the initialization sequence (must be a \e double). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but + takes a sequence of double values instead of integers. + \warning + - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence. + Otherwise, the constructor may crash or fill your image with garbage. + For instance, the code below will probably crash on most platforms: + \code + const CImg img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'! + \endcode + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double); + } + + //! Construct image with specified size and initialize pixel values from a value string. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initializes pixel values from the specified string \c values. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param values Value string describing the way pixel values are set. + \param repeat_values Tells if the value filling process is repeated over the image. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with values described in the value string \c values. + - Value string \c values may describe two different filling processes: + - Either \c values is a sequences of values assigned to the image pixels, as in "1,2,3,7,8,2". + In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence. + - Either, \c values is a formula, as in "cos(x/10)*sin(y/20)". + In this case, parameter \c repeat_values is pointless. + - For both cases, specifying \c repeat_values is mandatory. + It disambiguates the possible overloading of constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a const char*. + - A \c CImgArgumentException is thrown when an invalid value string \c values is specified. + \par Example + \code + const CImg img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence + img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula + (img1,img2).display(); + \endcode + \image html ref_constructor2.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values):_is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + fill(values,repeat_values); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initializes pixel values from the specified \c t* memory buffer. + \param values Pointer to the input memory buffer. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param is_shared Tells if input memory buffer must be shared by the current instance. + \note + - If \c is_shared is \c false, the image instance allocates its own pixel buffer, + and values from the specified input buffer are copied to the instance buffer. + If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy. + - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its + own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared + image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. when requested size is too big for available memory). + \warning + - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() + (e.g. already deallocated). + \par Example + \code + unsigned char tab[256*256] = { 0 }; + CImg img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab' + img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab' + tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1' + \endcode + **/ + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance " + "from a (%s*) buffer (pixel types are different).", + cimg_instance, + size_x,size_y,size_z,size_c,CImg::pixel_type()); + } + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + + } + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared; + if (_is_shared) _data = const_cast(values); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + std::memcpy(_data,values,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from memory buffer with specified size and pixel ordering scheme. + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const axes_order):_data(0),_is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } + else { ++n_code[c%=4]; s_code[l] = c; } + } + if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { + const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); + int s0 = 0, s1 = 0, s2 = 0, s3 = 0; + const char *inv_order = 0; + switch (code) { + case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc + case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz + case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc + case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy + case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz + case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy + case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc + case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz + case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc + case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx + case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz + case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx + case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc + case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy + case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc + case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx + case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy + case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx + case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz + case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy + case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz + case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx + case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy + case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx + } + CImg(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this); + } else { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from reading an image file. + /** + Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from + an image file. + \param filename Filename, as a C-string. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image + dimensions and pixel values from the specified image file. + - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system + and on the external libraries you used to link your code against. + - Considered pixel type \c T should better fit the file format specification, or data loss may occur during + file load (e.g. constructing a \c CImg from a float-valued image file). + - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not + recognized. + \par Example + \code + const CImg img("reference.jpg"); + img.display(); + \endcode + \image html ref_image.jpg + **/ + explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(filename); + } + + //! Construct image copy. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance. + \param img Input image to copy. + \note + - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the + input image \c img. + - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also + \e shared, and shares its pixel buffer with \c img. + Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img. + This behavior is needful to allow functions to return shared images. + - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input + image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and + \c t are different. + - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than + with different types. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. not enough available memory). + **/ + template + CImg(const CImg& img):_is_shared(false) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image copy \specialization. + CImg(const CImg& img) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = img._is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Advanced copy constructor. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance, + while forcing the shared state of the constructed copy. + \param img Input image to copy. + \param is_shared Tells about the shared state of the constructed copy. + \note + - Similar to CImg(const CImg&), except that it allows to decide the shared state of + the constructed image, which does not depend anymore on the shared state of the input image \c img: + - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img. + For that case, the pixel types \c T and \c t \e must be the same. + - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input + image \c img is shared or not. + - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t. + **/ + template + CImg(const CImg& img, const bool is_shared):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a shared instance from a " + "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).", + cimg_instance, + CImg::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data); + } + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Advanced copy constructor \specialization. + CImg(const CImg& img, const bool is_shared) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image with dimensions borrowed from another image. + /** + Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing + \c CImg instance. + \param img Input image from which dimensions are borrowed. + \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions + (\e not its pixel values) from an existing \c CImg instance. + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg&,const char*,T) + instead. + \par Example + \code + const CImg img1(256,128,1,3), // 'img1' is a 256x128x1x3 image + img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image + img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image + img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0') + \endcode + **/ + template + CImg(const CImg& img, const char *const dimensions): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values. + /** + Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing + \c CImg instance, and set all pixel values to specified \c value. + \param img Input image from which dimensions are borrowed. + \param dimensions String describing the image size along the X,Y,Z and V-dimensions. + \param value Value used for initialization. + \note + - Similar to CImg(const CImg&,const char*), but it also fills the pixel buffer with the specified \c value. + **/ + template + CImg(const CImg& img, const char *const dimensions, const T& value): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions).fill(value); + } + + //! Construct image from a display window. + /** + Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance. + \param disp Input display window. + \note + - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay. + - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 + (i.e. a 2D color image). + - The image pixels are read as 8-bits RGB values. + **/ + explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + disp.snapshot(*this); + } + + // Constructor and assignment operator for rvalue references (c++11). + // This avoids an additional image copy for methods returning new images. Can save RAM for big images ! +#if cimg_use_cpp11==1 + CImg(CImg&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + swap(img); + } + + CImg& operator=(CImg&& img) { + if (_is_shared) return assign(img); + return img.swap(*this); + } +#endif + + //! Construct empty image \inplace. + /** + In-place version of the default constructor CImg(). It simply resets the instance to an empty image. + **/ + CImg& assign() { + if (!_is_shared) delete[] _data; + _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; + return *this; + } + + //! Construct image with specified size \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!siz) return assign(); + const size_t curr_siz = (size_t)size(); + if (siz!=curr_siz) { + if (_is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from specified " + "image (%u,%u,%u,%u).", + cimg_instance, + size_x,size_y,size_z,size_c); + else { + delete[] _data; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + } + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + return *this; + } + + //! Construct image with specified size and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const T& value) { + return assign(size_x,size_y,size_z,size_c).fill(value); + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a value string \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values) { + return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \inplace. + /** + In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int). + **/ + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + assign(size_x,size_y,size_z,size_c); + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + const size_t curr_siz = (size_t)size(); + if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); + if (_is_shared || values + siz<_data || values>=_data + size()) { + assign(size_x,size_y,size_z,size_c); + if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T)); + else std::memcpy((void*)_data,(void*)values,siz*sizeof(T)); + } else { + T *new_data = 0; + try { new_data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + std::memcpy((void*)new_data,(void*)values,siz*sizeof(T)); + delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + } + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + if (is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from (%s*) buffer" + "(pixel types are different).", + cimg_instance, + CImg::pixel_type()); + return assign(values,size_x,size_y,size_z,size_c); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } + else { + if (!_is_shared) { + if (values + siz<_data || values>=_data + size()) assign(); + else cimg::warn(_cimg_instance + "assign(): Shared image instance has overlapping memory.", + cimg_instance); + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; + _data = const_cast(values); + } + return *this; + } + + //! Construct image from memory buffer with specified size and pixel ordering scheme. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const axes_order) { + CImg(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this); + } + + //! Construct image from reading an image file \inplace. + /** + In-place version of the constructor CImg(const char*). + **/ + CImg& assign(const char *const filename) { + return load(filename); + } + + //! Construct image copy \inplace. + /** + In-place version of the constructor CImg(const CImg&). + **/ + template + CImg& assign(const CImg& img) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum); + } + + //! In-place version of the advanced copy constructor. + /** + In-place version of the constructor CImg(const CImg&,bool). + **/ + template + CImg& assign(const CImg& img, const bool is_shared) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared); + } + + //! Construct image with dimensions borrowed from another image \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions) { + if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum); + unsigned int siz[4] = { 0,1,1,1 }, k = 0; + CImg item(256); + for (const char *s = dimensions; *s && k<4; ++k) { + if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item); + if (*s) { + unsigned int val = 0; char sep = 0; + if (cimg_sscanf(s,"%u%c",&val,&sep)>0) { + if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100; + else siz[k] = val; + while (*s>='0' && *s<='9') ++s; + if (sep=='%') ++s; + } else switch (cimg::lowercase(*s)) { + case 'x' : case 'w' : siz[k] = img._width; ++s; break; + case 'y' : case 'h' : siz[k] = img._height; ++s; break; + case 'z' : case 'd' : siz[k] = img._depth; ++s; break; + case 'c' : case 's' : siz[k] = img._spectrum; ++s; break; + default : + throw CImgArgumentException(_cimg_instance + "assign(): Invalid character '%c' detected in specified dimension string '%s'.", + cimg_instance, + *s,dimensions); + } + } + } + return assign(siz[0],siz[1],siz[2],siz[3]); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*,T). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions, const T& value) { + return assign(img,dimensions).fill(value); + } + + //! Construct image from a display window \inplace. + /** + In-place version of the constructor CImg(const CImgDisplay&). + **/ + CImg& assign(const CImgDisplay &disp) { + disp.snapshot(*this); + return *this; + } + + //! Construct empty image \inplace. + /** + Equivalent to assign(). + \note + - It has been defined for compatibility with STL naming conventions. + **/ + CImg& clear() { + return assign(); + } + + //! Transfer content of an image instance into another one. + /** + Transfer the dimensions and the pixel buffer content of an image instance into another one, + and replace instance by an empty image. It avoids the copy of the pixel buffer + when possible. + \param img Destination image. + \note + - Pixel types \c T and \c t of source and destination images can be different, though the process is + designed to be instantaneous when \c T and \c t are the same. + \par Example + \code + CImg src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0' + dest(16,16); // Construct a 16x16x1x1 (scalar) image + src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image + \endcode + **/ + template + CImg& move_to(CImg& img) { + img.assign(*this); + assign(); + return img; + } + + //! Transfer content of an image instance into another one \specialization. + CImg& move_to(CImg& img) { + if (_is_shared || img._is_shared) img.assign(*this); + else swap(img); + assign(); + return img; + } + + //! Transfer content of an image instance into a new image in an image list. + /** + Transfer the dimensions and the pixel buffer content of an image instance + into a newly inserted image at position \c pos in specified \c CImgList instance. + \param list Destination list. + \param pos Position of the newly inserted image in the list. + \note + - When optional parameter \c pos is omitted, the image instance is transferred as a new + image at the end of the specified \c list. + - It is convenient to sequentially insert new images into image lists, with no + additional copies of memory buffer. + \par Example + \code + CImgList list; // Construct an empty image list + CImg img("reference.jpg"); // Read image from filename + img.move_to(list); // Transfer image content as a new item in the list (no buffer copy) + \endcode + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos=~0U) { + const unsigned int npos = pos>list._width?list._width:pos; + move_to(list.insert(1,npos)[npos]); + return list; + } + + //! Swap fields of two image instances. + /** + \param img Image to swap fields with. + \note + - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing + with algorithms requiring two swapping buffers. + \par Example + \code + CImg img1("lena.jpg"), + img2("milla.jpg"); + img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena' + \endcode + **/ + CImg& swap(CImg& img) { + cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum); + cimg::swap(_data,img._data); + cimg::swap(_is_shared,img._is_shared); + return img; + } + + //! Return a reference to an empty image. + /** + \note + This function is useful mainly to declare optional parameters having type \c CImg in functions prototypes, + e.g. + \code + void f(const int x=0, const int y=0, const CImg& img=CImg::empty()); + \endcode + **/ + static CImg& empty() { + static CImg _empty; + return _empty.assign(); + } + + //! Return a reference to an empty image \const. + static const CImg& const_empty() { + static const CImg _empty; + return _empty; + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Access to a pixel value. + /** + Return a reference to a located pixel value of the image instance, + being possibly \e const, whether the image instance is \e const or not. + This is the standard method to get/set pixel values in \c CImg images. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Range of pixel coordinates start from (0,0,0,0) to + (width() - 1,height() - 1,depth() - 1,spectrum() - 1). + - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the + corresponding dimension is equal to \c 1. + For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by img(x,y,c) instead of + img(x,y,0,c). + \warning + - There is \e no boundary checking done in this operator, to make it as fast as possible. + You \e must take care of out-of-bounds access by yourself, if necessary. + For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary + checking operations in this operator. In that case, warning messages will be printed on the error output + when accessing out-of-bounds pixels. + \par Example + \code + CImg img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0' + const float + valR = img(10,10,0,0), // Read red value at coordinates (10,10) + valG = img(10,10,0,1), // Read green value at coordinates (10,10) + valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted) + avg = (valR + valG + valB)/3; // Compute average pixel value + img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value + \endcode + **/ +#if cimg_verbosity>=3 + T& operator()(const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + const ulongT off = (ulongT)offset(x,y,z,c); + if (!_data || off>=size()) { + cimg::warn(_cimg_instance + "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].", + cimg_instance, + (int)x,(int)y,(int)z,(int)c,off); + return *_data; + } + else return _data[off]; + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->operator()(x,y,z,c); + } + + //! Access to a pixel value. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param wh Precomputed offset, must be equal to width()*\ref height(). + \param whd Precomputed offset, must be equal to width()*\ref height()*\ref depth(). + \note + - Similar to (but faster than) operator()(). + It uses precomputed offsets to optimize memory access. You may use it to optimize + the reading/writing of several pixel values in the same image (e.g. in a loop). + **/ + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd=0) { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd=0) const { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } +#else + T& operator()(const unsigned int x) { + return _data[x]; + } + + const T& operator()(const unsigned int x) const { + return _data[x]; + } + + T& operator()(const unsigned int x, const unsigned int y) { + return _data[x + y*_width]; + } + + const T& operator()(const unsigned int x, const unsigned int y) const { + return _data[x + y*_width]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const ulongT wh) { + return _data[x + y*_width + z*wh]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const ulongT wh) const { + return _data[x + y*_width + z*wh]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd) { + return _data[x + y*_width + z*wh + c*whd]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd) const { + return _data[x + y*_width + z*wh + c*whd]; + } +#endif + + //! Implicitly cast an image into a \c T*. + /** + Implicitly cast a \c CImg instance into a \c T* or \c const \c T* pointer, whether the image instance + is \e const or not. The returned pointer points on the first value of the image pixel buffer. + \note + - It simply returns the pointer data() to the pixel buffer. + - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g. + \code + CImg img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image + if (img1) { // Test succeeds, 'img1' is not an empty image + if (!img2) { // Test succeeds, 'img2' is an empty image + std::printf("'img1' is not empty, 'img2' is empty."); + } + } + \endcode + - It also allows to use brackets to access pixel values, without need for a \c CImg::operator[](), e.g. + \code + CImg img(100,100); + const float value = img[99]; // Access to value of the last pixel on the first row + img[510] = 255; // Set pixel value at (10,5) + \endcode + **/ + operator T*() { + return _data; + } + + //! Implicitly cast an image into a \c T* \const. + operator const T*() const { + return _data; + } + + //! Assign a value to all image pixels. + /** + Assign specified \c value to each pixel value of the image instance. + \param value Value that will be assigned to image pixels. + \note + - The image size is never modified. + - The \c value may be casted to pixel type \c T if necessary. + \par Example + \code + CImg img(100,100); // Declare image (with garbage values) + img = 0; // Set all pixel values to '0' + img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char') + \endcode + **/ + CImg& operator=(const T& value) { + return fill(value); + } + + //! Assign pixels values from a specified expression. + /** + Initialize all pixel values from the specified string \c expression. + \param expression Value string describing the way pixel values are set. + \note + - String parameter \c expression may describe different things: + - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"), + the pixel values are set from specified \c expression and the image size is not modified. + - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and + replace the image instance. The image size is modified if necessary. + \par Example + \code + CImg img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values + img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence + img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula + img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified) + (img1,img2,img3).display(); + \endcode + \image html ref_operator_eq.jpg + **/ + CImg& operator=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + _fill(expression,true,1,0,0,"operator=",0); + } catch (CImgException&) { + cimg::exception_mode(omode); + load(expression); + } + cimg::exception_mode(omode); + return *this; + } + + //! Copy an image into the current image instance. + /** + Similar to the in-place copy constructor assign(const CImg&). + **/ + template + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy an image into the current image instance \specialization. + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy the content of a display window to the current image instance. + /** + Similar to assign(const CImgDisplay&). + **/ + CImg& operator=(const CImgDisplay& disp) { + disp.snapshot(*this); + return *this; + } + + //! In-place addition operator. + /** + Add specified \c value to all pixels of an image instance. + \param value Value to add. + \note + - Resulting pixel values are casted to fit the pixel type \c T. + For instance, adding \c 0.2 to a \c CImg is possible but does nothing indeed. + - Overflow values are treated as with standard C++ numeric types. For instance, + \code + CImg img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255' + img+=1; // Add '1' to each pixels -> Overflow + // here all pixels of image 'img' are equal to '0'. + \endcode + - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, + and use cut() after addition. + \par Example + \code + CImg img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]) + CImg img2(img1); // Construct a float-valued copy of 'img1' + img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats + img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint + img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1' + const CImg img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way + (img1,img2,img3).display(); + \endcode + \image html ref_operator_plus.jpg + **/ + template + CImg& operator+=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr + value,524288); + return *this; + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the specified string \c expression. + \param expression Value string describing the way pixel values are added. + \note + - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance, + instead of assigning them. + **/ + CImg& operator+=(const char *const expression) { + return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this); + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the values of the input image \c img. + \param img Input image to add. + \note + - The size of the image instance is never modified. + - It is not mandatory that input image \c img has the same size as the image instance. + If less values are available in \c img, then the values are added periodically. For instance, adding one + WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3) + means each color channel will be incremented with the same values at the same locations. + \par Example + \code + CImg img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) + // Construct a scalar shading (img2.spectrum()==1). + const CImg img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); + img1+=img2; // Add shading to each channel of 'img1' + img1.cut(0,255); // Prevent [0,255] overflow + (img2,img1).display(); + \endcode + \image html ref_operator_plus1.jpg + **/ + template + CImg& operator+=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this+=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator++() { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr + 1,524288); + return *this; + } + + //! In-place increment operator (postfix). + /** + Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance. + \note + - Use the prefixed version operator++() if you don't need a copy of the initial + (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage. + **/ + CImg operator++(int) { + const CImg copy(*this,false); + ++*this; + return copy; + } + + //! Return a non-shared copy of the image instance. + /** + \note + - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T. + Indeed, the usual copy constructor CImg(const CImg&) returns a shared copy of a shared input image, + and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no + information about the shared state of the input image. + - Writing \c (+img) is equivalent to \c CImg(img,false). + **/ + CImg operator+() const { + return CImg(*this,false); + } + + //! Addition operator. + /** + Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const t value) const { + return CImg<_cimg_Tt>(*this,false)+=value; + } + + //! Addition operator. + /** + Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator+(const char *const expression) const { + return CImg(*this,false)+=expression; + } + + //! Addition operator. + /** + Similar to operator+=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)+=img; + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const t), except that it performs a subtraction instead of an addition. + **/ + template + CImg& operator-=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr - value,524288); + return *this; + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const char*), except that it performs a subtraction instead of an addition. + **/ + CImg& operator-=(const char *const expression) { + return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this); + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const CImg&), except that it performs a subtraction instead of an addition. + **/ + template + CImg& operator-=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this-=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator--() { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr - 1,524288); + return *this; + } + + //! In-place decrement operator (postfix). + /** + Similar to operator++(int), except that it performs a decrement instead of an increment. + **/ + CImg operator--(int) { + const CImg copy(*this,false); + --*this; + return copy; + } + + //! Replace each pixel by its opposite value. + /** + \note + - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. + For instance, the \c unsigned \c char opposite of \c 1 is \c 255. + \par Example + \code + const CImg + img1("reference.jpg"), // Load a RGB color image + img2 = -img1; // Compute its opposite (in 'unsigned char') + (img1,img2).display(); + \endcode + \image html ref_operator_minus.jpg + **/ + CImg operator-() const { + return CImg(_width,_height,_depth,_spectrum,(T)0)-=*this; + } + + //! Subtraction operator. + /** + Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const t value) const { + return CImg<_cimg_Tt>(*this,false)-=value; + } + + //! Subtraction operator. + /** + Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator-(const char *const expression) const { + return CImg(*this,false)-=expression; + } + + //! Subtraction operator. + /** + Similar to operator-=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)-=img; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const t), except that it performs a multiplication instead of an addition. + **/ + template + CImg& operator*=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr * value,262144); + return *this; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. + **/ + CImg& operator*=(const char *const expression) { + return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this)); + } + + //! In-place multiplication operator. + /** + Replace the image instance by the matrix multiplication between the image instance and the specified matrix + \c img. + \param img Second operand of the matrix multiplication. + \note + - It does \e not compute a pointwise multiplication between two images. For this purpose, use + mul(const CImg&) instead. + - The size of the image instance can be modified by this operator. + \par Example + \code + CImg A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4] + const CImg X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2] + A*=X; // Assign matrix multiplication A*X to 'A' + // 'A' is now a 1x2 vector whose values are [5;11]. + \endcode + **/ + template + CImg& operator*=(const CImg& img) { + return ((*this)*img).move_to(*this); + } + + //! Multiplication operator. + /** + Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const t value) const { + return CImg<_cimg_Tt>(*this,false)*=value; + } + + //! Multiplication operator. + /** + Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator*(const char *const expression) const { + return CImg(*this,false)*=expression; + } + + //! Multiplication operator. + /** + Similar to operator*=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const CImg& img) const { + typedef _cimg_Ttdouble Ttdouble; + typedef _cimg_Tt Tt; + if (_width!=img._height || _depth!=1 || _spectrum!=1) + throw CImgArgumentException(_cimg_instance + "operator*(): Invalid multiplication of instance by specified " + "matrix (%u,%u,%u,%u,%p).", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + CImg res(img._width,_height); + + // Check for common cases to optimize. + if (img._width==1) { // Matrix * Vector + if (_height==1) switch (_width) { // Vector^T * Vector + case 1 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0]); + return res; + case 2 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); + return res; + case 3 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2]); + return res; + case 4 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); + return res; + default : { + Ttdouble val = 0; + cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096)) + cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i]; + res[0] = val; + return res; + } + } else if (_height==_width) switch (_width) { // Square_matrix * Vector + case 2 : // 2x2_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); + res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]); + return res; + case 3 : // 3x3_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2]); + res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] + + (Ttdouble)_data[5]*img[2]); + res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] + + (Ttdouble)_data[8]*img[2]); + return res; + case 4 : // 4x4_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); + res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] + + (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]); + res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] + + (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]); + res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] + + (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]); + return res; + } + } else if (_height==_width) { + if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix + case 2 : // 2x2_matrix * 2x2_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]); + res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]); + res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]); + return res; + case 3 : // 3x3_matrix * 3x3_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] + + (Ttdouble)_data[2]*img[6]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] + + (Ttdouble)_data[2]*img[7]); + res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] + + (Ttdouble)_data[2]*img[8]); + res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] + + (Ttdouble)_data[5]*img[6]); + res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] + + (Ttdouble)_data[5]*img[7]); + res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] + + (Ttdouble)_data[5]*img[8]); + res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] + + (Ttdouble)_data[8]*img[6]); + res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] + + (Ttdouble)_data[8]*img[7]); + res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] + + (Ttdouble)_data[8]*img[8]); + return res; + case 4 : // 4x4_matrix * 4x4_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] + + (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] + + (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]); + res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] + + (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]); + res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] + + (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]); + res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] + + (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]); + res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] + + (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]); + res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] + + (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]); + res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] + + (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]); + res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] + + (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]); + res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] + + (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]); + res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] + + (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]); + res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] + + (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]); + res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] + + (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]); + res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] + + (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]); + res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] + + (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]); + res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] + + (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]); + return res; + } else switch (_width) { // Square_matrix * Matrix + case 2 : { // 2x2_matrix * Matrix + const t *const ps0 = img.data(), *const ps1 = img.data(0,1); + Tt *const pd0 = res.data(), *const pd1 = res.data(0,1); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], + a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i]; + pd0[i] = (Tt)(a0*x + a1*y); + pd1[i] = (Tt)(a2*x + a3*y); + } + return res; + } + case 3 : { // 3x3_matrix * Matrix + const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2); + Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], + a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], + a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i]; + pd0[i] = (Tt)(a0*x + a1*y + a2*z); + pd1[i] = (Tt)(a3*x + a4*y + a5*z); + pd2[i] = (Tt)(a6*x + a7*y + a8*z); + } + return res; + } + case 4 : { // 4x4_matrix * Matrix + const t + *const ps0 = img.data(), *const ps1 = img.data(0,1), + *const ps2 = img.data(0,2), *const ps3 = img.data(0,3); + Tt + *const pd0 = res.data(), *const pd1 = res.data(0,1), + *const pd2 = res.data(0,2), *const pd3 = res.data(0,3); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3], + a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], + a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11], + a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14], + a15 = (Ttdouble)_data[15]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i]; + pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c); + pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c); + pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c); + pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c); + } + return res; + } + } + } + + // Fallback to generic version. +#if cimg_use_openmp!=0 + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 && + img.size()>(cimg_openmp_sizefactor)*1024)) + cimg_forXY(res,i,j) { + Ttdouble value = 0; + cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); + res(i,j) = (Tt)value; + } +#else + Tt *ptrd = res._data; + cimg_forXY(res,i,j) { + Ttdouble value = 0; + cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); + *(ptrd++) = (Tt)value; + } +#endif + return res; + } + + //! In-place division operator. + /** + Similar to operator+=(const t), except that it performs a division instead of an addition. + **/ + template + CImg& operator/=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr / value,32768); + return *this; + } + + //! In-place division operator. + /** + Similar to operator+=(const char*), except that it performs a division instead of an addition. + **/ + CImg& operator/=(const char *const expression) { + return div((+*this)._fill(expression,true,1,0,0,"operator/=",this)); + } + + //! In-place division operator. + /** + Replace the image instance by the (right) matrix division between the image instance and the specified + matrix \c img. + \param img Second operand of the matrix division. + \note + - It does \e not compute a pointwise division between two images. For this purpose, use + div(const CImg&) instead. + - It returns the matrix operation \c A*inverse(img). + - The size of the image instance can be modified by this operator. + **/ + template + CImg& operator/=(const CImg& img) { + return (*this*img.get_invert()).move_to(*this); + } + + //! Division operator. + /** + Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const t value) const { + return CImg<_cimg_Tt>(*this,false)/=value; + } + + //! Division operator. + /** + Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator/(const char *const expression) const { + return CImg(*this,false)/=expression; + } + + //! Division operator. + /** + Similar to operator/=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const CImg& img) const { + return (*this)*img.get_invert(); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const t), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384); + return *this; + } + + //! In-place modulo operator. + /** + Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. + **/ + CImg& operator%=(const char *const expression) { + return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const CImg&), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this%=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> operator%(const t value) const { + return CImg<_cimg_Tt>(*this,false)%=value; + } + + //! Modulo operator. + /** + Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator%(const char *const expression) const { + return CImg(*this,false)%=expression; + } + + //! Modulo operator. + /** + Similar to operator%=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator%(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)%=img; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768); + return *this; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. + **/ + CImg& operator&=(const char *const expression) { + return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this); + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this&=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator&(const t value) const { + return (+*this)&=value; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator&(const char *const expression) const { + return (+*this)&=expression; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator&(const CImg& img) const { + return (+*this)&=img; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768); + return *this; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. + **/ + CImg& operator|=(const char *const expression) { + return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this); + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this|=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator|(const t value) const { + return (+*this)|=value; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator|(const char *const expression) const { + return (+*this)|=expression; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator|(const CImg& img) const { + return (+*this)|=img; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead. + **/ + template + CImg& operator^=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768); + return *this; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. + **/ + CImg& operator^=(const char *const expression) { + return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this); + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg&) instead. + **/ + template + CImg& operator^=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator^(const t value) const { + return (+*this)^=value; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator^(const char *const expression) const { + return (+*this)^=expression; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator^(const CImg& img) const { + return (+*this)^=img; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536); + return *this; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. + **/ + CImg& operator<<=(const char *const expression) { + return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this); + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator<<(const t value) const { + return (+*this)<<=value; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator<<(const char *const expression) const { + return (+*this)<<=expression; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const CImg&), except that it returns a new image instance instead of + operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator<<(const CImg& img) const { + return (+*this)<<=img; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536); + return *this; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. + **/ + CImg& operator>>=(const char *const expression) { + return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this); + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs> (int)*(ptrs++)); + for (const t *ptrs = img._data; ptrd> (int)*(ptrs++)); + } + return *this; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const t value) const { + return (+*this)>>=value; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator>>(const char *const expression) const { + return (+*this)>>=expression; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const CImg&), except that it returns a new image instance instead of + operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const CImg& img) const { + return (+*this)>>=img; + } + + //! Bitwise inversion operator. + /** + Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value. + **/ + CImg operator~() const { + CImg res(_width,_height,_depth,_spectrum); + const T *ptrs = _data; + cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; } + return res; + } + + //! Test if all pixels of an image have the same value. + /** + Return \c true is all pixels of the image instance are equal to the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator==(const t value) const { + if (is_empty()) return false; + typedef _cimg_Tt Tt; + bool is_equal = true; + for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {} + return is_equal; + } + + //! Test if all pixel values of an image follow a specified expression. + /** + Return \c true is all pixels of the image instance are equal to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator==(const char *const expression) const { + return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this); + } + + //! Test if two images have the same size and values. + /** + Return \c true if the image instance and the input image \c img have the same pixel values, + even if the dimensions of the two images do not match. It returns \c false otherwise. + \param img Input image to compare with. + \note + - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() + to return \c true. + Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different + pixel types \c T and \c t. + \par Example + \code + const CImg img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values) + const CImg img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values) + if (img1==img2) { // Test succeeds, image dimensions and values are the same + std::printf("'img1' and 'img2' have same dimensions and values."); + } + \endcode + **/ + template + bool operator==(const CImg& img) const { + typedef _cimg_Tt Tt; + const ulongT siz = size(); + bool is_equal = true; + if (siz!=img.size()) return false; + t *ptrs = img._data + siz; + for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {} + return is_equal; + } + + //! Test if pixels of an image are all different from a value. + /** + Return \c true is all pixels of the image instance are different than the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator!=(const t value) const { + return !((*this)==value); + } + + //! Test if all pixel values of an image are different from a specified expression. + /** + Return \c true is all pixels of the image instance are different to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator!=(const char *const expression) const { + return !((*this)==expression); + } + + //! Test if two images have different sizes or values. + /** + Return \c true if the image instance and the input image \c img have different dimensions or pixel values, + and \c false otherwise. + \param img Input image to compare with. + \note + - Writing \c img1!=img2 is equivalent to \c !(img1==img2). + **/ + template + bool operator!=(const CImg& img) const { + return !((*this)==img); + } + + //! Construct an image list from two images. + /** + Return a new list of image (\c CImgList instance) containing exactly two elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image \c img, at position [\c 1]. + + \param img Input image that will be the second image of the resulting list. + \note + - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow + in practice (see warning below). + - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are + inserted as new non-shared copies in the resulting list. + - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary. + \warning + - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list. + This may become very expensive in terms of speed and used memory. You should avoid using this technique to + build a new CImgList instance from several images, if you are seeking for performance. + Fast insertions of images in an image list are possible with + CImgList::insert(const CImg&,unsigned int,bool) or move_to(CImgList&,unsigned int). + \par Example + \code + const CImg + img1("reference.jpg"), + img2 = img1.get_mirror('x'), + img3 = img2.get_blur(5); + const CImgList list = (img1,img2); // Create list of two elements from 'img1' and 'img2' + (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3' + \endcode + \image html ref_operator_comma.jpg + **/ + template + CImgList<_cimg_Tt> operator,(const CImg& img) const { + return CImgList<_cimg_Tt>(*this,img); + } + + //! Construct an image list from image instance and an input image list. + /** + Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()]. + + \param list Input image list that will be appended to the image instance. + \note + - Similar to operator,(const CImg&) const, except that it takes an image list as an argument. + **/ + template + CImgList<_cimg_Tt> operator,(const CImgList& list) const { + return CImgList<_cimg_Tt>(list,false).insert(*this,0); + } + + //! Split image along specified axis. + /** + Return a new list of images (\c CImgList instance) containing the split components + of the instance image along the specified axis. + \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c') + \note + - Similar to get_split(char,int) const, with default second argument. + \par Example + \code + const CImg img("reference.jpg"); // Load a RGB color image + const CImgList list = (img<'c'); // Get a list of its three R,G,B channels + (img,list).display(); + \endcode + \image html ref_operator_less.jpg + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the number of image columns. + /** + Return the image width, i.e. the image dimension along the X-axis. + \note + - The width() of an empty image is equal to \c 0. + - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations. + - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._width. + **/ + int width() const { + return (int)_width; + } + + //! Return the number of image rows. + /** + Return the image height, i.e. the image dimension along the Y-axis. + \note + - The height() of an empty image is equal to \c 0. + - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._height. + **/ + int height() const { + return (int)_height; + } + + //! Return the number of image slices. + /** + Return the image depth, i.e. the image dimension along the Z-axis. + \note + - The depth() of an empty image is equal to \c 0. + - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image + is said to be \e volumetric. + - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._depth. + **/ + int depth() const { + return (int)_depth; + } + + //! Return the number of image channels. + /** + Return the number of image channels, i.e. the image dimension along the C-axis. + \note + - The spectrum() of an empty image is equal to \c 0. + - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 + for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel). + The number of channels of an image instance is not limited. The meaning of the pixel values is not linked + up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image). + - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._spectrum. + **/ + int spectrum() const { + return (int)_spectrum; + } + + //! Return the total number of pixel values. + /** + Return width()*\ref height()*\ref depth()*\ref spectrum(), + i.e. the total number of values of type \c T in the pixel buffer of the image instance. + \note + - The size() of an empty image is equal to \c 0. + - The allocated memory size for a pixel buffer of a non-shared \c CImg instance is equal to + size()*sizeof(T). + \par Example + \code + const CImg img(100,100,1,3); // Construct new 100x100 color image + if (img.size()==30000) // Test succeeds + std::printf("Pixel buffer uses %lu bytes", + img.size()*sizeof(float)); + \endcode + **/ + ulongT size() const { + return (ulongT)_width*_height*_depth*_spectrum; + } + + //! Return a pointer to the first pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance, + whether the instance is \c const or not. + \note + - The data() of an empty image is equal to \c 0 (null pointer). + - The allocated pixel buffer for the image instance starts from \c data() + and goes to data()+\ref size() - 1 (included). + - To get the pointer to one particular location of the pixel buffer, use + data(unsigned int,unsigned int,unsigned int,unsigned int) instead. + **/ + T* data() { + return _data; + } + + //! Return a pointer to the first pixel value \const. + const T* data() const { + return _data; + } + + //! Return a pointer to a located pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer + of the image instance, + whether the instance is \c const or not. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)). Thus, this method has the same + properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + **/ +#if cimg_verbosity>=3 + T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + const ulongT off = (ulongT)offset(x,y,z,c); + if (off>=size()) + cimg::warn(_cimg_instance + "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].", + cimg_instance, + x,y,z,c,off); + return _data + off; + } + + //! Return a pointer to a located pixel value \const. + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->data(x,y,z,c); + } +#else + T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; + } + + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; + } +#endif + + //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)) - img.data(). + Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + \par Example + \code + const CImg img(100,100,1,3); // Define a 100x100 RGB-color image + const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10) + const float val = img[off]; // Get the blue value of this pixel + \endcode + **/ + longT offset(const int x, const int y=0, const int z=0, const int c=0) const { + return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth; + } + + //! Return a CImg::iterator pointing to the first pixel value. + /** + \note + - Equivalent to data(). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + iterator begin() { + return _data; + } + + //! Return a CImg::iterator pointing to the first value of the pixel buffer \const. + const_iterator begin() const { + return _data; + } + + //! Return a CImg::iterator pointing next to the last pixel value. + /** + \note + - Writing \c img.end() is equivalent to img.data() + img.size(). + - It has been mainly defined for compatibility with STL naming conventions. + \warning + - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. + Trying to read or write the content of the returned iterator will probably result in a crash. + Use it mainly as a strict upper bound for a CImg::iterator. + \par Example + \code + CImg img(100,100,1,3); // Define a 100x100 RGB color image + // 'img.end()' used below as an upper bound for the iterator. + for (CImg::iterator it = img.begin(); it::iterator pointing next to the last pixel value \const. + const_iterator end() const { + return _data + size(); + } + + //! Return a reference to the first pixel value. + /** + \note + - Writing \c img.front() is equivalent to img[0], or img(0,0,0,0). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& front() { + return *_data; + } + + //! Return a reference to the first pixel value \const. + const T& front() const { + return *_data; + } + + //! Return a reference to the last pixel value. + /** + \note + - Writing \c img.back() is equivalent to img[img.size() - 1], or + img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& back() { + return *(_data + size() - 1); + } + + //! Return a reference to the last pixel value \const. + const T& back() const { + return *(_data + size() - 1); + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to a specified default value in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note + - Writing \c img.at(offset,out_value) is similar to img[offset], except that if \c offset + is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value + is safely returned instead. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + **/ + T& at(const int offset, const T& out_value) { + return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const. + T at(const int offset, const T& out_value) const { + return (offset<0 || offset>=(int)size())?out_value:(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to the nearest pixel location in the image instance in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \note + - Similar to at(int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified offset, i.e. + - If \c offset<0, then \c img[0] is returned. + - If \c offset>=img.size(), then \c img[img.size() - 1] is returned. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int). + **/ + T& at(const int offset) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + T& _at(const int offset) { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const. + const T& at(const int offset) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + const T& _at(const int offset) const { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to a specified default value in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value + \c out_value. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const. + T atX(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || x>=width())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified X-coordinate. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _at(int,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + T& _atX(const int x, const int y=0, const int z=0, const int c=0) { + return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const. + const T& atX(const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const { + return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates. + **/ + T& atXY(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const. + T atXY(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXY(int,int,int,int). + **/ + T& atXY(const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + T& _atXY(const int x, const int y, const int z=0, const int c=0) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1),z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const. + const T& atXY(const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + const T& _atXY(const int x, const int y, const int z=0, const int c=0) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1),z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on + X,Y and Z-coordinates. + **/ + T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const. + T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXYZ(int,int,int,int). + **/ + T& atXYZ(const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + T& _atXYZ(const int x, const int y, const int z, const int c=0) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1),c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const. + const T& atXYZ(const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + const T& _atXYZ(const int x, const int y, const int z, const int c=0) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1),c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all + X,Y,Z and C-coordinates. + **/ + T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions \const. + T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value: + (*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXYZC(int,int,int,int). + **/ + T& atXYZC(const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + T& _atXYZC(const int x, const int y, const int z, const int c) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1), + cimg::cut(c,0,spectrum() - 1)); + } + + //! Access to a pixel value, using Neumann boundary conditions \const. + const T& atXYZC(const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + const T& _atXYZC(const int x, const int y, const int z, const int c) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1), + cimg::cut(c,0,spectrum() - 1)); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by + a linear interpolation along the X-axis, if corresponding coordinates are not integers. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1; + const float + dx = fx - x; + const Tfloat + Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access along + the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns + the value of the nearest pixel in the image instance, regarding the specified X-coordinate. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX(): Empty instance.", + cimg_instance); + + return _linear_atX(fx,y,z,c); + } + + Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx - x; + const unsigned int + nx = dx>0?x + 1:x; + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate. + Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX_p(): Empty instance.", + cimg_instance); + + return _linear_atX_p(fx,y,z,c); + } + + Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx - x; + const unsigned int + nx = cimg::mod(x + 1,_width); + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved both for X and Y-coordinates. + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1; + const float + dx = fx - x, + dy = fy - y; + const Tfloat + Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), + Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXY(float,float,int,int). + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY(): Empty instance.", + cimg_instance); + + return _linear_atXY(fx,fy,z,c); + } + + Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx - x, + dy = nfy - y; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y; + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates. + Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY_p(): Empty instance.", + cimg_instance); + + return _linear_atXY_p(fx,fy,z,c); + } + + Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx - x, + dy = nfy - y; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height); + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved both for X,Y and Z-coordinates. + **/ + Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z; + const Tfloat + Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), + Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), + Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), + Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXYZ(float,float,float,int). + **/ + Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ(): Empty instance.", + cimg_instance); + + return _linear_atXYZ(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = cimg::cut(fz,0,depth() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y, + nz = dz>0?z + 1:z; + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates. + Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ_p(): Empty instance.", + cimg_instance); + + return _linear_atXYZ_p(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f), + nfz = cimg::mod(fz,_depth - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height), + nz = cimg::mod(z + 1,_depth); + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved for all X,Y,Z and C-coordinates. + **/ + Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1, + c = (int)fc - (fc>=0?0:1), nc = c + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z, + dc = fc - c; + const Tfloat + Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value), + Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value), + Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value), + Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value), + Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value), + Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value), + Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value), + Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn -Icccc); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved for all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXYZC(float,float,float,float). + **/ + Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC(): Empty instance.", + cimg_instance); + + return _linear_atXYZC(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = cimg::cut(fz,0,depth() - 1), + nfc = cimg::cut(fc,0,spectrum() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y, + nz = dz>0?z + 1:z, + nc = dc>0?c + 1:c; + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn - Icccc); + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates. + Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC_p(): Empty instance.", + cimg_instance); + + return _linear_atXYZC_p(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f), + nfz = cimg::mod(fz,_depth - 0.5f), + nfc = cimg::mod(fc,_spectrum - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height), + nz = cimg::mod(z + 1,_depth), + nc = cimg::mod(c + 1,_spectrum); + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn - Icccc); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + The cubic interpolation uses Hermite splines. + \param fx d X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is + approximated by a \e cubic interpolation along the X-axis. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2; + const float + dx = fx - x; + const Tfloat + Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value), + In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atX(fx,y,z,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access + along the X-axis. The cubic interpolation uses Hermite splines. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is + approximated by a cubic interpolation along the X-axis. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX(): Empty instance.", + cimg_instance); + return _cubic_atX(fx,y,z,c); + } + + Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1); + const int + x = (int)nfx; + const float + dx = nfx - x; + const int + px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2; + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atX_c(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX(fx,y,z,c)); + } + + T _cubic_atX_c(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX(fx,y,z,c)); + } + + //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate. + Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX_p(): Empty instance.", + cimg_instance); + return _cubic_atX_p(fx,y,z,c); + } + + Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f); + const int + x = (int)nfx; + const float + dx = nfx - x; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()); + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atX_pc(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX_p(fx,y,z,c)); + } + + T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX_p(fx,y,z,c)); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X and Y-coordinates. + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, + y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2; + const float dx = fx - x, dy = fy - y; + const Tfloat + Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), + Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value), + Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), + Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), + Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved for both X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXY(float,float,int,int). + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY(): Empty instance.", + cimg_instance); + return _cubic_atXY(fx,fy,z,c); + } + + Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), + nfy = cimg::type::is_nan(fy)?0:cimg::cut(fy,0,height() - 1); + const int x = (int)nfx, y = (int)nfy; + const float dx = nfx - x, dy = nfy - y; + const int + px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2, + py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2; + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), + Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), + Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), + Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c)); + } + + T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY(fx,fy,z,c)); + } + + //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates. + Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY_p(): Empty instance.", + cimg_instance); + return _cubic_atXY_p(fx,fy,z,c); + } + + Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f), + nfy = cimg::type::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f); + const int x = (int)nfx, y = (int)nfy; + const float dx = nfx - x, dy = nfy - y; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()), + py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()); + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), + Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), + Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), + Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY_p(fx,fy,z,c)); + } + + T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY_p(fx,fy,z,c)); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, + y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2, + z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2; + const float dx = fx - x, dy = fy - y, dz = fz - z; + const Tfloat + Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value), + Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value), + Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value), + Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value), + Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value), + Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value), + Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), + Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value), + Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value), + Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value), + Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), + Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value), + Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value), + Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value), + Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value), + Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value), + Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay + in the min/max range of the datatype \c T. + **/ + T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ(): Empty instance.", + cimg_instance); + return _cubic_atXYZ(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), + nfy = cimg::type::is_nan(fy)?0:cimg::cut(fy,0,height() - 1), + nfz = cimg::type::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1); + const int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2, + py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2, + pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2; + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c)); + } + + T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ(fx,fy,fz,c)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ_p(): Empty instance.", + cimg_instance); + return _cubic_atXYZ_p(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f), + nfy = cimg::type::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f), + nfz = cimg::type::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f); + const int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()), + py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()), + pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth()); + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ_p(fx,fy,fz,c)); + } + + T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ_p(fx,fy,fz,c)); + } + + //! Set pixel value, using linear interpolation for the X-coordinates. + /** + Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that + the value is spread amongst several neighbors if the pixel coordinates are float-valued. + \param value Pixel value to set. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image + pixel(s). + \return A reference to the current image instance. + \note + - Calling this method with out-of-bounds coordinates does nothing. + **/ + CImg& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1; + const float + dx = fx - x; + if (y>=0 && y=0 && z=0 && c=0 && x=0 && nx& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1; + const float + dx = fx - x, + dy = fy - y; + if (z>=0 && z=0 && c=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z; + if (c>=0 && c=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx image whose buffer data() is a \c char* string describing the list of all pixel values + of the image instance (written in base 10), separated by specified \c separator character. + \param separator A \c char character which specifies the separator between values in the returned C-string. + \param max_size Maximum size of the returned image (or \c 0 if no limits are set). + \param format For float/double-values, tell the printf format used to generate the text representation + of the numbers (or \c 0 for default representation). + \note + - The returned image is never empty. + - For an empty image instance, the returned string is "". + - If \c max_size is equal to \c 0, there are no limits on the size of the returned string. + - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off + and terminated by character \c '\0'. In that case, the returned image size is max_size + 1. + **/ + CImg value_string(const char separator=',', const unsigned int max_size=0, + const char *const format=0) const { + if (is_empty() || max_size==1) return CImg(1,1,1,1,0); + CImgList items; + CImg s_item(256); *s_item = 0; + const T *ptrs = _data; + unsigned int string_size = 0; + const char *const _format = format?format:cimg::type::format(); + for (ulongT off = 0, siz = size(); off::format(*(ptrs++))); + CImg item(s_item._data,printed_size); + item[printed_size - 1] = separator; + item.move_to(items); + if (max_size) string_size+=printed_size; + } + CImg res; + (items>'x').move_to(res); + if (max_size && res._width>=max_size) res.crop(0,max_size - 1); + res.back() = 0; + return res; + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Test shared state of the pixel buffer. + /** + Return \c true if image instance has a shared memory buffer, and \c false otherwise. + \note + - A shared image do not own his pixel buffer data() and will not deallocate it on destruction. + - Most of the time, a \c CImg image instance will \e not be shared. + - A shared image can only be obtained by a limited set of constructors and methods (see list below). + **/ + bool is_shared() const { + return _is_shared; + } + + //! Test if image instance is empty. + /** + Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions + \c 0 x \c 0 x \c 0 x \c 0 and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise. + **/ + bool is_empty() const { + return !(_data && _width && _height && _depth && _spectrum); + } + + //! Test if image instance contains a 'inf' value. + /** + Return \c true, if image instance contains a 'inf' value, and \c false otherwise. + **/ + bool is_inf() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_inf((float)*p)) return true; + return false; + } + + //! Test if image instance contains a NaN value. + /** + Return \c true, if image instance contains a NaN value, and \c false otherwise. + **/ + bool is_nan() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_nan((float)*p)) return true; + return false; + } + + //! Test if image width is equal to specified value. + bool is_sameX(const unsigned int size_x) const { + return _width==size_x; + } + + //! Test if image width is equal to specified value. + template + bool is_sameX(const CImg& img) const { + return is_sameX(img._width); + } + + //! Test if image width is equal to specified value. + bool is_sameX(const CImgDisplay& disp) const { + return is_sameX(disp._width); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const unsigned int size_y) const { + return _height==size_y; + } + + //! Test if image height is equal to specified value. + template + bool is_sameY(const CImg& img) const { + return is_sameY(img._height); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const CImgDisplay& disp) const { + return is_sameY(disp._height); + } + + //! Test if image depth is equal to specified value. + bool is_sameZ(const unsigned int size_z) const { + return _depth==size_z; + } + + //! Test if image depth is equal to specified value. + template + bool is_sameZ(const CImg& img) const { + return is_sameZ(img._depth); + } + + //! Test if image spectrum is equal to specified value. + bool is_sameC(const unsigned int size_c) const { + return _spectrum==size_c; + } + + //! Test if image spectrum is equal to specified value. + template + bool is_sameC(const CImg& img) const { + return is_sameC(img._spectrum); + } + + //! Test if image width and height are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified. + **/ + bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const { + return _width==size_x && _height==size_y; + } + + //! Test if image width and height are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameY(const CImg&) const are both verified. + **/ + template + bool is_sameXY(const CImg& img) const { + return is_sameXY(img._width,img._height); + } + + //! Test if image width and height are the same as that of an existing display window. + /** + Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified. + **/ + bool is_sameXY(const CImgDisplay& disp) const { + return is_sameXY(disp._width,disp._height); + } + + //! Test if image width and depth are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const { + return _width==size_x && _depth==size_z; + } + + //! Test if image width and depth are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXZ(const CImg& img) const { + return is_sameXZ(img._width,img._depth); + } + + //! Test if image width and spectrum are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const { + return _width==size_x && _spectrum==size_c; + } + + //! Test if image width and spectrum are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXC(const CImg& img) const { + return is_sameXC(img._width,img._spectrum); + } + + //! Test if image height and depth are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const { + return _height==size_y && _depth==size_z; + } + + //! Test if image height and depth are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameYZ(const CImg& img) const { + return is_sameYZ(img._height,img._depth); + } + + //! Test if image height and spectrum are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const { + return _height==size_y && _spectrum==size_c; + } + + //! Test if image height and spectrum are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYC(const CImg& img) const { + return is_sameYC(img._height,img._spectrum); + } + + //! Test if image depth and spectrum are equal to specified values. + /** + Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const { + return _depth==size_z && _spectrum==size_c; + } + + //! Test if image depth and spectrum are the same as that of another image. + /** + Test if is_sameZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameZC(const CImg& img) const { + return is_sameZC(img._depth,img._spectrum); + } + + //! Test if image width, height and depth are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const { + return is_sameXY(size_x,size_y) && _depth==size_z; + } + + //! Test if image width, height and depth are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXYZ(const CImg& img) const { + return is_sameXYZ(img._width,img._height,img._depth); + } + + //! Test if image width, height and spectrum are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const { + return is_sameXY(size_x,size_y) && _spectrum==size_c; + } + + //! Test if image width, height and spectrum are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYC(const CImg& img) const { + return is_sameXYC(img._width,img._height,img._spectrum); + } + + //! Test if image width, depth and spectrum are equal to specified values. + /** + Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const { + return is_sameXZ(size_x,size_z) && _spectrum==size_c; + } + + //! Test if image width, depth and spectrum are the same as that of another image. + /** + Test if is_sameXZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXZC(const CImg& img) const { + return is_sameXZC(img._width,img._depth,img._spectrum); + } + + //! Test if image height, depth and spectrum are equal to specified values. + /** + Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const { + return is_sameYZ(size_y,size_z) && _spectrum==size_c; + } + + //! Test if image height, depth and spectrum are the same as that of another image. + /** + Test if is_sameYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYZC(const CImg& img) const { + return is_sameYZC(img._height,img._depth,img._spectrum); + } + + //! Test if image width, height, depth and spectrum are equal to specified values. + /** + Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both + verified. + **/ + bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c) const { + return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c; + } + + //! Test if image width, height, depth and spectrum are the same as that of another image. + /** + Test if is_sameXYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYZC(const CImg& img) const { + return is_sameXYZC(img._width,img._height,img._depth,img._spectrum); + } + + //! Test if specified coordinates are inside image bounds. + /** + Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance, + and \c false otherwise. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Return \c true only if all these conditions are verified: + - The image instance is \e not empty. + - 0<=x<=\ref width() - 1. + - 0<=y<=\ref height() - 1. + - 0<=z<=\ref depth() - 1. + - 0<=c<=\ref spectrum() - 1. + **/ + bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const { + return !is_empty() && x>=0 && x=0 && y=0 && z=0 && c img(100,100,1,3); // Construct a 100x100 RGB color image + const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0) + unsigned int x,y,z,c; + if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates + std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n", + offset,x,y,z,c); + } + \endcode + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z, t& c) const { + const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = (ulongT)(ppixel - _data); + const ulongT nc = off/whd; + off%=whd; + const ulongT nz = off/wh; + off%=wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc; + return true; + } + + //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z) const { + const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((ulongT)(ppixel - _data))%whd; + const ulongT nz = off/wh; + off%=wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; z = (t)nz; + return true; + } + + //! Test if pixel value is inside image bounds and get its X and Y-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y) const { + const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((unsigned int)(ppixel - _data))%wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; + return true; + } + + //! Test if pixel value is inside image bounds and get its X-coordinate. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set. + **/ + template + bool contains(const T& pixel, t& x) const { + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false; + x = (t)(((ulongT)(ppixel - _data))%_width); + return true; + } + + //! Test if pixel value is inside image bounds. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set. + **/ + bool contains(const T& pixel) const { + const T *const ppixel = &pixel; + return !is_empty() && ppixel>=_data && ppixel<_data + size(); + } + + //! Test if pixel buffers of instance and input images overlap. + /** + Return \c true, if pixel buffers attached to image instance and input image \c img overlap, + and \c false otherwise. + \param img Input image to compare with. + \note + - Buffer overlapping may happen when manipulating \e shared images. + - If two image buffers overlap, operating on one of the image will probably modify the other one. + - Most of the time, \c CImg instances are \e non-shared and do not overlap between each others. + \par Example + \code + const CImg + img1("reference.jpg"), // Load RGB-color image + img2 = img1.get_shared_channel(1); // Get shared version of the green channel + if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps + std::printf("Buffers overlap!\n"); + } + \endcode + **/ + template + bool is_overlapped(const CImg& img) const { + const ulongT csiz = size(), isiz = img.size(); + return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz)); + } + + //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object. + /** + Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a + valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image. + \param primitives List of primitives of the 3D object. + \param colors List of colors of the 3D object. + \param opacities List (or image) of opacities of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of + each 3D object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + template + bool is_object3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true, + char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check consistency for the particular case of an empty 3D object. + if (is_empty()) { + if (primitives || colors || opacities) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines no vertices but %u primitives, " + "%u colors and %lu opacities", + _width,primitives._width,primitives._width, + colors._width,(unsigned long)opacities.size()); + return false; + } + return true; + } + + // Check consistency of vertices. + if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)", + _width,primitives._width,_width,_height,_depth,_spectrum); + return false; + } + if (colors._width>primitives._width + 1) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines %u colors", + _width,primitives._width,colors._width); + return false; + } + if (opacities.size()>primitives._width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines %lu opacities", + _width,primitives._width,(unsigned long)opacities.size()); + return false; + } + if (!full_check) return true; + + // Check consistency of primitives. + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + const unsigned int psiz = (unsigned int)primitive.size(); + switch (psiz) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)primitive(0); + if (i0>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex index %u in " + "point primitive [%u]", + _width,primitives._width,i0,l); + return false; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + if (i0>=_width || i1>=_width || i2>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + _width,primitives._width,i0,i1,i2,l); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = (unsigned int)primitive(3); + if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + _width,primitives._width,i0,i1,i2,i3,l); + return false; + } + } break; + default : + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines an invalid primitive [%u] of size %u", + _width,primitives._width,l,(unsigned int)psiz); + return false; + } + } + + // Check consistency of colors. + cimglist_for(colors,c) { + const CImg& color = colors[c]; + if (!color) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines no color for primitive [%u]", + _width,primitives._width,c); + return false; + } + } + + // Check consistency of light texture. + if (colors._width>primitives._width) { + const CImg &light = colors.back(); + if (!light || light._depth>1) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", + _width,primitives._width,light._width, + light._height,light._depth,light._spectrum); + return false; + } + } + + return true; + } + + //! Test if image instance represents a valid serialization of a 3D object. + /** + Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise. + \param full_check Tells if full checking of the instance must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of + each 3D object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + bool is_CImg3d(const bool full_check=true, char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check instance dimension and header. + if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { + if (error_message) cimg_sprintf(error_message, + "CImg3d has invalid dimensions (%u,%u,%u,%u)", + _width,_height,_depth,_spectrum); + return false; + } + const T *ptrs = _data, *const ptre = end(); + if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || + !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { + if (error_message) cimg_sprintf(error_message, + "CImg3d header not found"); + return false; + } + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + + // Check consistency of number of vertices / primitives. + if (!full_check) { + const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives; + if (_data + minimal_size>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", + nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); + return false; + } + } + + // Check consistency of vertex data. + if (!nb_points) { + if (nb_primitives) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no vertices but %u primitives", + nb_points,nb_primitives,nb_primitives); + return false; + } + if (ptrs!=ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) is an empty object but contains %u value%s " + "more than expected", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); + return false; + } + return true; + } + if (ptrs + 3*nb_points>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines only %u vertices data", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); + return false; + } + ptrs+=3*nb_points; + + // Check consistency of primitive data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines %u vertices but no primitive", + nb_points,nb_primitives,nb_points); + return false; + } + + if (!full_check) return true; + + for (unsigned int p = 0; p=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]", + nb_points,nb_primitives,i0,p); + return false; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + ptrs+=3; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==6) ptrs+=4; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==9) ptrs+=6; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,p); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)), + i3 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==12) ptrs+=8; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,i3,p); + return false; + } + } break; + default : + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", + nb_points,nb_primitives,p,nb_inds); + return false; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); + return false; + } + } + + // Check consistency of color data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no color/texture data", + nb_points,nb_primitives); + return false; + } + for (unsigned int c = 0; c=c) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,c); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); + return false; + } + } + + // Check consistency of opacity data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no opacity data", + nb_points,nb_primitives); + return false; + } + for (unsigned int o = 0; o=o) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared opacity index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,o); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", + nb_points,nb_primitives,o); + return false; + } + } + + // Check end of data. + if (ptrs1?"s":""); + return false; + } + return true; + } + + static bool _is_CImg3d(const T val, const char c) { + return val>=(T)c && val<(T)(c + 1); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + // Define the math formula parser/compiler and expression evaluator. + struct _cimg_math_parser { + CImg mem; + CImg memtype, memmerge; + CImgList _code, &code, code_begin, code_end, + _code_begin_t, &code_begin_t, _code_end_t, &code_end_t; + CImg opcode; + const CImg *p_code_end, *p_code; + const CImg *const p_break; + + CImg expr, pexpr; + const CImg& imgin; + const CImgList& listin; + CImg &imgout; + CImgList& listout; + + CImg _img_stats, &img_stats, constcache_vals; + CImgList _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm; + CImg mem_img_stats, constcache_inds; + + CImg level, variable_pos, reserved_label; + CImgList variable_def, macro_def, macro_body; + CImgList macro_body_is_string; + char *user_macro; + + unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type, + constcache_size; + bool is_parallelizable, is_end_code, is_fill, return_new_comp, need_input_copy; + double *result; + cimg_uint64 rng; + const char *const calling_function, *s_op, *ss_op; + typedef double (*mp_func)(_cimg_math_parser&); + +#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value? +#define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value? +#define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? +#define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)? +#define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? +#define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN) +#define _cimg_mp_calling_function s_calling_function()._data +#define _cimg_mp_op(s) s_op = s; ss_op = ss +#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char) +#define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char) +#define _cimg_mp_check_constant_index(arg) check_constant_index(arg,ss,se,saved_char) +#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) +#define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char) +#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) +#define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; } +#define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan) +#define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val))) +#define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) +#define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) +#define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) +#define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3)) +#define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4)) +#define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5)) +#define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6)) +#define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7)) +#define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1)) +#define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2)) +#define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) +#define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) +#define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) +#define _cimg_mp_strerr \ + *se = saved_char; \ + for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \ + if (*s0==';') ++s0; \ + while (cimg::is_blank(*s0)) ++s0; \ + cimg::strellipsize(s0,64) + + // Constructors / Destructors. + ~_cimg_math_parser() { + cimg::srand(rng); + } + + _cimg_math_parser(const char *const expression, const char *const funcname=0, + const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0, + const bool _is_fill=false): + code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), + p_break((CImg*)(cimg_ulong)-2), + imgin(img_input),listin(list_inputs?*list_inputs:CImgList::const_empty()), + imgout(img_output?*img_output:CImg::empty()),listout(list_outputs?*list_outputs:CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0), + mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0), + constcache_size(0),is_parallelizable(true),is_fill(_is_fill),need_input_copy(false), + rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") { + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + if (!expression || !*expression) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Empty expression.", + pixel_type(),_cimg_mp_calling_function); + const char *_expression = expression; + while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression; + CImg::string(_expression).move_to(expr); + char *ps = &expr.back() - 1; + while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps; + *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1); + + // Ease the retrieval of previous non-space characters afterwards. + pexpr.assign(expr._width); + char c, *pe = pexpr._data; + for (ps = expr._data, c = ' '; *ps; ++ps) { + if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' '; + *(pe++) = c; + } + *pe = 0; + level = get_level(expr); + + // Init constant values. +#define _cimg_mp_interpolation (reserved_label[30]!=~0U?reserved_label[30]:0) +#define _cimg_mp_boundary (reserved_label[31]!=~0U?reserved_label[31]:0) +#define _cimg_mp_slot_t 17 +#define _cimg_mp_slot_nan 29 +#define _cimg_mp_slot_x 30 +#define _cimg_mp_slot_y 31 +#define _cimg_mp_slot_z 32 +#define _cimg_mp_slot_c 33 + + mem.assign(96); + for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10 + for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5 + mem[16] = 0.5; + mem[_cimg_mp_slot_t] = 0; // thread_id + mem[18] = (double)imgin._width; // w + mem[19] = (double)imgin._height; // h + mem[20] = (double)imgin._depth; // d + mem[21] = (double)imgin._spectrum; // s + mem[22] = (double)imgin._is_shared; // r + mem[23] = (double)imgin._width*imgin._height; // wh + mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd + mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds + mem[26] = (double)listin._width; // l + mem[27] = std::exp(1.); // e + mem[28] = cimg::PI; // pi + mem[_cimg_mp_slot_nan] = cimg::type::nan(); // nan + + // Set value property : + // { -1 = reserved (e.g. variable) | 0 = computation value | + // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }. + memtype.assign(mem._width,1,1,1,0); + for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1; + memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = + memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1; + mempos = _cimg_mp_slot_c + 1; + variable_pos.assign(8); + + reserved_label.assign(128,1,1,1,~0U); + // reserved_label[0-31] are used to store the memory index of these variables: + // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, + // [8] = is, [9] = ip, [10] = ic, [11] = in, [12] = xm, [13] = ym, [14] = zm, [15] = cm, [16] = xM, + // [17] = yM, [18] = zM, [19] = cM, [20] = i0...[29] = i9, [30] = interpolation, [31] = boundary + + // Compile expression into a sequence of opcodes. + s_op = ""; ss_op = expr._data; + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false); + if (!_cimg_mp_is_constant(ind_result)) { + if (_cimg_mp_is_vector(ind_result)) + CImg(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true). + fill(cimg::type::nan()); + else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type::nan(); + } + + // Free resources used for compiling expression and prepare evaluation. + result_dim = _cimg_mp_size(ind_result); + if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1); + result = mem._data + ind_result; + memtype.assign(); + constcache_vals.assign(); + constcache_inds.assign(); + level.assign(); + variable_pos.assign(); + reserved_label.assign(); + expr.assign(); + pexpr.assign(); + opcode.assign(); + opcode._is_shared = true; + + // Execute begin() bloc if any specified. + if (code_begin) { + mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_begin.end(); + for (p_code = code_begin; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + p_code_end = code.end(); + } + + _cimg_math_parser(): + code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), + p_code_end(0),p_break((CImg*)(cimg_ulong)-2), + imgin(CImg::const_empty()),listin(CImgList::const_empty()), + imgout(CImg::empty()),listout(CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0), + result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false), + need_input_copy(false),rng(0),calling_function(0) { + mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() + result = mem._data; + } + + _cimg_math_parser(const _cimg_math_parser& mp): + mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t), + p_code_end(mp.p_code_end),p_break(mp.p_break), + imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout), + img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm), + debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0), + is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill), + need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)), + rng((cimg::_rand(),cimg::rng())),calling_function(0) { + +#if cimg_use_openmp!=0 + mem[_cimg_mp_slot_t] = omp_get_thread_num(); + rng+=omp_get_thread_num(); +#endif + opcode.assign(); + opcode._is_shared = true; + } + + // Compilation procedure. + unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref, + const bool is_critical) { + if (depth>256) { + cimg::strellipsize(expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Call stack overflow (infinite recursion?), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + char c1, c2; + + // Simplify expression when possible. + do { + c2 = 0; + if (ssss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; + } + while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { + ++ss; --se; c2 = 1; + } + } while (c2 && ss::%s: %s%s Missing %s, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + *s_op=='F'?"argument":"item", + (ss_op - 4)>expr._data?"...":"", + (ss_op - 4)>expr._data?ss_op - 4:expr._data, + ss_op + std::strlen(ss_op)<&expr.back()?"...":""); + } + + static const size_t siz_ref = 7*sizeof(unsigned int); + const char *const previous_s_op = s_op, *const previous_ss_op = ss_op; + const unsigned int depth1 = depth + 1; + unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; + char + *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, + *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4, + *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8, + *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; + double val = 0, val1, val2; + mp_func op; + return_new_comp = false; + + // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value + // linked to the returned memory slot (reference that cannot be determined at compile time). + // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | + // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | + // 5 = image value as a vector (coordinates) }. + // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: + // When p_ref[0]==0, p_ref is actually unlinked. + // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ]. + // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ]. + // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ]. + if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; } + + const char saved_char = *se; *se = 0; + const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; + bool is_sth, is_relative; + CImg ref; + CImg variable_name; + CImgList l_opcode; + + // Look for a single value or a pre-defined variable. + int nb = 0; + s = ss + (*ss=='+' || *ss=='-'?1:0); + if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf + is_sth = *ss=='-'; + if (!cimg::strcasecmp(s,"inf")) { val = cimg::type::inf(); nb = 1; } + else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); nb = 1; } + if (nb==1 && is_sth) val = -val; + } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number + is_sth = *ss=='-'; + if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) { + nb = 1; + val = (double)arg1; + if (is_sth) val = -val; + } + } + if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); + if (nb==1) _cimg_mp_constant(val); + if (nb==2 && sep=='%') _cimg_mp_constant(val/100); + + if (ss1==se) switch (*ss) { // One-char reserved variable + case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c); + case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20); + case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27); + case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19); + case 'k' : + if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']); + pos = get_mem_img_index(); + if (pos!=~0U) _cimg_mp_return(pos); + _cimg_mp_return_nan(); + case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26); + case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22); + case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21); + case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t); + case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18); + case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x); + case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y); + case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z); + case 'u' : + if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']); + _cimg_mp_scalar2(mp_u,0,1); + case 'g' : + if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']); + _cimg_mp_scalar0(mp_g); + case 'i' : + if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']); + _cimg_mp_scalar0(mp_i); + case 'I' : + _cimg_mp_op("Variable 'I'"); + if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']); + if (!imgin._spectrum) _cimg_mp_return(0); + need_input_copy = true; + pos = vector(imgin._spectrum); + CImg::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + case 'R' : + if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0); + case 'G' : + if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0); + case 'B' : + if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0); + case 'A' : + if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0); + } + else if (ss2==se) { // Two-chars reserved variable + arg1 = arg2 = ~0U; + if (*ss=='w' && *ss1=='h') // wh + _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23); + if (*ss=='p' && *ss1=='i') // pi + _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28); + if (*ss=='i') { + if (*ss1>='0' && *ss1<='9') { // i0...i9 + pos = 20 + *ss1 - '0'; + if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0); + } + switch (*ss1) { + case 'm' : arg1 = 4; arg2 = 0; break; // im + case 'M' : arg1 = 5; arg2 = 1; break; // iM + case 'a' : arg1 = 6; arg2 = 2; break; // ia + case 'v' : arg1 = 7; arg2 = 3; break; // iv + case 's' : arg1 = 8; arg2 = 12; break; // is + case 'p' : arg1 = 9; arg2 = 13; break; // ip + case 'c' : // ic + if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); + if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; + _cimg_mp_return(mem_img_median); + break; + case 'n' : // in + if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); + if (mem_img_norm==~0U) mem_img_norm = imgin?constant(imgin.magnitude()):0; + _cimg_mp_return(mem_img_norm); + } + } + else if (*ss1=='m') switch (*ss) { + case 'x' : arg1 = 12; arg2 = 4; break; // xm + case 'y' : arg1 = 13; arg2 = 5; break; // ym + case 'z' : arg1 = 14; arg2 = 6; break; // zm + case 'c' : arg1 = 15; arg2 = 7; break; // cm + } + else if (*ss1=='M') switch (*ss) { + case 'x' : arg1 = 16; arg2 = 8; break; // xM + case 'y' : arg1 = 17; arg2 = 9; break; // yM + case 'z' : arg1 = 18; arg2 = 10; break; // zM + case 'c' : arg1 = 19; arg2 = 11; break; // cM + } + if (arg1!=~0U) { + if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); + if (!img_stats) { + img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); + mem_img_stats.assign(1,14,1,1,~0U); + } + if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); + _cimg_mp_return(mem_img_stats[arg2]); + } + } else if (ss3==se) { // Three-chars reserved variable + if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd + _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24); + } else if (ss4==se) { // Four-chars reserved variable + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds + _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25); + } + + pos = ~0U; + is_sth = false; + for (s0 = ss, s = ss1; s='i'?1:3,0); + if (_cimg_mp_is_vector(arg2)) { + if (p1!=~0U) { + _cimg_mp_check_constant_index(p1); + p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + p2 = listin[p3]._spectrum; + } else p2 = imgin._spectrum; + if (!p2) _cimg_mp_return(0); + _cimg_mp_check_type(arg2,2,2,p2); + } else p2 = 0; + + if (p_ref) { + *p_ref = _cimg_mp_is_vector(arg2)?4:2; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + if (_cimg_mp_is_vector(arg2)) + set_reserved_vector(arg2); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1; + } + + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg1,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value + if (!is_critical) is_parallelizable = false; + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0='i'?1:3,0); + if (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + } else if (s1='i') + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg5,p1,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg5,p1,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg5); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg5,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg5,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); + } + _cimg_mp_return(arg5); + } + } + + // Assign vector value (direct). + if (l_variable_name>3 && *ve1==']' && *ss!='[') { + s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss && is_varname(ss,s0 - ss)) { + variable_name[s0 - ss] = 0; // Remove brackets in variable name + get_variable_pos(variable_name,arg1,arg2); + arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot + if (arg1==~0U || _cimg_mp_is_scalar(arg1)) + compile(ss,s0,depth1,0,is_critical); // Variable does not exist or is not a vector -> error + + arg2 = compile(++s0,ve1,depth1,0,is_critical); // Index + arg3 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + _cimg_mp_check_type(arg3,2,1,0); + + if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) { + arg1+=nb + 1; + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + _cimg_mp_return(arg1); + } + compile(ss,s,depth1,0,is_critical); // Out-of-bounds reference -> error + } + + // Case of non-constant index -> return assigned value + linked reference + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + } + CImg::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg3); + } + } + + // Assign user-defined macro. + if (l_variable_name>2 && *ve1==')' && *ss!='(') { + s0 = ve1; while (s0>ss && *s0!='(') --s0; + if (is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) && + std::strncmp(variable_name,"print(",6)) { // Valid macro name + s0 = variable_name._data + (s0 - ss); + *s0 = 0; + s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis + CImg(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0); + ++s; while (*s && cimg::is_blank(*s)) ++s; + CImg(s,(unsigned int)(se - s + 1)).move_to(macro_body,0); + + p1 = 1; // Index of current parsed argument + for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments + if (p1>24) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro " + "definition '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && p1==1) break; // Function has no arguments + + s2 = s; // Start of the argument name + is_sth = true; // is_valid_argument_name? + if (*s>='0' && *s<='9') is_sth = false; + else for (ns = s; ns::%s: %s: %s name specified for argument %u when defining " + "macro '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + is_sth?"Empty":"Invalid",p1, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (ns==s1 || *ns==',') { // New argument found + *s3 = 0; + p2 = (unsigned int)(s3 - s2); // Argument length + for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number + if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) || + (ps + p2macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign + *(ps - 1) = (char)p1; + if (ps + p26 && !std::strncmp(variable_name,"const ",6); + s0 = variable_name._data; + if (is_const) { + s0+=6; while (cimg::is_blank(*s0)) ++s0; + variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1); + } + if (is_varname(variable_name)) { // Valid variable name + + // Assign variable (direct). + get_variable_pos(variable_name,arg1,arg2); + arg3 = compile(s + 1,se,depth1,0,is_critical); + is_sth = return_new_comp; // is arg3 a new blank object? + if (is_const) _cimg_mp_check_constant(arg3,2,0); + arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; + + if (arg1==~0U) { // Create new variable + if (_cimg_mp_is_vector(arg3)) { // Vector variable + arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3); + set_reserved_vector(arg1); // Prevent from being used in further optimization + } else { // Scalar variable + if (is_const) arg1 = arg3; + else { + arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3); + memtype[arg1] = -1; + } + } + + if (arg2!=~0U) reserved_label[arg2] = arg1; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg1; + variable_name.move_to(variable_def); + } + + } else { // Variable already exists -> assign a new value + if (is_const || _cimg_mp_is_constant(arg1)) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"already-defined ":"non-", + variable_name._data, + !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1)) { // Vector + if (_cimg_mp_is_vector(arg3)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3). + move_to(code); + } else // Scalar + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + } + return_new_comp = false; + _cimg_mp_return(arg1); + } + + // Assign lvalue (variable name was not valid for a direct assignment). + arg1 = ~0U; + is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? + if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment + + if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { + ref.assign(7); + arg1 = compile(ss,s,depth1,ref,is_critical); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + + if (*ref==1) { // Vector value (scalar): V[k] = scalar + _cimg_mp_check_type(arg2,2,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(arg2); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg3).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg2,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg2,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg3).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code); + } + + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg3,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg2,p1,arg3,arg4,arg5).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); + } + + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg2,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar + _cimg_mp_check_type(arg2,2,1,0); + CImg::vector((ulongT)mp_copy,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + } + + // No assignment expressions match -> error + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++. + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps && + level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=) + _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"); + + ref.assign(7); + arg1 = compile(ss,ns,depth1,ref,is_critical); // Vector slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Right operand + _cimg_mp_check_type(arg1,1,2,2); + _cimg_mp_check_type(arg2,2,3,2); + if (_cimg_mp_is_vector(arg2)) { // Complex **= complex + if (*ps=='*') + CImg::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code); + else if (*ps=='/') + CImg::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code); + } else { // Complex **= scalar + if (*ps=='*') { + if (arg2==1) _cimg_mp_return(arg1); + self_vector_s(arg1,mp_self_mul,arg2); + } else if (*ps=='/') { + if (arg2==1) _cimg_mp_return(arg1); + self_vector_s(arg1,mp_self_div,arg2); + } else { + if (arg2==1) _cimg_mp_return(arg1); + CImg::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code); + } + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + + } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + + _cimg_mp_return(arg1); + } + + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || + *ps=='&' || *ps=='^' || *ps=='|' || + (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && + level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) + switch (*ps) { + case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break; + case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break; + case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break; + case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break; + case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break; + case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break; + case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break; + case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break; + case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break; + default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break; + } + s1 = *ps=='>' || *ps=='<'?ns:ps; + + ref.assign(7); + arg1 = compile(ss,s1,depth1,ref,is_critical); // Variable slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to apply + + // Check for particular case to be simplified. + if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1); + if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1); + + // Apply operator on a copy to prevent modifying a constant or a variable. + if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k] += scalar + _cimg_mp_check_type(arg2,2,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(arg1); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector + else self_vector_s(arg1,op,arg2); // Vector += scalar + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar + _cimg_mp_check_type(arg2,2,1,0); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + + variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; + cimg::strpare(variable_name,false,true); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + for (s = ss1; s::vector((ulongT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') + _cimg_mp_op("Operator '||'"); + arg1 = compile(ss,s,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,1,0); + if (arg1>0 && arg1<=16) _cimg_mp_return(1); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] || mem[arg2]); + if (!arg1) _cimg_mp_return(arg2); + pos = scalar(); + CImg::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') + _cimg_mp_op("Operator '&&'"); + arg1 = compile(ss,s,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,1,0); + if (!arg1) _cimg_mp_return(0); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] && mem[arg2]); + if (arg1>0 && arg1<=16) _cimg_mp_return(arg2); + pos = scalar(); + CImg::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se2; s>ss; --s) + if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') + _cimg_mp_op("Operator '|'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + if (!arg1) _cimg_mp_return(arg2); + _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]); + if (!arg2) _cimg_mp_return(arg1); + if (!arg1) _cimg_mp_return(arg2); + _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); + } + + for (s = se2; s>ss; --s) + if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') + _cimg_mp_op("Operator '&'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]); + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') + _cimg_mp_op("Operator '!='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + if (arg1==arg2) _cimg_mp_return(0); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); + if (p1 || p2) { + if (p1 && p2 && p1!=p2) _cimg_mp_return(1); + pos = scalar(); + CImg::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); + _cimg_mp_scalar2(mp_neq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') + _cimg_mp_op("Operator '=='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + if (arg1==arg2) _cimg_mp_return(1); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); + if (p1 || p2) { + if (p1 && p2 && p1!=p2) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); + _cimg_mp_scalar2(mp_eq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') + _cimg_mp_op("Operator '<='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); + if (arg1==arg2) _cimg_mp_return(1); + _cimg_mp_scalar2(mp_lte,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') + _cimg_mp_op("Operator '>='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); + if (arg1==arg2) _cimg_mp_return(1); + _cimg_mp_scalar2(mp_gte,arg1,arg2); + } + + for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) + if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') + _cimg_mp_op("Operator '<'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) + if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>') + _cimg_mp_op("Operator '>'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); + if (arg1==arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_gt,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') + _cimg_mp_op("Operator '<<'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]); + if (!arg1) _cimg_mp_return(0); + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') + _cimg_mp_op("Operator '>>'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]); + if (!arg1) _cimg_mp_return(0); + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Addition ('+') + _cimg_mp_op("Operator '+'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (!arg2) _cimg_mp_return(arg1); + if (!arg1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]); + if (code) { // Try to spot linear case 'a*b + c' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); + _cimg_mp_return(arg3); + } + } + if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1); + if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2); + _cimg_mp_scalar2(mp_add,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Subtraction ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (!arg2) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2); + _cimg_mp_vector2_sv(mp_sub,arg1,arg2); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]); + if (!arg1) _cimg_mp_scalar1(mp_minus,arg2); + if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right), + arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code); + _cimg_mp_return(arg3); + } + } + if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1); + _cimg_mp_scalar2(mp_sub,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**') + _cimg_mp_op("Operator '**'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + if (arg1==1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') + _cimg_mp_op("Operator '//'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (!arg1) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') + _cimg_mp_op("Operator '*'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + p2 = _cimg_mp_size(arg2); + if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication + pos = vector(p2); + CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (arg1==1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + + if (code) { // Try to spot double multiplication 'a*b*c' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); + _cimg_mp_return(arg3); + } + } + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') + _cimg_mp_op("Operator '/'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (!arg1) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2, ns = se1; s>ss; --s, --ns) + if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') + _cimg_mp_op("Operator '%'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); + _cimg_mp_scalar2(mp_modulo,arg1,arg2); + } + + if (se1>ss) { + if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) { // Unary plus ('+') + _cimg_mp_op("Operator '+'"); + _cimg_mp_return(compile(ss1,se,depth1,0,is_critical)); + } + + if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); + _cimg_mp_scalar1(mp_minus,arg1); + } + + if (*ss=='!') { // Logical not ('!') + _cimg_mp_op("Operator '!'"); + if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)' + arg1 = compile(ss2,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + _cimg_mp_scalar1(mp_bool,arg1); + } + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); + _cimg_mp_scalar1(mp_logical_not,arg1); + } + + if (*ss=='~') { // Bitwise not ('~') + _cimg_mp_op("Operator '~'"); + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]); + _cimg_mp_scalar1(mp_bitwise_not,arg1); + } + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') + _cimg_mp_op("Operator '^^'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + pos = vector(2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + CImg::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); + else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); + else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + CImg::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se2; s>ss; --s) + if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') + _cimg_mp_op("Operator '^'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + switch (arg2) { + case 0 : _cimg_mp_return(1); + case 2 : _cimg_mp_scalar1(mp_sqr,arg1); + case 3 : _cimg_mp_scalar1(mp_pow3,arg1); + case 4 : _cimg_mp_scalar1(mp_pow4,arg1); + default : + if (_cimg_mp_is_constant(arg2)) { + if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); } + else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); } + } + _cimg_mp_scalar2(mp_pow,arg1,arg2); + } + } + + // Percentage computation. + if (*se1=='%') { + arg1 = compile(ss,se1,depth1,0,is_critical); + arg2 = _cimg_mp_is_constant(arg1)?0:constant(100); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment + if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { + _cimg_mp_op("Operator '++'"); + op = mp_self_increment; + } else { + _cimg_mp_op("Operator '--'"); + op = mp_self_decrement; + } + ref.assign(7); + arg1 = is_sth?compile(ss2,se,depth1,ref,is_critical): + compile(ss,se2,depth1,ref,is_critical); // Variable slot + + // Apply operator on a copy to prevent modifying a constant or a variable. + if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (is_sth) pos = arg1; // Determine return index, depending on pre/post action + else { + if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1); + else pos = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k]++ + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1,1).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(pos); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++ + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + _cimg_mp_return(pos); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++ + CImg::vector((ulongT)op,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1)); + else variable_name.assign(ss,(unsigned int)(se1 - ss)); + variable_name.back() = 0; + cimg::strpare(variable_name,false,true); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'. + if (*se1==']') { + _cimg_mp_op("Value accessor '[]'"); + + // Find opening bracket for the offset. + s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } + is_sth=s0>ss && *(s0-1)==']'; // Particular case s.a. '..[..][..]' ? + is_relative = *ss=='j' || *ss=='J'; + + if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' && + (reserved_label[(int)*ss]==~0U || + !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff), + pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); + } else { + need_input_copy = true; + CImg::vector((ulongT)(is_relative?mp_Joff:mp_Ioff), + pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' && + (reserved_label[(int)*ss]==~0U || + !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { // Vector element + arg1 = compile(ss,s0,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) { + variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + + } + s1 = s0 + 1; while (s1 sub-vector extraction + p1 = _cimg_mp_size(arg1); + arg2 = compile(++s0,s1,depth1,0,is_critical); // Starting index + s0 = ++s1; while (s0::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // One argument -> vector value reference + arg2 = compile(++s0,se1,depth1,0,is_critical); + if (_cimg_mp_is_constant(arg2)) { // Constant index + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); + variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " + "(vector '%s' has dimension %u), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data,nb, + variable_name._data,_cimg_mp_size(arg1), + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization + } + pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2); + memtype[pos] = -1; // Prevent from being used in further optimization + _cimg_mp_return(pos); + } + } + + // Look for a function call, an access to image value, or a parenthesis. + if (*se1==')') { + if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_critical)); // Simple parentheses + _cimg_mp_op("Value accessor '()'"); + is_relative = *ss=='j' || *ss=='J'; + s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } + + // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions) + if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + if (s1::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz), + pos,p1,arg1,arg2,arg3, + arg4==~0U?_cimg_mp_interpolation:arg4, + arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); + else { + need_input_copy = true; + CImg::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz), + pos,arg1,arg2,arg3, + arg4==~0U?_cimg_mp_interpolation:arg4, + arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions) + if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + if (s1::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode); + for (s = ++s2; s::vector(arg3).move_to(l_opcode); + ++p3; + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (_cimg_mp_is_constant(arg1)) { + p3-=1; // Number of args + if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1); + else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]); + if (arg1::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test) + _cimg_mp_op("Function 'breakpoint()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"bool(",5)) { // Boolean cast + _cimg_mp_op("Function 'bool()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + _cimg_mp_scalar1(mp_bool,arg1); + } + + if (!std::strncmp(ss,"begin(",6)) { // Begin + _cimg_mp_op("Function 'begin()'"); + code.swap(code_begin); + arg1 = compile(ss6,se1,depth1,p_ref,true); + code.swap(code_begin); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread + _cimg_mp_op("Function 'begin_t()'"); + code.swap(code_begin_t); + arg1 = compile(ss8,se1,depth1,p_ref,true); + code.swap(code_begin_t); + _cimg_mp_return(arg1); + } + break; + + case 'c' : + if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value + _cimg_mp_op("Function 'cabs()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0); + _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2); + } + + if (!std::strncmp(ss,"carg(",5)) { // Complex argument + _cimg_mp_op("Function 'carg()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1); + _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1); + } + + if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root + _cimg_mp_op("Function 'cbrt()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1])); + _cimg_mp_scalar1(mp_cbrt,arg1); + } + + if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate + _cimg_mp_op("Function 'cconj()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ceil(",5)) { // Ceil + _cimg_mp_op("Function 'ceil()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1])); + _cimg_mp_scalar1(mp_ceil,arg1); + } + + if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential + _cimg_mp_op("Function 'cexp()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm + _cimg_mp_op("Function 'clog()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine + _cimg_mp_op("Function 'ccos()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"csin(",5)) { // Complex sine + _cimg_mp_op("Function 'csin()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent + _cimg_mp_op("Function 'ctan()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine + _cimg_mp_op("Function 'ccosh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine + _cimg_mp_op("Function 'csinh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent + _cimg_mp_op("Function 'ctanh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"continue(",9)) { // Continue loop + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"copy(",5)) { // Memory copy + _cimg_mp_op("Function 'copy()'"); + ref.assign(14); + s1 = ss5; while (s1=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]); + } + if (_cimg_mp_is_vector(arg2)) { + if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2)); + if (!ref[7]) ++arg2; + if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]); + } + if (arg3==~0U) arg3 = 1; + if (arg4==~0U) arg4 = 1; + if (arg5==~0U) arg5 = 1; + _cimg_mp_check_type(arg3,3,1,0); + _cimg_mp_check_type(arg4,4,1,0); + _cimg_mp_check_type(arg5,5,1,0); + _cimg_mp_check_type(arg6,5,1,0); + CImg(1,22).move_to(code); + code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6); + code.back().get_shared_rows(8,21).fill(ref); + _cimg_mp_return(p1); + } + + if (!std::strncmp(ss,"cos(",4)) { // Cosine + _cimg_mp_op("Function 'cos()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); + _cimg_mp_scalar1(mp_cos,arg1); + } + + if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine + _cimg_mp_op("Function 'cosh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); + _cimg_mp_scalar1(mp_cosh,arg1); + } + + if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time) + _cimg_mp_op("Function 'critical()'"); + p1 = code._width; + arg1 = compile(ss + 9,se1,depth1,p_ref,true); + CImg::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"crop(",5)) { // Image crop + _cimg_mp_op("Function 'crop()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)); + opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode); + is_sth = true; + } else { + _cimg_mp_check_type(arg1,pos + 1,1,0); + CImg::vector(arg1).move_to(l_opcode); + } + s = ns; + } + (l_opcode>'y').move_to(opcode); + + arg1 = 0; arg2 = (p1!=~0U); + switch (opcode._height) { + case 0 : case 1 : + CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); + break; + case 2 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + 2; + break; + case 3 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); + arg1 = arg2 + 2; + break; + case 4 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). + move_to(opcode); + arg1 = arg2 + (is_sth?2:3); + break; + case 5 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). + move_to(opcode); + arg1 = arg2 + (is_sth?2:3); + break; + case 6 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + _cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + (is_sth?2:4); + break; + case 7 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + opcode[6]).move_to(opcode); + arg1 = arg2 + (is_sth?2:4); + break; + case 8 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], + opcode[7],_cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + (is_sth?2:5); + break; + case 9 : + arg1 = arg2 + (is_sth?2:5); + break; + default : // Error -> too much arguments + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too much arguments specified, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0); + _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0); + _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0); + _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0); + if (opcode[4]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3); + opcode[4] = (ulongT)mem[opcode[4]]; + } + if (opcode[5]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3); + opcode[5] = (ulongT)mem[opcode[5]]; + } + if (opcode[6]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3); + opcode[6] = (ulongT)mem[opcode[6]]; + } + if (opcode[7]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3); + opcode[7] = (ulongT)mem[opcode[7]]; + } + _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0); + + if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U || + opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) { + p2 = 0; + if (p1!=~0U) { + _cimg_mp_check_constant(p1,1,1); + p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + } + const CImg &img = p1!=~0U?listin[p2]:imgin; + if (!img) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Cannot crop empty image when " + "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width; + if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height; + if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth; + if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum; + } + + pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); + CImg::vector((ulongT)mp_crop, + pos,p1, + *opcode,opcode[1],opcode[2],opcode[3], + opcode[4],opcode[5],opcode[6],opcode[7], + opcode[8]).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cross(",6)) { // Cross product + _cimg_mp_op("Function 'cross()'"); + s1 = ss6; while (s1::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cut(",4)) { // Cut + _cimg_mp_op("Function 'cut()'"); + s1 = ss4; while (s1val2?val2:val); + } + _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); + } + + if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate + is_sth = *ss2=='n'; // is_convolve? + _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'"); + op = is_sth?mp_convolve:mp_correlate; + const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector + 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA + 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM + 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode + 11,11,11, // [15]=xcenter, [16]=ycenter, [17]=zcenter (default value:-1) + 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart + 11,11,11, // [21]=xend, [22]=yend, [23]=zend (default value: -1) + 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride + 1,1,1 }; // [27]=xdilation, [28]=ydilation, [29]=zdilation + + l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'! + CImg(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode); + + arg1 = 2; + for (s = std::strchr(ss,'(') + 1; s=opcode._height) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: %s arguments provided, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg1<12?"Not enough":"Too much", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + _cimg_mp_check_type(opcode[2],1,2,0); // A + _cimg_mp_check_constant(opcode[3],2,3); // wA + _cimg_mp_check_constant(opcode[4],3,3); // hA + _cimg_mp_check_constant(opcode[5],4,3); // dA + _cimg_mp_check_constant(opcode[6],5,3); // sA + _cimg_mp_check_type(opcode[7],6,2,0); // M + _cimg_mp_check_constant(opcode[8],7,3); // wM + _cimg_mp_check_constant(opcode[9],8,3); // hM + _cimg_mp_check_constant(opcode[10],9,3); // dM + _cimg_mp_check_constant(opcode[11],10,3); // sM + _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions + _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized + _cimg_mp_check_constant(opcode[14],13,1); // channel_mode + _cimg_mp_check_type(opcode[15],14,1,0); // xcenter + _cimg_mp_check_type(opcode[16],15,1,0); // ycenter + _cimg_mp_check_type(opcode[17],16,1,0); // zcenter + _cimg_mp_check_constant(opcode[18],17,1); // xstart + _cimg_mp_check_constant(opcode[19],18,1); // ystart + _cimg_mp_check_constant(opcode[20],19,1); // zstart + _cimg_mp_check_constant(opcode[21],20,1); // xend + _cimg_mp_check_constant(opcode[22],21,1); // yend + _cimg_mp_check_constant(opcode[23],22,1); // zend + _cimg_mp_check_constant(opcode[24],23,3); // xstride + _cimg_mp_check_constant(opcode[25],24,3); // ystride + _cimg_mp_check_constant(opcode[26],25,3); // zstride + _cimg_mp_check_type(opcode[27],26,1,0); // xdilation + _cimg_mp_check_type(opcode[28],27,1,0); // ydilation + _cimg_mp_check_type(opcode[29],28,1,0); // zdilation + + const unsigned int + wA = (unsigned int)mem[opcode[3]], + hA = (unsigned int)mem[opcode[4]], + dA = (unsigned int)mem[opcode[5]], + sA = (unsigned int)mem[opcode[6]], + wM = (unsigned int)mem[opcode[8]], + hM = (unsigned int)mem[opcode[9]], + dM = (unsigned int)mem[opcode[10]], + sM = (unsigned int)mem[opcode[11]], + channel_mode = (unsigned int)mem[opcode[14]], + xstart = std::min((unsigned int)mem[opcode[18]],wA - 1), + ystart = std::min((unsigned int)mem[opcode[19]],hA - 1), + zstart = std::min((unsigned int)mem[opcode[20]],dA - 1), + xend = std::min((unsigned int)mem[opcode[21]],wA - 1), + yend = std::min((unsigned int)mem[opcode[22]],hA - 1), + zend = std::min((unsigned int)mem[opcode[23]],dA - 1); + + if (xstart>xend || ystart>yend || zstart>zend) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid xyz-start/end arguments " + "(start = (%u,%u,%u), end = (%u,%u,%u)), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + xstart,ystart,zstart,xend,yend,zend, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + const float + xstride = (float)mem[opcode[24]], + ystride = (float)mem[opcode[25]], + zstride = (float)mem[opcode[26]]; + + if (xstride<=0 || ystride<=0 || zstride<=0) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + xstride,ystride,zstride, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + arg2 = 1 + (unsigned int)std::floor((xend - xstart)/xstride); + arg3 = 1 + (unsigned int)std::floor((yend - ystart)/ystride); + arg4 = 1 + (unsigned int)std::floor((zend + zstart)/zstride); + arg5 = channel_mode==0?sM:channel_mode==1?std::max(sA,sM):sA*sM; + + opcode[1] = pos = vector(arg2*arg3*arg4*arg5); + opcode[3] = (ulongT)wA; + opcode[4] = (ulongT)hA; + opcode[5] = (ulongT)dA; + opcode[6] = (ulongT)sA; + opcode[8] = (ulongT)wM; + opcode[9] = (ulongT)hM; + opcode[10] = (ulongT)dM; + opcode[11] = (ulongT)sM; + opcode[14] = (ulongT)channel_mode; + opcode[18] = (ulongT)xstart; + opcode[19] = (ulongT)ystart; + opcode[20] = (ulongT)zstart; + opcode[21] = (ulongT)xend; + opcode[22] = (ulongT)yend; + opcode[23] = (ulongT)zend; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'd' : + if (*ss1=='(') { // Image depth + _cimg_mp_op("Function 'd()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_d,p1); + } + + if (!std::strncmp(ss,"date(",5)) { // Current date or file date + _cimg_mp_op("Function 'date()'"); + s1 = ss5; while (s1::vector((ulongT)mp_date,pos,_cimg_mp_size(pos), + arg1,arg1==~0U?~0U:_cimg_mp_size(arg1), + arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"debug(",6)) { // Print debug info + _cimg_mp_op("Function 'debug()'"); + p1 = code._width; + arg1 = compile(ss6,se1,depth1,p_ref,is_critical); + *se1 = 0; + variable_name.assign(CImg::string(ss6,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + ((CImg::vector((ulongT)mp_debug,arg1,0,code._width - p1), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code,p1); + *se1 = ')'; + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image + _cimg_mp_op("Function 'display()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + if (*ss8!='#') { // Vector + s1 = ss8; while (s1::string(ss8,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(arg1)) + ((CImg::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0), + variable_name)>'y').move_to(opcode); + else + ((CImg::vector((ulongT)mp_print,arg1,0,0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + + ((CImg::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1), + arg2,arg3,arg4,arg5), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *s1 = c1; + _cimg_mp_return(arg1); + + } else { // Image + p1 = compile(ss8 + 1,se1,depth1,0,is_critical); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"det(",4)) { // Matrix determinant + _cimg_mp_op("Function 'det()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + _cimg_mp_scalar2(mp_det,arg1,p1); + } + + if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix + _cimg_mp_op("Function 'diag()'"); + CImg::vector((ulongT)mp_diag,0,0).move_to(l_opcode); + for (s = ss5; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + arg1 = opcode._height - 3; + pos = vector(arg1*arg1); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"dot(",4)) { // Dot product + _cimg_mp_op("Function 'dot()'"); + s1 = ss4; while (s1::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1), + p1>=arg6 && !_cimg_mp_is_constant(p1), + p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + _cimg_mp_return(p1); + } + + if (!std::strncmp(ss,"draw(",5)) { // Draw image + if (!is_critical) is_parallelizable = false; + _cimg_mp_op("Function 'draw()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s01) { + arg3 = arg2 + 1; + if (p2>2) { + arg4 = arg3 + 1; + if (p2>3) arg5 = arg4 + 1; + } + } + ++s0; + is_sth = true; + } else { + if (s0::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5, + 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode); + + arg2 = arg3 = arg4 = arg5 = ~0U; + p2 = p1!=~0U?0:1; + if (s0::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode); + for (s = ss5; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector + _cimg_mp_op("Function 'eig()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + pos = vector((p1 + 1)*p1); + CImg::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing + if (!is_critical) is_parallelizable = false; + _cimg_mp_op("Function 'ellipse()'"); + if (*ss8=='#') { // Index specified + s0 = ss + 9; while (s0::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"erf(",4)) { // Error function + _cimg_mp_op("Function 'erf()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::erf(mem[arg1])); + _cimg_mp_scalar1(mp_erf,arg1); + } + + if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function + _cimg_mp_op("Function 'erfinv()'"); + arg1 = compile(ss7,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::erfinv(mem[arg1])); + _cimg_mp_scalar1(mp_erfinv,arg1); + } + + if (!std::strncmp(ss,"exp(",4)) { // Exponential + _cimg_mp_op("Function 'exp()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); + _cimg_mp_scalar1(mp_exp,arg1); + } + + if (!std::strncmp(ss,"expr(",5)) { // Vector from expression + _cimg_mp_op("Function 'expr()'"); + s1 = ss5; while (s1::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"eye(",4)) { // Identity matrix + _cimg_mp_op("Function 'eye()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_constant(arg1,1,3); + p1 = (unsigned int)mem[arg1]; + pos = vector(p1*p1); + CImg::vector((ulongT)mp_eye,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"end(",4)) { // End + _cimg_mp_op("Function 'end()'"); + code.swap(code_end); + compile(ss4,se1,depth1,p_ref,true); + code.swap(code_end); + is_end_code = true; + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"end_t(",6)) { // End thread + _cimg_mp_op("Function 'end_t()'"); + code.swap(code_end_t); + compile(ss6,se1,depth1,p_ref,true); + code.swap(code_end_t); + is_end_code = true; + _cimg_mp_return_nan(); + } + break; + + case 'f' : + if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion + _cimg_mp_op("Function 'f2ui()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::float2uint((float)mem[arg1])); + _cimg_mp_scalar1(mp_f2ui,arg1); + } + + if (!std::strncmp(ss,"fact(",5)) { // Factorial + _cimg_mp_op("Function 'fact()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1])); + _cimg_mp_scalar1(mp_factorial,arg1); + } + + if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci + _cimg_mp_op("Function 'fibo()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1])); + _cimg_mp_scalar1(mp_fibonacci,arg1); + } + + if (!std::strncmp(ss,"fill(",5)) { // Fill + _cimg_mp_op("Function 'fill()'"); + s0 = ss5; while (s0::%s: %s: Target scalar is constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + ss>expr._data?"...":"",ss,se<&expr.back()?"...":""); + s1 = ++s0; while (s1::%s: %s: Invalid loop variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg3,3,1,0); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,is_critical); + } + // arg2 = variable slot, arg3 = fill expression. + _cimg_mp_check_type(arg3,3,1,0); + CImg::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1). + move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"find(",5)) { // Find + _cimg_mp_op("Function 'find()'"); + + // First argument: data to look at. + s0 = ss5; while (s01) + _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4); + _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4); + } + if (_cimg_mp_size(arg2)>1) + _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4); + _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4); + } + + if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop + _cimg_mp_op("Function 'for()'"); + s1 = ss4; while (s1::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2, + arg4 - arg3,code._width - arg4, + p3>=arg6 && !_cimg_mp_is_constant(p3), + p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + _cimg_mp_return(p3); + } + + if (!std::strncmp(ss,"floor(",6)) { // Floor + _cimg_mp_op("Function 'floor()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1])); + _cimg_mp_scalar1(mp_floor,arg1); + } + + if (!std::strncmp(ss,"fsize(",6)) { // File size + _cimg_mp_op("Function 'fsize()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,2,0); + pos = scalar(); + CImg::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'g' : + if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function + _cimg_mp_op("Function 'gauss()'"); + s1 = ss6; while (s1::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + break; + + case 'h' : + if (*ss1=='(') { // Image height + _cimg_mp_op("Function 'h()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_h,p1); + } + break; + + case 'i' : + if (*ss1=='c' && *ss2=='(') { // Image median + _cimg_mp_op("Function 'ic()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_median,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (*ss1=='n' && *ss2=='(') { // Image norm + _cimg_mp_op("Function 'in()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_norm,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (*ss1=='f' && *ss2=='(') { // If..then[..else.] + _cimg_mp_op("Function 'if()'"); + s1 = ss3; while (s1::vector((ulongT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"inrange(",8)) { // Check value range + _cimg_mp_op("Function 'inrange()'"); + s1 = ss8; while (s1=val1) + is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"int(",4)) { // Integer cast + _cimg_mp_op("Function 'int()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]); + _cimg_mp_scalar1(mp_int,arg1); + } + + if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion + _cimg_mp_op("Function 'invert()'"); + s1 = ss7; while (s1::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]); + _cimg_mp_scalar2(mp_div,1,arg1); + } + + if (*ss1=='s') { // Family of 'is_?()' functions + + if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? + _cimg_mp_op("Function 'isbool()'"); + if (ss7==se1) _cimg_mp_return(0); + try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + catch(CImgException&) { _cimg_mp_return(0); } + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.); + _cimg_mp_scalar1(mp_isbool,arg1); + } + + if (!std::strncmp(ss,"isdir(",6)) { // Is directory? + _cimg_mp_op("Function 'isdir()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isfile(",7)) { // Is file? + _cimg_mp_op("Function 'isfile()'"); + arg1 = compile(ss7,se1,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector? + if (ss5>=se1) _cimg_mp_return(0); + _cimg_mp_op("Function 'isin()'"); + pos = scalar(); + CImg::vector((ulongT)mp_isin,pos,0).move_to(l_opcode); + for (s = ss5; s::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)). + move_to(l_opcode); + else CImg::vector(arg1).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? + _cimg_mp_op("Function 'isinf()'"); + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); + _cimg_mp_scalar1(mp_isinf,arg1); + } + + if (!std::strncmp(ss,"isint(",6)) { // Is integer? + _cimg_mp_op("Function 'isint()'"); + if (ss6==se1) _cimg_mp_return(0); + try { arg1 = compile(ss6,se1,depth1,0,is_critical); } + catch(CImgException&) { _cimg_mp_return(0); } + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1])); + _cimg_mp_scalar1(mp_isint,arg1); + } + + if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? + _cimg_mp_op("Function 'isnan()'"); + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); + _cimg_mp_scalar1(mp_isnan,arg1); + } + + if (!std::strncmp(ss,"isnum(",6)) { // Is number? + _cimg_mp_op("Function 'isnum()'"); + val = 0; + if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); + _cimg_mp_return(0); + } + + if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression? + _cimg_mp_op("Function 'isexpr()'"); + if (ss7==se1) _cimg_mp_return(0); + try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + catch (CImgException&) { _cimg_mp_return(0); } + _cimg_mp_return(1); + } + } + break; + + case 'l' : + if (*ss1=='(') { // Size of image list + _cimg_mp_op("Function 'l()'"); + if (ss2!=se1) break; + _cimg_mp_scalar0(mp_list_l); + } + + if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation + _cimg_mp_op("Function 'lerp()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"log(",4)) { // Natural logarithm + _cimg_mp_op("Function 'log()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); + _cimg_mp_scalar1(mp_log,arg1); + } + + if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm + _cimg_mp_op("Function 'log2()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); + _cimg_mp_scalar1(mp_log2,arg1); + } + + if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm + _cimg_mp_op("Function 'log10()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); + _cimg_mp_scalar1(mp_log10,arg1); + } + + if (!std::strncmp(ss,"lowercase(",10)) { // Lower case + _cimg_mp_op("Function 'lowercase()'"); + arg1 = compile(ss + 10,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1])); + _cimg_mp_scalar1(mp_lowercase,arg1); + } + break; + + case 'm' : + if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication + _cimg_mp_op("Function 'mul()'"); + s1 = ss4; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " + "do not match with third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary + _cimg_mp_op("Function 'mproj()'"); + s1 = ss6; while (s1::%s: %s: Type of first argument ('%s') " + "do not match with second argument 'nb_colsS=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,wS, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (wD*hD!=p2) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of third argument ('%s') " + "do not match with fourth argument 'nb_colsD=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg3)._data,wD, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (hS!=hD) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of first argument ('%s') " + "do not match with third argument ('%s'), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg3)._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(wS*wD); + CImg::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables + _cimg_mp_op("Function 'merge()'"); + s1 = ss6; while (s1::%s: %s: Merge has already been requested before " + "for specified variable " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (arg1==~0U) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified operator " + "(should be one of '=,+,-,*,/,min,max'), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + memmerge.resize(3,memmerge._height + 1,1,1,0,0); + memmerge(0,memmerge._height - 1) = (int)pos; + memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos); + memmerge(2,memmerge._height - 1) = (int)arg1; + _cimg_mp_return(pos); + } + break; + + case 'n' : +#ifdef cimg_mp_func_name + if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector + _cimg_mp_op("Function 'name()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::vector((ulongT)mp_name,pos,p1,arg1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + + if (!std::strncmp(ss,"narg(",5)) { // Number of arguments + _cimg_mp_op("Function 'narg()'"); + if (ss5>=se1) _cimg_mp_return(0); + arg1 = 0; + for (s = ss5; s::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break; + case 1 : + CImg::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break; + case 2 : + CImg::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break; + case ~0U : + CImg::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break; + default : + CImg::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). + move_to(l_opcode); + } + for ( ; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + + (l_opcode>'y').move_to(opcode); + if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 + _cimg_mp_scalar1(mp_abs,opcode[3]); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'p' : + if (!std::strncmp(ss,"permut(",7)) { // Number of permutations + _cimg_mp_op("Function 'permut()'"); + s1 = ss7; while (s1::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions + is_sth = ss[5]=='s'; // is prints() + _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); + s0 = is_sth?ss7:ss6; + if (*s0!='#' || is_sth) { // Regular expression + for (s = s0; s::string(s,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), + variable_name)>'y').move_to(opcode); + else // Scalar + ((CImg::vector((ulongT)mp_print,pos,0,is_sth?1:0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *ns = c1; s = ns; + } + _cimg_mp_return(pos); + } else { // Image + p1 = compile(ss7,se1,depth1,0,is_critical); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion + _cimg_mp_op("Function 'pseudoinvert()'"); + s1 = ss + 13; while (s1::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1); + CImg::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'r' : + if (!std::strncmp(ss,"ref(",4)) { // Variable declaration + _cimg_mp_op("Function 'ref()'"); + s1 = ss4; while (s1=se1 || !*s1) compile(s1,s1,depth1,0,is_critical); // Will throw missing argument error + arg3 = compile(ss4,s1++,depth1,p_ref,is_critical); + *se1 = 0; + + if (!is_varname(s1)) { // Invalid variable name + variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0; + cimg::strellipsize(variable_name,64); + *se1 = ')'; + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + + } + get_variable_pos(s1,arg1,arg2); + if (arg2!=~0U) reserved_label[arg2] = arg3; + else if (arg1!=~0U) variable_pos[arg1] = arg3; + else { // New variable + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg3; + CImg::string(s1).move_to(variable_def); + } + if (_cimg_mp_is_vector(arg3)) + set_reserved_vector(arg3); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; + *se1 = ')'; + _cimg_mp_return(arg3); + } + + if (!std::strncmp(ss,"repeat(",7)) { // Repeat + _cimg_mp_op("Function 'repeat()'"); + s0 = ss7; while (s0::%s: %s: Invalid loop variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,is_critical); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,is_critical); + } + // arg2 = variable slot, arg3 = fill expression. + CImg::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg3); + } + + if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize + _cimg_mp_op("Function 'resize()'"); + if (*ss7!='#') { // Vector + s1 = ss7; while (s1::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1), + arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + + } else { // Image + if (!is_critical) is_parallelizable = false; + s0 = ss8; while (s0::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). + move_to(l_opcode); + pos = 0; + for (s = s0; s10) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + pos<1?"Missing":"Too much", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + l_opcode[0].move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse + _cimg_mp_op("Function 'reverse()'"); + arg1 = compile(ss8,se1,depth1,0,is_critical); + if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); + p1 = _cimg_mp_size(arg1); + pos = vector(p1); + CImg::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation + _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'"); + s1 = ss4; while (s11) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + arg4 = compile(++s1,se1,depth1,0,is_critical); + } else { + s2 = s1 + 1; while (s2::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code); + } else { // 2D rotation + _cimg_mp_check_type(arg1,1,1,0); + pos = vector(4); + CImg::vector((ulongT)mp_rot2d,pos,arg1).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"round(",6)) { // Value rounding + _cimg_mp_op("Function 'round()'"); + s1 = ss6; while (s1::vector((ulongT)mp_run,0,0).move_to(l_opcode); + pos = 1; + for (s = ss4; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + pos = scalar(); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + break; + + case 's' : + if (*ss1=='(') { // Image spectrum + _cimg_mp_op("Function 's()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_s,p1); + } + + if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values + _cimg_mp_op("Function 'same()'"); + s1 = ss5; while (s1::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sign(",5)) { // Sign + _cimg_mp_op("Function 'sign()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1])); + _cimg_mp_scalar1(mp_sign,arg1); + } + + if (!std::strncmp(ss,"sin(",4)) { // Sine + _cimg_mp_op("Function 'sin()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1])); + _cimg_mp_scalar1(mp_sin,arg1); + } + + if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal + _cimg_mp_op("Function 'sinc()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1])); + _cimg_mp_scalar1(mp_sinc,arg1); + } + + if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine + _cimg_mp_op("Function 'sinh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1])); + _cimg_mp_scalar1(mp_sinh,arg1); + } + + if (!std::strncmp(ss,"size(",5)) { // Vector size + _cimg_mp_op("Function 'size()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); + } + + if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system + _cimg_mp_op("Function 'solve()'"); + s1 = ss6; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " + "do not match with third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sort(",5)) { // Sort vector + _cimg_mp_op("Function 'sort()'"); + s1 = ss5; while (s1::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sqr(",4)) { // Square + _cimg_mp_op("Function 'sqr()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); + _cimg_mp_scalar1(mp_sqr,arg1); + } + + if (!std::strncmp(ss,"sqrt(",5)) { // Square root + _cimg_mp_op("Function 'sqrt()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); + _cimg_mp_scalar1(mp_sqrt,arg1); + } + + if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed + _cimg_mp_op("Function 'srand()'"); + arg1 = ss6::vector((ulongT)mp_image_stats,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + +#ifdef cimg_mp_func_store + if (!std::strncmp(ss,"store(",6)) { // Store vector to variable + _cimg_mp_op("Function 'store()'"); + s1 = ss6; while (s1::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2, + arg3,arg4,arg5,arg6,pos).move_to(code); + _cimg_mp_return_nan(); + } +#endif + + if (!std::strncmp(ss,"stov(",5)) { // String to double + _cimg_mp_op("Function 'stov()'"); + s1 = ss5; while (s1::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments + _cimg_mp_op("Function 'string()'"); + CImg::vector((ulongT)mp_string,0,0,0).move_to(l_opcode); + + if (*ss7=='#') { // Output vector size specified, with '#' + s0 = ss8; while (s0::vector(arg2,p2).move_to(l_opcode); + s = ns; + } + if (arg1==~0U) arg1 = p1; + pos = vector(arg1,0); + (l_opcode>'y').move_to(opcode); + opcode[1] = pos; + opcode[2] = arg1; + opcode[3] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD + _cimg_mp_op("Function 'svd()'"); + s1 = ss4; while (s1::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1 + p2 + p2*p2); + CImg::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"swap(",5)) { // Swap values + _cimg_mp_op("Function 'swap()'"); + s1 = ss5; while (s1::%s: %s: %s argument cannot be a constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"First":"Second", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + CImg::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code); + + // Write back values of linked arg1 and arg2. + const unsigned int *_ref = ref; + is_sth = true; // Is first argument? + do { + switch (*_ref) { + case 1 : // arg1: V[k] + arg3 = _ref[1]; // Vector slot + arg4 = _ref[2]; // Index + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + break; + case 2 : // arg1: i/j[_#ind,off] + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // Offset + if (p1!=~0U) { + if (listout) + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (imgout) + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + break; + case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c) + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // X + arg4 = _ref[4]; // Y + arg5 = _ref[5]; // Z + arg6 = _ref[6]; // C + if (p1!=~0U) { + if (listout) + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (imgout) + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + break; + case 4: // arg1: I/J[_#ind,off] + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // Offset + if (p1!=~0U) { + if (listout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg1,p1,arg3).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + } + } else { + if (imgout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg1,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + } + break; + case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c) + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // X + arg4 = _ref[4]; // Y + arg5 = _ref[5]; // Z + if (p1!=~0U) { + if (listout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg1,p1,arg3,arg4,arg5).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + } else { + if (imgout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg1,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + break; + } + + _ref+=7; + arg1 = arg2; + is_sth = !is_sth; + } while (!is_sth); + + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + _cimg_mp_return(arg1); + } + break; + + case 't' : + if (!std::strncmp(ss,"tan(",4)) { // Tangent + _cimg_mp_op("Function 'tan()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); + _cimg_mp_scalar1(mp_tan,arg1); + } + + if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent + _cimg_mp_op("Function 'tanh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); + _cimg_mp_scalar1(mp_tanh,arg1); + } + + if (!std::strncmp(ss,"trace(",6)) { // Matrix trace + _cimg_mp_op("Function 'trace()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + _cimg_mp_scalar2(mp_trace,arg1,p1); + } + + if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose + _cimg_mp_op("Function 'transpose()'"); + s1 = ss + 10; while (s1::%s: %s: Size of first argument ('%s') does not match " + "second argument 'nb_cols=%u', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p3*p2); + CImg::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'u' : + if (*ss1=='(') { // Random value with uniform distribution + _cimg_mp_op("Function 'u()'"); + if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); + s1 = ss2; while (s1float conversion + _cimg_mp_op("Function 'ui2f()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::uint2float((unsigned int)mem[arg1])); + _cimg_mp_scalar1(mp_ui2f,arg1); + } + + if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable + _cimg_mp_op("Function 'unref()'"); + arg1=~0U; + for (s0 = ss6; s0ss6 && *s0==',') ++s0; + s1 = s0; while (s1s0) { + *s1 = 0; + get_variable_pos(s0,arg1,arg2); + if (arg2!=~0U) reserved_label[arg2] = ~0U; + else if (arg1!=~0U) { + variable_def.remove(arg1); + if (arg10) || + !std::strncmp(ss,"vector(",7) || + (!std::strncmp(ss,"vector",6) && ss7::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode); + arg2+=arg4; + } else { CImg::vector(arg3).move_to(l_opcode); ++arg2; } + s = ns; + } + if (arg1==~0U) arg1 = arg2; + if (!arg1) _cimg_mp_return(0); + pos = vector(arg1); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) || + !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) || + !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) || + !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) || + !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) || + !std::strncmp(ss,"vprod(",6) || + !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) || + !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) || + !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions + _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'": + ss[4]=='k'?"Function 'vargkth()'": + ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'": + ss[5]=='i'?"Function vargminabs()'": + ss[7]=='('?"Function 'vargmax()'": + "Function 'vargmaxabs()'"): + ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"): + ss[1]=='k'?"Function 'vkth()'": + ss[1]=='p'?"Function 'vprod()'": + ss[1]=='v'?"Function 'vvar()'": + ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'": + "Function 'vminabs()'"): + ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'": + "Function 'vmaxabs()'"): + "Function 'vmed()'"); + op = ss[1]=='a'?(ss[2]=='v'?mp_vavg: + ss[4]=='k'?mp_vargkth: + ss[5]=='i' && ss[7]=='('?mp_vargmin: + ss[5]=='i'?mp_vargminabs: + ss[7]=='('?mp_vargmax:mp_vargmaxabs): + ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd): + ss[1]=='k'?mp_vkth: + ss[1]=='p'?mp_vprod: + ss[1]=='v'?mp_vvar: + ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs): + ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs): + mp_vmedian; + CImg::vector((ulongT)op,0,0,0).move_to(l_opcode); + p1 = ~0U; + p3 = 1; + for (s = std::strchr(ss,'(') + 1; s::vector(arg2,p2).move_to(l_opcode); + s = ns; + ++p3; + } + (l_opcode>'y').move_to(opcode); + if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1); + opcode[1] = pos; + opcode[2] = p1; + opcode[3] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string + _cimg_mp_op("Function 'vtos()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'w' : + if (*ss1=='(') { // Image width + _cimg_mp_op("Function 'w()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_w,p1); + } + + if (*ss1=='h' && *ss2=='(') { // Image width*height + _cimg_mp_op("Function 'wh()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_wh,p1); + } + + if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth + _cimg_mp_op("Function 'whd()'"); + if (*ss4=='#') { // Index specified + p1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss4!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_whd,p1); + } + + if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum + _cimg_mp_op("Function 'whds()'"); + if (*ss5=='#') { // Index specified + p1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss5!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_whds,p1); + } + + if (!std::strncmp(ss,"while(",6)) { // While...do + _cimg_mp_op("Function 'while()'"); + s0 = *ss5=='('?ss6:ss8; + s1 = s0; while (s1::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2, + pos>=arg6 && !_cimg_mp_is_constant(pos), + arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1); + _cimg_mp_return(pos); + } + break; + + case 'x' : + if (!std::strncmp(ss,"xor(",4)) { // Xor + _cimg_mp_op("Function 'xor()'"); + s1 = ss4; while (s1::vector((ulongT)op,pos,0).move_to(l_opcode); + for (s = std::strchr(ss,'(') + 1; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + is_sth&=_cimg_mp_is_constant(arg2); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (is_sth) _cimg_mp_constant(op(*this)); + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // No corresponding built-in function -> Look for a user-defined macro call. + s0 = strchr(ss,'('); + if (s0) { + variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; + + // Count number of specified arguments. + p1 = 0; + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && !p1) break; + ns = s; while (ns _expr = macro_body[l]; // Expression to be substituted + + p1 = 1; // Index of current parsed argument + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && p1==1) break; // Function has no arguments + if (p1>p2) { ++p1; break; } + ns = s; while (ns _pexpr(_expr._width); + ns = _pexpr._data; + for (ps = _expr._data, c1 = ' '; *ps; ++ps) { + if (!cimg::is_blank(*ps)) c1 = *ps; + *(ns++) = c1; + } + *ns = 0; + + CImg _level = get_level(_expr); + expr.swap(_expr); + pexpr.swap(_pexpr); + level.swap(_level); + s0 = user_macro; + user_macro = macro_def[l]; + pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_critical); + user_macro = s0; + level.swap(_level); + pexpr.swap(_pexpr); + expr.swap(_expr); + _cimg_mp_return(pos); + } + + if (arg3) { // Macro name matched but number of arguments does not + CImg sig_nargs(arg3); + arg1 = 0; + cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name)) + sig_nargs[arg1++] = (unsigned int)macro_def[l].back(); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + if (sig_nargs._width>1) { + sig_nargs.sort(); + arg1 = sig_nargs.back(); + --sig_nargs._width; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " + "does not match macro declaration (defined for %s or %u arguments), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p1,sig_nargs.value_string()._data,arg1, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " + "does not match macro declaration (defined for %u argument%s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p1,*sig_nargs,*sig_nargs!=1?"s":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + } // if (se1==')') + + // Char / string initializer. + if (*se1=='\'' && + ((se1>ss && *ss=='\'') || + (se1>ss1 && *ss=='_' && *ss1=='\''))) { + if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; } + else { _cimg_mp_op("String initializer"); s1 = ss1; } + arg1 = (unsigned int)(se1 - s1); // Original string length + if (arg1) { + CImg(s1,arg1 + 1).move_to(variable_name).back() = 0; + cimg::strunescape(variable_name); + arg1 = (unsigned int)std::strlen(variable_name); + } + if (!arg1) _cimg_mp_return(0); // Empty string -> 0 + if (*ss=='_') { + if (arg1==1) _cimg_mp_constant((unsigned char)*variable_name); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Literal %s contains more than one byte, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + ss1, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg1); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // Vector initializer [ ... ]. + if (*ss=='[' && *se1==']') { + _cimg_mp_op("Vector initializer"); + s1 = ss1; while (s1s1 && cimg::is_blank(*s2)) --s2; + if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string + arg1 = (unsigned int)(s2 - s1 - 1); // Original string length + if (arg1) { + CImg(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0; + cimg::strunescape(variable_name); + arg1 = (unsigned int)std::strlen(variable_name); + } + if (!arg1) _cimg_mp_return(0); // Empty string -> 0 + pos = vector(arg1); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); + } else { // Vector values provided as list of items + arg1 = 0; // Number of specified values + if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode); + arg1+=arg3; + } else { CImg::vector(arg2).move_to(l_opcode); ++arg1; } + s = ns; + } + if (!arg1) _cimg_mp_return(0); + pos = vector(arg1); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + // Variables related to the input list of images. + if (*ss1=='#' && ss2::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + case 'R' : // R#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0, + 0,_cimg_mp_boundary); + case 'G' : // G#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1, + 0,_cimg_mp_boundary); + case 'B' : // B#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2, + 0,_cimg_mp_boundary); + case 'A' : // A#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3, + 0,_cimg_mp_boundary); + } + } + + if (*ss1 && *ss2=='#' && ss3='0' && *ss1<='9') { // i0#ind...i9#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0', + 0,_cimg_mp_boundary); + } + + if (*ss1=='c') { // ic#ind + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_median) list_median.assign(listin._width); + if (!list_median[p1]) CImg::vector(listin[p1].median()).move_to(list_median[p1]); + _cimg_mp_constant(*list_median[p1]); + } + _cimg_mp_scalar1(mp_list_median,arg1); + } + + if (*ss1=='n') { // in#ind + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_norm) list_norm.assign(listin._width); + if (!list_norm[p1]) CImg::vector(listin[p1].magnitude()).move_to(list_norm[p1]); + _cimg_mp_constant(*list_norm[p1]); + } + _cimg_mp_scalar1(mp_list_norm,arg1); + } + + switch (*ss1) { + case 'm' : arg2 = 0; break; // im#ind + case 'M' : arg2 = 1; break; // iM#ind + case 'a' : arg2 = 2; break; // ia#ind + case 'v' : arg2 = 3; break; // iv#ind + case 's' : arg2 = 12; break; // is#ind + case 'p' : arg2 = 13; break; // ip#ind + } + } else if (*ss1=='m') switch (*ss) { + case 'x' : arg2 = 4; break; // xm#ind + case 'y' : arg2 = 5; break; // ym#ind + case 'z' : arg2 = 6; break; // zm#ind + case 'c' : arg2 = 7; break; // cm#ind + } else if (*ss1=='M') switch (*ss) { + case 'x' : arg2 = 8; break; // xM#ind + case 'y' : arg2 = 9; break; // yM#ind + case 'z' : arg2 = 10; break; // zM#ind + case 'c' : arg2 = 11; break; // cM#ind + } + if (arg2!=~0U) { + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_stats) list_stats.assign(listin._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); + _cimg_mp_constant(list_stats(p1,arg2)); + } + _cimg_mp_scalar2(mp_list_stats,arg1,arg2); + } + } + + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4 error. + c1 = *se1; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + if (is_sth) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s1 = std::strchr(ss,'('); + s_op = s1 && c1==')'?"function call":"item"; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + s_op,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Evaluation procedure. + double operator()(const double x, const double y, const double z, const double c) { + mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; + for (p_code = code; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + return *result; + } + + // Evaluation procedure (return output values in vector 'output'). + template + void operator()(const double x, const double y, const double z, const double c, t *const output) { + mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; + for (p_code = code; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + if (result_dim) { + const double *ptrs = result + 1; + t *ptrd = output; + for (unsigned int k = 0; k_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + p_code_end = code.end(); + } + + // Evaluation procedure for end_t() bloc. + void end_t() { + if (!code_end_t) return; + if (imgin) { + mem[_cimg_mp_slot_x] = imgin._width - 1.; + mem[_cimg_mp_slot_y] = imgin._height - 1.; + mem[_cimg_mp_slot_z] = imgin._depth - 1.; + mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; + } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_end_t.end(); + for (p_code = code_end_t; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Evaluation procedure the end() bloc. + void end() { + if (!code_end) return; + if (imgin) { + mem[_cimg_mp_slot_x] = imgin._width - 1.; + mem[_cimg_mp_slot_y] = imgin._height - 1.; + mem[_cimg_mp_slot_z] = imgin._depth - 1.; + mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; + } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_end.end(); + for (p_code = code_end; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Merge inter-thread variables. + // (argument 'mp' is the master instance). + void merge(_cimg_math_parser& mp) { + if (&mp==this) return; + cimg_rofY(mp.memmerge,k) { + const unsigned int + pos = (unsigned int)mp.memmerge(0,k), + siz = (unsigned int)mp.memmerge(1,k), + iop = (unsigned int)mp.memmerge(2,k); + if (!siz) switch (iop) { // Scalar value + case 0 : mp.mem[pos] = mem[pos]; break; // Assignment + case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+ + case 2 : mp.mem[pos]-=mem[pos]; break; // Operator- + case 3 : mp.mem[pos]*=mem[pos]; break; // Operator* + case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/ + case 5 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min' + case 6 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max' + } else switch (iop) { // Vector value + case 0 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true) = CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 1 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 2 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 3 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 4 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 5 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg(&mem[pos + 1],siz,1,1,1,true)); + break; + case 6 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg(&mem[pos + 1],siz,1,1,1,true)); + break; + } + } + } + + // Return specified argument number as a string. + static const char *s_argth(const unsigned int n_arg) { + const char + *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth", + "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", + "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" }; + return _s_arg[n_arg s_calling_function() const { + CImg res; + const unsigned int + l1 = calling_function?(unsigned int)std::strlen(calling_function):0U, + l2 = user_macro?(unsigned int)std::strlen(user_macro):0U; + if (l2) { + res.assign(l1 + l2 + 48); + cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro); + } else { + res.assign(l1 + l2 + 4); + cimg_snprintf(res,res._width,"%s()",calling_function); + } + return res; + } + + // Return type of a memory element as a string. + CImg s_type(const unsigned int arg) const { + CImg res; + if (_cimg_mp_is_vector(arg)) { // Vector + CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); + cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg)); + } else if (_cimg_mp_is_constant(arg)) CImg::string("const scalar").move_to(res); // Const scalar + else CImg::string("scalar").move_to(res); // Scalar + return res; + } + + // Count parentheses/brackets level of each character of the expression. + CImg get_level(CImg& _expr) const { + bool is_escaped = false, next_is_escaped = false; + unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string + CImg res(_expr._width - 1); + unsigned int *pd = res._data; + int _level = 0; + for (const char *ps = _expr._data; *ps && _level>=0; ++ps) { + if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true; + if (!is_escaped && *ps=='\'') { // Non-escaped character + if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string + else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string + else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string + } + *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1): + *ps=='(' || *ps=='['?_level++: + *ps==')' || *ps==']'?--_level: + _level); + mode = next_mode; + is_escaped = next_is_escaped; + next_is_escaped = false; + } + if (mode) { + cimg::strellipsize(_expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unterminated string literal, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + _expr._data); + } + if (_level) { + cimg::strellipsize(_expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + _expr._data); + } + return res; + } + + // Find and return index of current image 'imgin' within image list 'listin'. + unsigned int get_mem_img_index() { + if (mem_img_index==~0U) { + if (&imgout>listout.data() && &imgout='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9 + else if (c2=='m') rp = 4; // im + else if (c2=='M') rp = 5; // iM + else if (c2=='a') rp = 6; // ia + else if (c2=='v') rp = 7; // iv + else if (c2=='s') rp = 8; // is + else if (c2=='p') rp = 9; // ip + else if (c2=='c') rp = 10; // ic + else if (c2=='n') rp = 11; // in + } else if (c2=='m') { + if (c1=='x') rp = 12; // xm + else if (c1=='y') rp = 13; // ym + else if (c1=='z') rp = 14; // zm + else if (c1=='c') rp = 15; // cm + } else if (c2=='M') { + if (c1=='x') rp = 16; // xM + else if (c1=='y') rp = 17; // yM + else if (c1=='z') rp = 18; // zM + else if (c1=='c') rp = 19; // cM + } + } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd + } else if (variable_name[1] && variable_name[2] && variable_name[3] && + !variable_name[4]) { // Four-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + c4 = variable_name[3]; + if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds + } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation + else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary + + if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels + + // Multi-char variable name : check for existing variable with same name + cimglist_for(variable_def,i) + if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; } + } + + // Tell for each character of an expression if it is inside a string or not. + CImg is_inside_string(CImg& _expr) const { + bool is_escaped = false, next_is_escaped = false; + unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string + CImg res = CImg::string(_expr); + bool *pd = res._data; + for (const char *ps = _expr._data; *ps; ++ps) { + if (!next_is_escaped && *ps=='\\') next_is_escaped = true; + if (!is_escaped && *ps=='\'') { // Non-escaped character + if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string + else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string + else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string + } + *(pd++) = mode>=1 || is_escaped; + mode = next_mode; + is_escaped = next_is_escaped; + next_is_escaped = false; + } + return res; + } + + // Return true if specified argument can be a part of an allowed variable name. + bool is_varchar(const char c) const { + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + } + + // Return true if specified argument can be considered as a variable name. + bool is_varname(const char *const str, const unsigned int length=~0U) const { + if (*str>='0' && *str<='9') return false; + for (unsigned int l = 0; l128) return false; + const int *ptr = memtype.data(arg + 1); + bool is_tmp = true; + while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } + return is_tmp; + } + + // Check if a memory slot is a positive integer constant scalar value. + // 'mode' can be: + // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant } + void check_constant(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, + char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,1,0); + if (!(_cimg_mp_is_constant(arg) && + (!mode || (double)(int)mem[arg]==mem[arg]) && + (mode<2 || mem[arg]>=(mode==3)))) { + const char *const s_arg = s_argth(n_arg); + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_arg?" argument":" Argument",s_type(arg)._data, + !mode?"":mode==1?"n integer": + mode==2?" positive integer":" strictly positive integer", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check if an image index is a constant value. + void check_constant_index(const unsigned int arg, + char *const ss, char *const se, const char saved_char) { + if (arg!=~0U && !_cimg_mp_is_constant(arg)) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Specified image index is not a constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check a matrix is square. + void check_matrix_square(const unsigned int arg, const unsigned int n_arg, + char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,2,0); + const unsigned int + siz = _cimg_mp_size(arg), + n = (unsigned int)cimg::round(std::sqrt((float)siz)); + if (n*n!=siz) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand"; + else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One"; + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') " + "cannot be considered as a square matrix, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), + s_type(arg)._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check type compatibility for one argument. + // Bits of 'mode' tells what types are allowed: + // { 1 = scalar | 2 = vectorN }. + // If 'N' is not zero, it also restricts the vectors to be of size N only. + void check_type(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, const unsigned int N, + char *const ss, char *const se, const char saved_char) { + const bool + is_scalar = _cimg_mp_is_scalar(arg), + is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N); + bool cond = false; + if (mode&1) cond|=is_scalar; + if (mode&2) cond|=is_vector; + if (!cond) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand"; + else s_arg = s_argth(n_arg); + CImg sb_type(32); + if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'"); + else if (mode==2) { + if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'vector'"); + } else { + if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'"); + } + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), + s_type(arg)._data,sb_type._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check that listin or listout are not empty. + void check_list(const bool is_out, + char *const ss, char *const se, const char saved_char) { + if ((!is_out && !listin) || (is_out && !listout)) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Invalid call with an empty image list, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Insert constant value in memory. + unsigned int constant(const double val) { + + // Search for built-in constant. + if (cimg::type::is_nan(val)) return _cimg_mp_slot_nan; + if (val==(double)(int)val) { + if (val>=0 && val<=10) return (unsigned int)val; + if (val<0 && val>=-5) return (unsigned int)(10 - val); + } + if (val==0.5) return 16; + + // Search for constant already requested before (in const cache). + unsigned int ind = ~0U; + if (constcache_size<1024) { + if (!constcache_size) { + constcache_vals.assign(16,1,1,1,0); + constcache_inds.assign(16,1,1,1,0); + *constcache_vals = val; + constcache_size = 1; + ind = 0; + } else { // Dichotomic search + const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1]; + if (val_beg>=val) ind = 0; + else if (val_end==val) ind = constcache_size - 1; + else if (val_end=constcache_size || constcache_vals[ind]!=val) { + ++constcache_size; + if (constcache_size>constcache_vals._width) { + constcache_vals.resize(-200,1,1,1,0); + constcache_inds.resize(-200,1,1,1,0); + } + const int l = constcache_size - (int)ind - 1; + if (l>0) { + std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double)); + std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int)); + } + constcache_vals[ind] = val; + constcache_inds[ind] = 0; + } + } + if (constcache_inds[ind]) return constcache_inds[ind]; + } + + // Insert new constant in memory if necessary. + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); } + const unsigned int pos = mempos++; + mem[pos] = val; + memtype[pos] = 1; // Set constant property + if (ind!=~0U) constcache_inds[ind] = pos; + return pos; + } + + // Insert new scalar in memory. + unsigned int scalar() { + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); } + return mempos++; + } + + // Insert new vector of specified size in memory. + unsigned int vector(const unsigned int siz) { + if (mempos + siz>=mem._width) { + mem.resize(2*mem._width + siz,1,1,1,0); + memtype.resize(mem._width,1,1,1,0); + } + const unsigned int pos = mempos++; + mem[pos] = cimg::type::nan(); + memtype[pos] = siz + 1; + mempos+=siz; + return pos; + } + + // Insert new initialized vector. + unsigned int vector(const unsigned int siz, const double value) { + const unsigned int pos = vector(siz); + double *ptr = &mem[pos] + 1; + for (unsigned int i = 0; i::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code); + return pos; + } + + // Set reserved status to all values of a vector. + void set_reserved_vector(const unsigned int arg) { + unsigned int siz = _cimg_mp_size(arg); + int *ptr = memtype.data(arg + 1); + while (siz-->0) *(ptr++) = -1; + } + + unsigned int scalar0(const mp_func op) { + const unsigned int pos = scalar(); + CImg::vector((ulongT)op,pos).move_to(code); + return_new_comp = true; + return pos; + } + + unsigned int scalar1(const mp_func op, const unsigned int arg1) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1).move_to(code); + return pos; + } + + unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2).move_to(code); + return pos; + } + + unsigned int scalar3(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code); + return pos; + } + + unsigned int scalar4(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code); + return pos; + } + + unsigned int scalar5(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); + return pos; + } + + unsigned int scalar6(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); + return pos; + } + + unsigned int scalar7(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6, + const unsigned int arg7) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); + return pos; + } + + void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) { + const unsigned int siz = _cimg_mp_size(pos); + if (siz>24) CImg::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]); + } + } + + void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) { + const unsigned int siz = _cimg_mp_size(pos); + if (siz>24) CImg::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); + } + } + + unsigned int vector1_v(const mp_func op, const unsigned int arg1) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg2), + pos = is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + // Evaluation functions, known by the parser. + // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT), + // so we can store pointers to them directly in the opcode vectors. +#ifdef _mp_arg +#undef _mp_arg +#endif +#define _mp_arg(x) mp.mem[mp.opcode[x]] + + static double mp_abs(_cimg_math_parser& mp) { + return cimg::abs(_mp_arg(2)); + } + + static double mp_add(_cimg_math_parser& mp) { + return _mp_arg(2) + _mp_arg(3); + } + + static double mp_acos(_cimg_math_parser& mp) { + return std::acos(_mp_arg(2)); + } + + static double mp_acosh(_cimg_math_parser& mp) { + return cimg::acosh(_mp_arg(2)); + } + + static double mp_asinh(_cimg_math_parser& mp) { + return cimg::asinh(_mp_arg(2)); + } + + static double mp_atanh(_cimg_math_parser& mp) { + return cimg::atanh(_mp_arg(2)); + } + + static double mp_arg(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(4); + const unsigned int + nb_args = (unsigned int)mp.opcode[2] - 4, + ind = _ind<0?_ind + nb_args:(unsigned int)_ind, + siz = (unsigned int)mp.opcode[3]; + if (siz>0) { + if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); + else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); + return cimg::type::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + static double mp_arg0(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(4); + const unsigned int + nb_args = (unsigned int)mp.opcode[2] - 4, + ind = _ind<0?_ind + nb_args:_ind + 1U, + siz = (unsigned int)mp.opcode[3]; + if (siz>0) { + if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); + else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); + return cimg::type::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + static double mp_argkth(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = mp_kth(mp); + for (unsigned int i = 4; ival) { val = _val; argval = i - 3; } + } + return (double)argval; + } + + static double mp_argmaxabs(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3), absval = cimg::abs(val); + unsigned int argval = 0; + for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; argval = i - 3; } + } + return (double)argval; + } + + static double mp_asin(_cimg_math_parser& mp) { + return std::asin(_mp_arg(2)); + } + + static double mp_atan(_cimg_math_parser& mp) { + return std::atan(_mp_arg(2)); + } + + static double mp_atan2(_cimg_math_parser& mp) { + return std::atan2(_mp_arg(2),_mp_arg(3)); + } + + static double mp_avg(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i>(unsigned int)_mp_arg(3)); + } + + static double mp_bitwise_xor(_cimg_math_parser& mp) { + return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3)); + } + + static double mp_bool(_cimg_math_parser& mp) { + return (double)(bool)_mp_arg(2); + } + + static double mp_break(_cimg_math_parser& mp) { + mp.break_type = 1; + mp.p_code = mp.p_break - 1; + return cimg::type::nan(); + } + + static double mp_breakpoint(_cimg_math_parser& mp) { + cimg_abort_init; + cimg_abort_test; + cimg::unused(mp); + return cimg::type::nan(); + } + +#ifdef cimg_mp_func_run + static double mp_run(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + CImg str = _str>'x'; + cimg_mp_func_run(str._data); + return cimg::type::nan(); + } +#endif + + static double mp_cbrt(_cimg_math_parser& mp) { + return cimg::cbrt(_mp_arg(2)); + } + + static double mp_ceil(_cimg_math_parser& mp) { + return std::ceil(_mp_arg(2)); + } + + static double mp_complex_abs(_cimg_math_parser& mp) { + return cimg::_hypot(_mp_arg(2),_mp_arg(3)); + } + + static double mp_complex_conj(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = real; + ptrd[1] = -imag; + return cimg::type::nan(); + } + + static double mp_complex_div_sv(_cimg_math_parser& mp) { + const double + *ptr2 = &_mp_arg(3) + 1, + r1 = _mp_arg(2), + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = r1*r2/denom; + *ptrd = -r1*i2/denom; + return cimg::type::nan(); + } + + static double mp_complex_div_vv(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = (r1*r2 + i1*i2)/denom; + *ptrd = (r2*i1 - r1*i2)/denom; + return cimg::type::nan(); + } + + static double mp_complex_exp(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = exp_real*std::cos(imag); + ptrd[1] = exp_real*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_log(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = 0.5*std::log(real*real + imag*imag); + ptrd[1] = std::atan2(imag,real); + return cimg::type::nan(); + } + + static double mp_complex_mul(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + *(ptrd++) = r1*r2 - i1*i2; + *(ptrd++) = r1*i2 + r2*i1; + return cimg::type::nan(); + } + + static void _mp_complex_pow(const double r1, const double i1, + const double r2, const double i2, + double *ptrd) { + double ro, io; + if (cimg::abs(i2)<1e-15) { // Exponent is real + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) { + if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; } + else ro = io = 0; + } else { + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2), + phio = r2*phi1; + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + } else { // Exponent is complex + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0; + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1), + phio = r2*phi1 + 0.5*i2*std::log(mod1_2); + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + *(ptrd++) = ro; + *ptrd = io; + } + + static double mp_complex_pow_ss(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_sv(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vs(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vv(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_cos(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::cos(real)*std::cosh(imag); + ptrd[1] = -std::sin(real)*std::sinh(imag); + return cimg::type::nan(); + } + + static double mp_complex_sin(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sin(real)*std::cosh(imag); + ptrd[1] = std::cos(real)*std::sinh(imag); + return cimg::type::nan(); + } + + static double mp_complex_tan(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sin(2*real)/denom; + ptrd[1] = std::sinh(2*imag)/denom; + return cimg::type::nan(); + } + + static double mp_complex_cosh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::cosh(real)*std::cos(imag); + ptrd[1] = std::sinh(real)*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_sinh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sinh(real)*std::cos(imag); + ptrd[1] = std::cosh(real)*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_tanh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sinh(2*real)/denom; + ptrd[1] = std::sin(2*imag)/denom; + return cimg::type::nan(); + } + + static double mp_continue(_cimg_math_parser& mp) { + mp.break_type = 2; + mp.p_code = mp.p_break - 1; + return cimg::type::nan(); + } + + static double mp_convolve(_cimg_math_parser &mp) { + return _mp_correlate(mp,true); + } + + static double mp_copy(_cimg_math_parser& mp) { + return _mp_arg(2); + } + + static double mp_correlate(_cimg_math_parser &mp) { + return _mp_correlate(mp,false); + } + + static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) { + double *ptrd = &_mp_arg(1) + 1; + const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1; + const unsigned int + wA = (unsigned int)mp.opcode[3], + hA = (unsigned int)mp.opcode[4], + dA = (unsigned int)mp.opcode[5], + sA = (unsigned int)mp.opcode[6], + wM = (unsigned int)mp.opcode[8], + hM = (unsigned int)mp.opcode[9], + dM = (unsigned int)mp.opcode[10], + sM = (unsigned int)mp.opcode[11], + boundary_conditions = (unsigned int)_mp_arg(12), + channel_mode = (unsigned int)mp.opcode[14], + xcenter = (unsigned int)_mp_arg(15), + ycenter = (unsigned int)_mp_arg(16), + zcenter = (unsigned int)_mp_arg(17), + xstart = (unsigned int)mp.opcode[18], + ystart = (unsigned int)mp.opcode[19], + zstart = (unsigned int)mp.opcode[20], + xend = (unsigned int)mp.opcode[21], + yend = (unsigned int)mp.opcode[22], + zend = (unsigned int)mp.opcode[23]; + const bool + is_normalized = (bool)_mp_arg(13); + const float + xstride = (float)_mp_arg(24), + ystride = (float)_mp_arg(25), + zstride = (float)_mp_arg(26), + xdilation = (float)_mp_arg(27), + ydilation = (float)_mp_arg(28), + zdilation = (float)_mp_arg(29); + CImg res; + if (is_convolve) res = CImg(ptrA,wA,hA,dA,sA,true). + get_convolve(CImg(ptrM,wM,hM,dM,sM,true), + boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter, + xstart,ystart,zstart, + xend,yend,zend, + xstride,ystride,zstride, + xdilation,ydilation,zdilation); + else res = CImg(ptrA,wA,hA,dA,sA,true). + get_correlate(CImg(ptrM,wM,hM,dM,sM,true), + boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter, + xstart,ystart,zstart, + xend,yend,zend, + xstride,ystride,zstride, + xdilation,ydilation,zdilation); + CImg(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res; + return cimg::type::nan(); + } + + static double mp_cos(_cimg_math_parser& mp) { + return std::cos(_mp_arg(2)); + } + + static double mp_cosh(_cimg_math_parser& mp) { + return std::cosh(_mp_arg(2)); + } + + static double mp_critical(_cimg_math_parser& mp) { + const ulongT g_target = mp.opcode[1]; + cimg_pragma_openmp(critical(mp_critical)) + { + for (const CImg *const p_end = ++mp.p_code + mp.opcode[2]; + mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + } + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const unsigned int + dx = (unsigned int)mp.opcode[7], + dy = (unsigned int)mp.opcode[8], + dz = (unsigned int)mp.opcode[9], + dc = (unsigned int)mp.opcode[10]; + const unsigned int boundary_conditions = (unsigned int)_mp_arg(11); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); + else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, + x + dx - 1,y + dy - 1, + z + dz - 1,c + dc - 1, + boundary_conditions); + return cimg::type::nan(); + } + + static double mp_cross(_cimg_math_parser& mp) { + CImg + vout(&_mp_arg(1) + 1,1,3,1,1,true), + v1(&_mp_arg(2) + 1,1,3,1,1,true), + v2(&_mp_arg(3) + 1,1,3,1,1,true); + (vout = v1).cross(v2); + return cimg::type::nan(); + } + + static double mp_cut(_cimg_math_parser& mp) { + double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); + return valcmax?cmax:val; + } + + static double mp_date(_cimg_math_parser& mp) { + const unsigned int + siz_out = (unsigned int)mp.opcode[2], + siz_arg1 = (unsigned int)mp.opcode[4], + siz_arg2 = (unsigned int)mp.opcode[6]; + double *ptr_out = &_mp_arg(1) + (siz_out?1:0); + const double + *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0), + *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1; + + if (!ptr_arg2) { // No filename specified + if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1); + if (siz_arg1==~0U) for (unsigned int k = 0; k::nan(); + } + + // Filename specified. + CImg ss(siz_arg2 + 1); + cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i]; + ss.back() = 0; + if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1); + for (unsigned int k = 0; k::nan(); + } + + static double mp_debug(_cimg_math_parser& mp) { + CImg expr(mp.opcode[2] - 4); + { + const ulongT *ptrs = mp.opcode._data + 4; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + } + cimg::strellipsize(expr); + const ulongT g_target = mp.opcode[1]; + +#if cimg_use_openmp==0 + const unsigned int n_thread = 0; +#else + const unsigned int n_thread = omp_get_thread_num(); +#endif + cimg_pragma_openmp(critical(mp_debug)) + { + std::fprintf(cimg::output(), + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" + "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width); + std::fflush(cimg::output()); + mp.debug_indent+=3; + } + const CImg *const p_end = ++mp.p_code + mp.opcode[3]; + CImg _op; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; + + _op.assign(1,op._height - 1); + const ulongT *ptrs = op._data + 1; + for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd mem[%u] = %.17g", + (void*)&mp,n_thread,mp.debug_indent,' ', + (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), + (unsigned int)target,mp.mem[target]); + std::fflush(cimg::output()); + } + } + cimg_pragma_openmp(critical(mp_debug)) + { + mp.debug_indent-=3; + std::fprintf(cimg::output(), + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" + "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); + std::fflush(cimg::output()); + } + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_decrement(_cimg_math_parser& mp) { + return _mp_arg(2) - 1; + } + + static double mp_det(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + return CImg(ptrs,k,k,1,1,true).det(); + } + + static double mp_diag(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3; + double *ptrd = &_mp_arg(1) + 1; + std::memset(ptrd,0,siz*siz*sizeof(double)); + for (unsigned int i = 3; i::nan(); + } + + static double mp_display_memory(_cimg_math_parser& mp) { + cimg::unused(mp); + std::fputc('\n',cimg::output()); + CImg title(128); + cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width); + mp.mem.display(title); + return cimg::type::nan(); + } + + static double mp_display(_cimg_math_parser& mp) { + const unsigned int + _siz = (unsigned int)mp.opcode[3], + siz = _siz?_siz:1; + const double *const ptr = &_mp_arg(1) + (_siz?1:0); + const int + w = (int)_mp_arg(4), + h = (int)_mp_arg(5), + d = (int)_mp_arg(6), + s = (int)_mp_arg(7); + CImg img; + if (w>0 && h>0 && d>0 && s>0) { + if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true); + else img.assign(ptr,siz).resize(w,h,d,s,-1); + } else img.assign(ptr,1,siz,1,1,true); + + CImg expr(mp.opcode[2] - 8); + const ulongT *ptrs = mp.opcode._data + 8; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + ((CImg::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr); + cimg::strellipsize(expr); + std::fputc('\n',cimg::output()); + img.display(expr._data); + return cimg::type::nan(); + } + + static double mp_div(_cimg_math_parser& mp) { + return _mp_arg(2)/_mp_arg(3); + } + + static double mp_dot(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[4]; + return CImg(&_mp_arg(2) + 1,1,siz,1,1,true). + dot(CImg(&_mp_arg(3) + 1,1,siz,1,1,true)); + } + + static double mp_do(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_body = ++mp.p_code, + *const p_cond = p_body + mp.opcode[3], + *const p_end = p_cond + mp.opcode[4]; + const unsigned int vsiz = (unsigned int)mp.opcode[5]; + if (mp.opcode[6]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[7]) mp.mem[mem_cond] = 0; + + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + do { + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } while (mp.mem[mem_cond]); + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_draw(_cimg_math_parser& mp) { + const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + unsigned int + dx = (unsigned int)mp.opcode[8], + dy = (unsigned int)mp.opcode[9], + dz = (unsigned int)mp.opcode[10], + dc = (unsigned int)mp.opcode[11]; + dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); + dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); + dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); + dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); + + const ulongT sizS = mp.opcode[2]; + if (sizS<(ulongT)dx*dy*dz*dc) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); + const float opacity = (float)_mp_arg(12); + + if (img._data) { + if (mp.opcode[13]!=~0U) { // Opacity mask specified + const ulongT sizM = mp.opcode[14]; + if (sizM<(ulongT)dx*dy*dz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); + img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); + } else img.draw_image(x,y,z,c,S,opacity); + } + return cimg::type::nan(); + } + + static double mp_echo(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type::nan(); } // No arguments + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + const CImg str = _str>'x'; + std::fprintf(cimg::output(),"\n%s",str._data); + return cimg::type::nan(); + } + + static double mp_ellipse(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + CImg color(img._spectrum,1,1,1,0); + bool is_invalid_arguments = false, is_outlined = false; + float r1 = 0, r2 = 0, angle = 0, opacity = 1; + unsigned int i = 4, pattern = ~0U; + int x0 = 0, y0 = 0; + if (i>=i_end) is_invalid_arguments = true; + else { + x0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + y0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + r1 = (float)_mp_arg(i++); + if (i>=i_end) r2 = r1; + else { + r2 = (float)_mp_arg(i++); + if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + + static double mp_eq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)==_mp_arg(3)); + } + + static double mp_erf(_cimg_math_parser& mp) { + return std::erf(_mp_arg(2)); + } + + static double mp_erfinv(_cimg_math_parser& mp) { + return cimg::erfinv(_mp_arg(2)); + } + + static double mp_exp(_cimg_math_parser& mp) { + return std::exp(_mp_arg(2)); + } + + static double mp_expr(_cimg_math_parser& mp) { + const unsigned int + sizs = (unsigned int)mp.opcode[3], + w = (unsigned int)mp.opcode[4], + h = (unsigned int)mp.opcode[5], + d = (unsigned int)mp.opcode[6], + s = (unsigned int)mp.opcode[7], + sizd = w*h*d*s; + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1); + CImg ss(sizs + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + if (!sizd) return CImg(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result + CImg(++ptrd,w,h,d,s,true) = CImg(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout); + return cimg::type::nan(); + } + + static double mp_eye(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int k = (unsigned int)mp.opcode[2]; + CImg(ptrd,k,k,1,1,true).identity_matrix(); + return cimg::type::nan(); + } + + static double mp_f2ui(_cimg_math_parser& mp) { + return (double)cimg::float2uint((float)_mp_arg(2)); + } + + static double mp_factorial(_cimg_math_parser& mp) { + return cimg::factorial((int)_mp_arg(2)); + } + + static double mp_fibonacci(_cimg_math_parser& mp) { + return cimg::fibonacci((int)_mp_arg(2)); + } + + static double mp_fill(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double + *ptrd = &_mp_arg(1), + *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0, + *const ptrs = &_mp_arg(4); + if (siz) ++ptrd; else ++siz; // Fill vector-valued slot + const CImg + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[5]; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + unsigned int it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + *ptrc = (double)it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return *ptrd; + } + + static double mp_find(_cimg_math_parser& mp) { + const int _step = (int)_mp_arg(6), step = _step?_step:-1; + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1); + if (ind<0 || ind>=(longT)siz) return -1.; + const double + *const ptrb = &_mp_arg(2) + 1, + *const ptre = ptrb + siz, + val = _mp_arg(4), + *ptr = ptrb + ind; + + // Forward search + if (step>0) { + while (ptr=ptre?-1.:(double)(ptr - ptrb); + } + + // Backward search. + while (ptr>=ptrb && *ptr!=val) ptr+=step; + return ptr0?0:siz1 - 1); + if (ind<0 || ind>=(longT)siz1) return -1.; + const double + *const ptr1b = &_mp_arg(2) + 1, + *const ptr1e = ptr1b + siz1, + *const ptr2b = &_mp_arg(4) + 1, + *const ptr2e = ptr2b + siz2, + *ptr1 = ptr1b + ind, + *p1 = 0, + *p2 = 0; + + // Forward search. + if (step>0) { + do { + while (ptr1=ptr1e) return -1.; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b && *ptr1!=*ptr2b) ptr1+=step; + if (ptr1=ptr1b); + return p2 + *const p_init = ++mp.p_code, + *const p_cond = p_init + mp.opcode[4], + *const p_body = p_cond + mp.opcode[5], + *const p_post = p_body + mp.opcode[6], + *const p_end = p_post + mp.opcode[7]; + const unsigned int vsiz = (unsigned int)mp.opcode[2]; + bool is_cond = false; + if (mp.opcode[8]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[9]) mp.mem[mem_cond] = 0; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + for (mp.p_code = p_init; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + + if (!mp.break_type) do { + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; + + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond && !mp.break_type) { + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + + for (mp.p_code = p_post; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } + } while (is_cond); + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_fsize(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const ulongT siz = (ulongT)mp.opcode[3]; + CImg ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::fsize(ss); + } + + static double mp_g(_cimg_math_parser& mp) { + cimg::unused(mp); + return cimg::grand(&mp.rng); + } + + static double mp_gauss(_cimg_math_parser& mp) { + const double x = _mp_arg(2), s = _mp_arg(3); + return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1); + } + +#ifdef cimg_mp_func_get + static double mp_get(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1); + const unsigned int + sizs = (unsigned int)mp.opcode[3], + sizd = (unsigned int)mp.opcode[4]; + const bool to_string = (bool)mp.opcode[5]; + CImg ss(sizs + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data); + else cimg_mp_func_get(ptrd,0,to_string,ss._data); + return cimg::type::nan(); + } +#endif + + static double mp_gcd(_cimg_math_parser& mp) { + return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3)); + } + +#ifdef cimg_mp_func_name + static double mp_name(_cimg_math_parser& mp) { + double *const ptr = &_mp_arg(1) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) std::memset(ptr,0,siz*sizeof(double)); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + cimg_mp_func_name(ind,ptr,siz); + } + return cimg::type::nan(); + } +#endif + + static double mp_gt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>_mp_arg(3)); + } + + static double mp_gte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>=_mp_arg(3)); + } + + static double mp_i(_cimg_math_parser& mp) { + return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y], + (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0); + } + + static double mp_if(_cimg_math_parser& mp) { + const bool is_cond = (bool)_mp_arg(2); + const ulongT + mem_left = mp.opcode[3], + mem_right = mp.opcode[4]; + const CImg + *const p_right = ++mp.p_code + mp.opcode[5], + *const p_end = p_right + mp.opcode[6]; + const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7]; + if (is_cond) for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + else for (mp.p_code = p_right; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.p_code==mp.p_break) --mp.p_code; + else mp.p_code = p_end - 1; + if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz); + return mp.mem[is_cond?mem_left:mem_right]; + } + + static double mp_image_d(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.depth(); + } + + static double mp_image_display(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + CImg title(256); + std::fputc('\n',cimg::output()); + cimg_snprintf(title,title._width,"[ Image #%u ]",ind); + img.display(title); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_h(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.height(); + } + + static double mp_image_median(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.median(); + } + + static double mp_image_norm(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.magnitude(); + } + + static double mp_image_print(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + CImg title(256); + std::fputc('\n',cimg::output()); + cimg_snprintf(title,title._width,"[ Image #%u ]",ind); + img.print(title); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_resize(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + const double + _w = mp.opcode[3]==~0U?-100:_mp_arg(3), + _h = mp.opcode[4]==~0U?-100:_mp_arg(4), + _d = mp.opcode[5]==~0U?-100:_mp_arg(5), + _s = mp.opcode[6]==~0U?-100:_mp_arg(6); + const unsigned int + w = (unsigned int)(_w>=0?_w:-_w*img.width()/100), + h = (unsigned int)(_h>=0?_h:-_h*img.height()/100), + d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100), + s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100), + interp = (int)_mp_arg(7); + if (mp.is_fill && img._data==mp.imgout._data) { + cimg::mutex(6,0); + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': " + "Cannot both fill and resize image (%u,%u,%u,%u) " + "to new dimensions (%u,%u,%u,%u).", + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s); + } + const unsigned int + boundary = (int)_mp_arg(8); + const float + cx = (float)_mp_arg(9), + cy = (float)_mp_arg(10), + cz = (float)_mp_arg(11), + cc = (float)_mp_arg(12); + img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_s(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.spectrum(); + } + + static double mp_image_sort(_cimg_math_parser& mp) { + const bool is_increasing = (bool)_mp_arg(3); + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()), + axis = (unsigned int)_mp_arg(4); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + img.sort(is_increasing, + axis==0 || axis=='x'?'x': + axis==1 || axis=='y'?'y': + axis==2 || axis=='z'?'z': + axis==3 || axis=='c'?'c':0); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_stats(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) CImg(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats(); + } + return cimg::type::nan(); + } + + static double mp_image_w(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width(); + } + + static double mp_image_wh(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height(); + } + + static double mp_image_whd(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height()*img.depth(); + } + + static double mp_image_whds(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height()*img.depth()*img.spectrum(); + } + + static double mp_increment(_cimg_math_parser& mp) { + return _mp_arg(2) + 1; + } + + static double mp_inrange(_cimg_math_parser& mp) { + const unsigned int sizd = (unsigned int)mp.opcode[2]; + const bool + include_m = (bool)_mp_arg(9), + include_M = (bool)_mp_arg(10); + if (!sizd) { // Scalar result + const double val = _mp_arg(3); + const double m = _mp_arg(5), M = _mp_arg(7); + if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val=M):(val>M)) && (include_m?(val<=m):(val=m) + ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val=M):(val>M)) && (include_m?(val<=m):(val::nan(); + } + + static double mp_int(_cimg_math_parser& mp) { + return (double)(longT)_mp_arg(2); + } + + static double mp_ioff(_cimg_math_parser& mp) { + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const CImg &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::is_directory(ss); + } + + static double mp_isin(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = _mp_arg(3); + for (unsigned int i = 4; i::is_inf(_mp_arg(2)); + } + + static double mp_isint(_cimg_math_parser& mp) { + return (double)((double)(longT)_mp_arg(2)==_mp_arg(2)); + } + + static double mp_isfile(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const ulongT siz = (ulongT)mp.opcode[3]; + CImg ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::is_file(ss); + } + + static double mp_isnan(_cimg_math_parser& mp) { + return (double)cimg::type::is_nan(_mp_arg(2)); + } + + static double mp_ixyzc(_cimg_math_parser& mp) { + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.imgin; + const double + x = _mp_arg(2), y = _mp_arg(3), + z = _mp_arg(4), c = _mp_arg(5); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx &img = mp.imgin; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), + z = oz + _mp_arg(4), c = oc + _mp_arg(5); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx vals(i_end - 4); + double *p = vals.data(); + for (unsigned int i = 4; i &img = mp.listin[indi]; + const int _step = (int)_mp_arg(5), step = _step?_step:-1; + const ulongT siz = (ulongT)img.size(); + longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1); + if (ind<0 || ind>=(longT)siz) return -1.; + const T + *const ptrb = img.data(), + *const ptre = img.end(), + *ptr = ptrb + ind; + const double val = _mp_arg(3); + + // Forward search + if (step>0) { + while (ptr=ptre?-1.:(double)(ptr - ptrb); + } + + // Backward search. + while (ptr>=ptrb && (double)*ptr!=val) ptr+=step; + return ptr &img = mp.listin[indi]; + const int _step = (bool)_mp_arg(6), step = _step?_step:-1; + const ulongT + siz1 = (ulongT)img.size(), + siz2 = (ulongT)mp.opcode[4]; + longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1); + if (ind<0 || ind>=(longT)siz1) return -1.; + const T + *const ptr1b = img.data(), + *const ptr1e = ptr1b + siz1, + *ptr1 = ptr1b + ind, + *p1 = 0; + const double + *const ptr2b = &_mp_arg(3) + 1, + *const ptr2e = ptr2b + siz2, + *p2 = 0; + + // Forward search. + if (step>0) { + do { + while (ptr1=ptr1e) return -1.; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b && *ptr1!=*ptr2b) ptr1+=step; + if (ptr1=ptr1b); + return p2 &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.listin[ind]; + const double + x = _mp_arg(3), y = _mp_arg(4), + z = _mp_arg(5), c = _mp_arg(6); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx &img = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), + z = oz + _mp_arg(5), c = oc + _mp_arg(6); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); + return *mp.list_median[ind]; + } + + static double mp_list_norm(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + if (!mp.list_norm) mp.list_norm.assign(mp.listin._width); + if (!mp.list_norm[ind]) CImg::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]); + return *mp.list_norm[ind]; + } + + static double mp_list_set_ioff(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), y = (int)_mp_arg(4), + z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; + const int + x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_set_Joff_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(3)), + y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(3)), + y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_spectrum(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._spectrum; + } + + static double mp_list_stats(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + k = (unsigned int)mp.opcode[3]; + if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); + if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); + return mp.list_stats(ind,k); + } + + static double mp_list_wh(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height; + } + + static double mp_list_whd(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; + } + + static double mp_list_whds(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; + } + + static double mp_list_width(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width; + } + + static double mp_list_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4), + vsiz = (unsigned int)mp.opcode[5]; + const CImg &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_list_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7), + vsiz = (unsigned int)mp.opcode[8]; + const CImg &img = mp.listin[ind]; + const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_list_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4), + vsiz = (unsigned int)mp.opcode[5]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z]; + const CImg &img = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_list_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7), + vsiz = (unsigned int)mp.opcode[8]; + const CImg &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_log(_cimg_math_parser& mp) { + return std::log(_mp_arg(2)); + } + + static double mp_log10(_cimg_math_parser& mp) { + return std::log10(_mp_arg(2)); + } + + static double mp_log2(_cimg_math_parser& mp) { + return cimg::log2(_mp_arg(2)); + } + + static double mp_logical_and(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (!val_left) { mp.p_code = p_end - 1; return 0; } + const ulongT mem_right = mp.opcode[3]; + for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_logical_not(_cimg_math_parser& mp) { + return (double)!_mp_arg(2); + } + + static double mp_logical_or(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (val_left) { mp.p_code = p_end - 1; return 1; } + const ulongT mem_right = mp.opcode[3]; + for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_lowercase(_cimg_math_parser& mp) { + return cimg::lowercase(_mp_arg(2)); + } + + static double mp_lt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<_mp_arg(3)); + } + + static double mp_lte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<=_mp_arg(3)); + } + + static double mp_matrix_eig(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + CImg val, vec; + CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); + CImg(ptrd,1,k,1,1,true) = val; + CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); + return cimg::type::nan(); + } + + static double mp_matrix_invert(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + const bool use_LU = (bool)_mp_arg(4); + CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(use_LU); + return cimg::type::nan(); + } + + static double mp_matrix_mul(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode[4], + l = (unsigned int)mp.opcode[5], + m = (unsigned int)mp.opcode[6]; + CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); + return cimg::type::nan(); + } + + static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + const bool use_LU = (bool)_mp_arg(5); + CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU); + return cimg::type::nan(); + } + + static double mp_matrix_svd(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + CImg U, S, V; + CImg(ptr1,k,l,1,1,true).SVD(U,S,V); + CImg(ptrd,k,l,1,1,true) = U; + CImg(ptrd + k*l,1,k,1,1,true) = S; + CImg(ptrd + k*l + k,k,k,1,1,true) = V; + return cimg::type::nan(); + } + + static double mp_max(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; } + } + return val; + } + + static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref, + const longT siz, const long inc) { + const longT + off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind, + eoff = off + (siz - 1)*inc; + if (off<0 || eoff>=mp.mem.width()) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " + "Out-of-bounds variable pointer " + "(length: %ld, increment: %ld, offset start: %ld, " + "offset end: %ld, offset max: %u).", + mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1); + return &mp.mem[off]; + } + + static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref, + const longT siz, const long inc, const bool is_out) { + const unsigned ind = (unsigned int)p_ref[1]; + const CImg &img = is_out? + (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]): + (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]); + const bool is_relative = (bool)p_ref[2]; + int ox, oy, oz, oc; + longT off = 0; + if (is_relative) { + ox = (int)mp.mem[_cimg_mp_slot_x]; + oy = (int)mp.mem[_cimg_mp_slot_y]; + oz = (int)mp.mem[_cimg_mp_slot_z]; + oc = (int)mp.mem[_cimg_mp_slot_c]; + off = img.offset(ox,oy,oz,oc); + } + if ((*p_ref)%2) { + const int + x = (int)mp.mem[p_ref[3]], + y = (int)mp.mem[p_ref[4]], + z = (int)mp.mem[p_ref[5]], + c = *p_ref==5?0:(int)mp.mem[p_ref[6]]; + off+=img.offset(x,y,z,c); + } else off+=(longT)mp.mem[p_ref[3]]; + const longT eoff = off + (siz - 1)*inc; + if (off<0 || eoff>=(longT)img.size()) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " + "Out-of-bounds image pointer " + "(length: %ld, increment: %ld, offset start: %ld, " + "offset end: %ld, offset max: %lu).", + mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1); + return (float*)&img[off]; + } + + static double mp_memcopy(_cimg_math_parser& mp) { + longT siz = (longT)_mp_arg(4); + const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6); + const float + _opacity = (float)_mp_arg(7), + opacity = (float)cimg::abs(_opacity), + omopacity = 1 - std::max(_opacity,0.f); + if (siz>0) { + const bool + is_doubled = mp.opcode[8]<=1, + is_doubles = mp.opcode[15]<=1; + if (is_doubled && is_doubles) { // (double*) <- (double*) + double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); + const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); + if (inc_d==1 && inc_s==1 && _opacity>=1) { + if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double)); + else std::memmove(ptrd,ptrs,siz*sizeof(double)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) { + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else { // Overlapping buffers + CImg buf((unsigned int)siz); + cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; } + ptrs = buf; + if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } + } + } + } else if (is_doubled && !is_doubles) { // (double*) <- (float*) + double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); + const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false); + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else if (!is_doubled && is_doubles) { // (float*) <- (double*) + float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true); + const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); + if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; } + } else { // (float*) <- (float*) + float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true); + const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false); + if (inc_d==1 && inc_s==1 && _opacity>=1) { + if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float)); + else std::memmove(ptrd,ptrs,siz*sizeof(float)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) { + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else { // Overlapping buffers + CImg buf((unsigned int)siz); + cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; } + ptrs = buf; + if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } + } + } + } + } + return _mp_arg(1); + } + + static double mp_min(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i(ptrd,wS,wD,1,1,true) = CImg(ptrS,wS,hS,1,1,false). + project_matrix(CImg(ptrD,wD,hS,1,1,true),method,max_iter,max_residual); + return cimg::type::nan(); + } + + static double mp_mul(_cimg_math_parser& mp) { + return _mp_arg(2)*_mp_arg(3); + } + + static double mp_mul2(_cimg_math_parser& mp) { + return _mp_arg(2)*_mp_arg(3)*_mp_arg(4); + } + + static double mp_neq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)!=_mp_arg(3)); + } + + static double mp_norm0(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + switch (i_end - 3) { + case 1 : return _mp_arg(3)!=0; + case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0); + } + double res = 0; + for (unsigned int i = 3; ires) res = val; + } + return res; + } + + static double mp_normp(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + if (i_end==4) return cimg::abs(_mp_arg(3)); + const double p = (double)mp.opcode[3]; + double res = 0; + for (unsigned int i = 4; i0?res:0.; + } + + static double mp_permutations(_cimg_math_parser& mp) { + return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4)); + } + + static double mp_polygon(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + bool is_invalid_arguments = i_end<=4, is_outlined = false; + if (!is_invalid_arguments) { + int nbv = (int)_mp_arg(4); + if (!nbv) is_invalid_arguments = true; + else { + if (nbv<0) { nbv = -nbv; is_outlined = true; } + CImg points(nbv,2,1,1,0); + CImg color(img._spectrum,1,1,1,0); + float opacity = 1; + unsigned int i = 5, pattern=~0U; + cimg_foroff(points,k) if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + + static double mp_pow(_cimg_math_parser& mp) { + const double v = _mp_arg(2), p = _mp_arg(3); + return std::pow(v,p); + } + + static double mp_pow0_25(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return std::sqrt(std::sqrt(val)); + } + + static double mp_pow3(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val; + } + + static double mp_pow4(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val*val; + } + + static double mp_print(_cimg_math_parser& mp) { + const double val = _mp_arg(1); + const bool print_char = (bool)mp.opcode[3]; + cimg_pragma_openmp(critical(mp_print)) + { + CImg _expr(mp.opcode[2] - 4); + const ulongT *ptrs = mp.opcode._data + 4; + cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(_expr); + cimg::mutex(6); + if (print_char) + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'", + _expr._data,val,(int)val); + else + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g", + _expr._data,val); + std::fflush(cimg::output()); + cimg::mutex(6,0); + } + return val; + } + + static double mp_prod(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[4]; + + if (nb_it>0) { + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + double it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + *ptrc = it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + mp.break_type = _break_type; + } + + mp.p_code = p_end - 1; + return *ptrs; + } + + static double mp_rol(_cimg_math_parser& mp) { + return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3)); + } + + static double mp_ror(_cimg_math_parser& mp) { + return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3)); + } + + static double mp_rot2d(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float + theta = (float)_mp_arg(2)*cimg::PI/180, + ca = std::cos(theta), + sa = std::sin(theta); + *(ptrd++) = ca; + *(ptrd++) = -sa; + *(ptrd++) = sa; + *ptrd = ca; + return cimg::type::nan(); + } + + static double mp_rot3d(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); + CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + return cimg::type::nan(); + } + + static double mp_round(_cimg_math_parser& mp) { + return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); + } + + static double mp_self_add(_cimg_math_parser& mp) { + return _mp_arg(1)+=_mp_arg(2); + } + + static double mp_self_bitwise_and(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val & (longT)_mp_arg(2)); + } + + static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val<<(unsigned int)_mp_arg(2)); + } + + static double mp_self_bitwise_or(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val | (longT)_mp_arg(2)); + } + + static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val>>(unsigned int)_mp_arg(2)); + } + + static double mp_self_decrement(_cimg_math_parser& mp) { + return --_mp_arg(1); + } + + static double mp_self_increment(_cimg_math_parser& mp) { + return ++_mp_arg(1); + } + + static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2]; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode[2] = mp.opcode[4]; // Scalar argument + l_opcode.swap(mp.opcode); + ulongT &target = mp.opcode[1]; + while (siz-->0) { target = ptrd++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + ulongT &target = mp.opcode[1], &argument = mp.opcode[2]; + while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_self_mul(_cimg_math_parser& mp) { + return _mp_arg(1)*=_mp_arg(2); + } + + static double mp_self_div(_cimg_math_parser& mp) { + return _mp_arg(1)/=_mp_arg(2); + } + + static double mp_self_modulo(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = cimg::mod(val,_mp_arg(2)); + } + + static double mp_self_pow(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = std::pow(val,_mp_arg(2)); + } + + static double mp_self_sub(_cimg_math_parser& mp) { + return _mp_arg(1)-=_mp_arg(2); + } + + static double mp_set_ioff(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + x = (int)_mp_arg(2), y = (int)_mp_arg(3), + z = (int)_mp_arg(4), c = (int)_mp_arg(5); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; + const int + x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Ixyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + x = (int)_mp_arg(2), + y = (int)_mp_arg(3), + z = (int)_mp_arg(4); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const int + x = (int)_mp_arg(2), + y = (int)_mp_arg(3), + z = (int)_mp_arg(4); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_set_Joff_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Jxyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(2)), + y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(2)), + y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_shift(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + const int + shift = (int)_mp_arg(4), + boundary_conditions = (int)_mp_arg(5); + CImg(ptrd,siz,1,1,1,true) = CImg(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions); + return cimg::type::nan(); + } + + static double mp_sign(_cimg_math_parser& mp) { + return cimg::sign(_mp_arg(2)); + } + + static double mp_sin(_cimg_math_parser& mp) { + return std::sin(_mp_arg(2)); + } + + static double mp_sinc(_cimg_math_parser& mp) { + return cimg::sinc(_mp_arg(2)); + } + + static double mp_sinh(_cimg_math_parser& mp) { + return std::sinh(_mp_arg(2)); + } + + static double mp_solve(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode[4], + l = (unsigned int)mp.opcode[5], + m = (unsigned int)mp.opcode[6]; + CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,false).solve(CImg(ptr1,k,l,1,1,true)); + return cimg::type::nan(); + } + + static double mp_sort(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const bool is_increasing = (bool)_mp_arg(4); + const unsigned int + siz = (unsigned int)mp.opcode[3], + nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5), + siz_elt = (unsigned int)_mp_arg(6); + const ulongT sn = siz_elt*nb_elts; + if (sn>siz || siz_elt<1) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': " + "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid " + "for sorting a vector of size %u.", + mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz); + CImg(ptrd,siz_elt,nb_elts,1,1,true) = CImg(ptrs,siz_elt,nb_elts,1,1,true). + get_sort(is_increasing,siz_elt>1?'y':0); + if (sn(ptrd + sn,siz - sn,1,1,1,true) = CImg(ptrs + sn,siz - sn,1,1,1,true); + return cimg::type::nan(); + } + + static double mp_sqr(_cimg_math_parser& mp) { + return cimg::sqr(_mp_arg(2)); + } + + static double mp_sqrt(_cimg_math_parser& mp) { + return std::sqrt(_mp_arg(2)); + } + + static double mp_srand(_cimg_math_parser& mp) { + mp.rng = (cimg_uint64)_mp_arg(2); + return cimg::type::nan(); + } + + static double mp_srand0(_cimg_math_parser& mp) { + cimg::srand(&mp.rng); + +#if cimg_use_openmp!=0 + mp.rng+=omp_get_thread_num(); +#endif + return cimg::type::nan(); + } + + static double mp_std(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i0) mp.mem[ptrd++] = (double)*(ptrs++); + return cimg::type::nan(); + } + +#ifdef cimg_mp_func_store + static double mp_store(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2), + *ptr2 = &_mp_arg(4) + 1; + const unsigned int + siz1 = (unsigned int)mp.opcode[3], + siz2 = (unsigned int)mp.opcode[5]; + const int + w = (int)_mp_arg(6), + h = (int)_mp_arg(7), + d = (int)_mp_arg(8), + s = (int)_mp_arg(9); + + const bool is_compressed = (bool)_mp_arg(10); + if (w<0 || h<0 || d<0 || s<0) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': " + "Specified image dimensions (%d,%d,%d,%d) are invalid.", + pixel_type(),w,h,d,s); + CImg ss(siz2 + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i]; + ss.back() = 0; + if (siz1) cimg_mp_func_store(ptr1 + 1,siz1, + (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); + else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); + return cimg::type::nan(); + } +#endif + + static double mp_stov(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2); + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)_mp_arg(4); + const bool is_strict = (bool)_mp_arg(5); + double val = cimg::type::nan(); + if (ind<0 || ind>=(longT)siz) return val; + if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val; + + CImg ss(siz + 1 - ind); + ptrs+=1 + ind; + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + + const char *s = ss._data; + while (*s && *s<=32) ++s; + const bool is_negative = *s=='-'; + if (is_negative || *s=='+') ++s; + int err = 0; + char sep; + + if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number + unsigned int ival; + err = cimg_sscanf(s + 2,"%x%c",&ival,&sep); + if (err>0) val = (double)ival; + } else if (*s>32) { // Decimal number + err = cimg_sscanf(s,"%lf%c",&val,&sep); +#if cimg_OS==2 + // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able + // to read those particular values. + if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) { + if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type::inf(); err = 1 + (s[3]!=0); } + else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type::nan(); err = 1 + (s[3]!=0); } + } +#endif + } + if (err<=0 || (is_strict && err!=1)) return cimg::type::nan(); + if (is_negative) val = -val; + return val; + } + + static double mp_string(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(4 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + const CImg str = _str>'x'; + const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]); + std::memset(ptrd,0,mp.opcode[2]*sizeof(double)); + for (unsigned int k = 0; k::nan(); + } + + static double mp_sub(_cimg_math_parser& mp) { + return _mp_arg(2) - _mp_arg(3); + } + + static double mp_sum(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i(ptrs,k,k,1,1,true).trace(); + } + + static double mp_transpose(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + CImg(ptrd,l,k,1,1,true) = CImg(ptrs,k,l,1,1,true).get_transpose(); + return cimg::type::nan(); + } + + static double mp_u(_cimg_math_parser& mp) { + return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng); + } + + static double mp_ui2f(_cimg_math_parser& mp) { + return (double)cimg::uint2float((unsigned int)_mp_arg(2)); + } + + static double mp_uppercase(_cimg_math_parser& mp) { + return cimg::uppercase(_mp_arg(2)); + } + + static double mp_var(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i::nan(); + } + + static double mp_vector_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const longT + length = (longT)mp.opcode[3], + start = (longT)_mp_arg(4), + sublength = (longT)mp.opcode[5], + step = (longT)_mp_arg(6); + if (start<0 || start + step*(sublength-1)>=length) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': " + "Out-of-bounds sub-vector request " + "(length: %ld, start: %ld, sub-length: %ld, step: %ld).", + mp.imgin.pixel_type(),length,start,sublength,step); + ptrs+=start; + if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double)); + else for (longT k = 0; k::nan(); + } + + static double mp_vector_init(_cimg_math_parser& mp) { + unsigned int + ptrs = 4U, + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[3]; + switch (mp.opcode[2] - 4) { + case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given + case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break; + default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; } + } + return cimg::type::nan(); + } + + static double mp_vector_eq(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(4) + 1; + unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n; + const int N = (int)_mp_arg(6); + const bool case_sensitive = (bool)_mp_arg(7); + bool still_equal = true; + double value; + if (!N) return true; + + // Compare all values. + if (N<0) { + if (p1>0 && p2>0) { // Vector == vector + if (p1!=p2) return false; + if (case_sensitive) + while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++); + else + while (still_equal && p1--) + still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); + return still_equal; + } else if (p1>0 && !p2) { // Vector == scalar + value = _mp_arg(4); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && p1--) still_equal = *(ptr1++)==value; + return still_equal; + } else if (!p1 && p2>0) { // Scalar == vector + value = _mp_arg(2); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && p2--) still_equal = *(ptr2++)==value; + return still_equal; + } else { // Scalar == scalar + if (case_sensitive) return _mp_arg(2)==_mp_arg(4); + else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); + } + } + + // Compare only first N values. + if (p1>0 && p2>0) { // Vector == vector + n = cimg::min((unsigned int)N,p1,p2); + if (case_sensitive) + while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++); + else + while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); + return still_equal; + } else if (p1>0 && !p2) { // Vector == scalar + n = std::min((unsigned int)N,p1); + value = _mp_arg(4); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && n--) still_equal = *(ptr1++)==value; + return still_equal; + } else if (!p1 && p2>0) { // Scalar == vector + n = std::min((unsigned int)N,p2); + value = _mp_arg(2); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && n--) still_equal = *(ptr2++)==value; + return still_equal; + } // Scalar == scalar + if (case_sensitive) return _mp_arg(2)==_mp_arg(4); + return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); + } + + static double mp_vector_lerp(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double *ptrd = &_mp_arg(1) + 1; + const double + *ptrs1 = &_mp_arg(3) + 1, + *ptrs2 = &_mp_arg(4) + 1, + t = _mp_arg(5); + for (unsigned int k = 0; k::nan(); + } + + static double mp_vector_off(_cimg_math_parser& mp) { + const unsigned int + ptr = (unsigned int)mp.opcode[2] + 1, + siz = (unsigned int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type::nan(); + } + + static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(4); + l_opcode[2] = mp.opcode[4]; // Scalar argument1 + l_opcode.swap(mp.opcode); + ulongT &argument2 = mp.opcode[3]; + while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode.swap(mp.opcode); + ulongT &argument = mp.opcode[2]; + while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,5); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode[4] = mp.opcode[6]; // Scalar argument3 + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs1 = (unsigned int)mp.opcode[4] + 1, + ptrs2 = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; + while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_neq(_cimg_math_parser& mp) { + return !mp_vector_eq(mp); + } + + static double mp_vector_print(_cimg_math_parser& mp) { + const bool print_string = (bool)mp.opcode[4]; + cimg_pragma_openmp(critical(mp_vector_print)) + { + CImg _expr(mp.opcode[2] - 5); + const ulongT *ptrs = mp.opcode._data + 5; + cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(_expr); + unsigned int + ptr = (unsigned int)mp.opcode[1] + 1, + siz0 = (unsigned int)mp.opcode[3], + siz = siz0; + cimg::mutex(6); + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data); + unsigned int count = 0; + while (siz-->0) { + if (count>=64 && siz>=64) { + std::fprintf(cimg::output(),"...,"); + ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64; + siz = 64; + } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":""); + ++count; + } + if (print_string) { + CImg str(siz0 + 1); + ptr = (unsigned int)mp.opcode[1] + 1; + for (unsigned int k = 0; k::nan(); + } + + static double mp_vector_resize(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4]; + const int + interpolation = (int)_mp_arg(5), + boundary_conditions = (int)_mp_arg(6); + if (p2) { // Resize vector + const double *const ptrs = &_mp_arg(3) + 1; + CImg(ptrd,p1,1,1,1,true) = CImg(ptrs,p2,1,1,1,true). + get_resize(p1,1,1,1,interpolation,boundary_conditions); + } else { // Resize scalar + const double value = _mp_arg(3); + CImg(ptrd,p1,1,1,1,true) = CImg(1,1,1,1,value).resize(p1,1,1,1,interpolation, + boundary_conditions); + } + return cimg::type::nan(); + } + + static double mp_vector_reverse(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int p1 = (unsigned int)mp.opcode[3]; + CImg(ptrd,p1,1,1,1,true) = CImg(ptrs,p1,1,1,1,true).get_mirror('x'); + return cimg::type::nan(); + } + + static double mp_vector_set_off(_cimg_math_parser& mp) { + const unsigned int + ptr = (unsigned int)mp.opcode[2] + 1, + siz = (unsigned int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1); + return _mp_arg(1); + } + +#define _cimg_mp_vfunc(func) \ + const longT sizd = (longT)mp.opcode[2];\ + const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \ + double *const ptrd = &_mp_arg(1) + (sizd?1:0); \ + cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \ + { CImg vec(nbargs); double res; \ + cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \ + cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \ + func; ptrd[k] = res; \ + }} \ + return sizd?cimg::type::nan():*ptrd; + + static double _mp_vargkth(CImg& vec) { + const double val = (+vec).get_shared_points(1,vec.width() - 1). + kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)); + cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.; + return 1.; + } + + static double mp_vargkth(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = _mp_vargkth(vec)); + } + + static double mp_vargmax(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data())); + } + + static double mp_vargmaxabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data())); + } + + static double mp_vargmin(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data())); + } + + static double mp_vargminabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data())); + } + + static double mp_vavg(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.mean()); + } + + static double mp_vkth(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1). + kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2))); + } + + static double mp_vmax(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.max()); + } + + static double mp_vmaxabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.maxabs()); + } + + static double mp_vmedian(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.median()); + } + + static double mp_vmin(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.min()); + } + + static double mp_vminabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.minabs()); + } + + static double mp_vprod(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.product()); + } + + static double mp_vstd(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3])); + } + + static double mp_vsum(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.sum()); + } + + static double mp_vvar(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.get_stats()[3]); + } + + static double mp_vtos(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + sizd = (unsigned int)mp.opcode[2], + sizs = (unsigned int)mp.opcode[4]; + std::memset(ptrd,0,sizd*sizeof(double)); + const int nb_digits = (int)_mp_arg(5); + CImg format(8); + switch (nb_digits) { + case -1 : std::strcpy(format,"%g"); break; + case 0 : std::strcpy(format,"%.17g"); break; + default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits); + } + CImg str; + if (sizs) { // Vector expression + const double *ptrs = &_mp_arg(3) + 1; + CImg(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str); + } else { // Scalar expression + str.assign(sizd + 1); + cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); + } + const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1); + CImg(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); + return cimg::type::nan(); + } + + static double mp_while(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_cond = ++mp.p_code, + *const p_body = p_cond + mp.opcode[3], + *const p_end = p_body + mp.opcode[4]; + const unsigned int vsiz = (unsigned int)mp.opcode[5]; + bool is_cond = false; + if (mp.opcode[6]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[7]) mp.mem[mem_cond] = 0; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + do { + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond && !mp.break_type) // Evaluate body + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } while (is_cond); + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3), + vsiz = (unsigned int)mp.opcode[4]; + const CImg &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6), + vsiz = (unsigned int)mp.opcode[7]; + const CImg &img = mp.imgin; + const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3), + vsiz = (unsigned int)mp.opcode[4]; + const CImg &img = mp.imgin; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], + oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z]; + const longT + off = img.offset(ox,oy,oz) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6), + vsiz = (unsigned int)mp.opcode[7]; + const CImg &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + +#undef _mp_arg + + }; // struct _cimg_math_parser {} + +#define _cimg_create_pointwise_functions(name,func,min_size) \ + CImg& name() { \ + if (is_empty()) return *this; \ + cimg_openmp_for(*this,func((double)*ptr),min_size); \ + return *this; \ + } \ + CImg get_##name() const { \ + return CImg(*this,false).name(); \ + } + + //! Compute the square value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqr().normalize(0,255)).display(); + \endcode + \image html ref_sqr.jpg + **/ + _cimg_create_pointwise_functions(sqr,cimg::sqr,524288) + + //! Compute the square root of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqrt().normalize(0,255)).display(); + \endcode + \image html ref_sqrt.jpg + **/ + _cimg_create_pointwise_functions(sqrt,std::sqrt,8192) + + //! Compute the exponential of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(exp,std::exp,4096) + + //! Compute the logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm + \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log,std::log,262144) + + //! Compute the base-2 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm + \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log2,cimg::log2,4096) + + //! Compute the base-10 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm + \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log10,std::log10,4096) + + //! Compute the absolute value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(abs,cimg::abs,524288) + + //! Compute the sign of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign + \f$\mathrm{sign}(I_{(x,y,z,c)})\f$. + \note + - The sign is set to: + - \c 1 if pixel value is strictly positive. + - \c -1 if pixel value is strictly negative. + - \c 0 if pixel value is equal to \c 0. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sign,cimg::sign,32768) + + //! Compute the cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(cos,std::cos,8192) + + //! Compute the sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sin,std::sin,8192) + + //! Compute the sinc of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc + \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sinc,cimg::sinc,2048) + + //! Compute the tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(tan,std::tan,2048) + + //! Compute the hyperbolic cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine + \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(cosh,std::cosh,2048) + + //! Compute the hyperbolic sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine + \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sinh,std::sinh,2048) + + //! Compute the hyperbolic tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent + \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(tanh,std::tanh,2048) + + //! Compute the arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine + \f$\mathrm{acos}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(acos,std::acos,8192) + + //! Compute the arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine + \f$\mathrm{asin}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(asin,std::asin,8192) + + //! Compute the arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent + \f$\mathrm{atan}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(atan,std::atan,8192) + + //! Compute the arctangent2 of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 + \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$. + \param img Image whose pixel values specify the second argument of the \c atan2() function. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2' + img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2' + img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value + (img_x,img_y,img_atan2).display(); + \endcode + **/ + template + CImg& atan2(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return atan2(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_atan2(const CImg& img) const { + return CImg(*this,false).atan2(img); + } + + //! Compute the hyperbolic arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh + \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(acosh,cimg::acosh,8192) + + //! Compute the hyperbolic arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine + \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(asinh,cimg::asinh,8192) + + //! Compute the hyperbolic arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent + \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(atanh,cimg::atanh,8192) + + //! In-place pointwise multiplication. + /** + Compute the pointwise multiplication between the image instance and the specified input image \c img. + \param img Input image, as the second operand of the multiplication. + \note + - Similar to operator+=(const CImg&), except that it performs a pointwise multiplication + instead of an addition. + - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg&) instead. + \par Example + \code + CImg + img("reference.jpg"), + shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false); + shade.normalize(0,1); + (img,shade,img.get_mul(shade)).display(); + \endcode + **/ + template + CImg& mul(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return mul(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_mul(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).mul(img); + } + + //! In-place pointwise division. + /** + Similar to mul(const CImg&), except that it performs a pointwise division instead of a multiplication. + **/ + template + CImg& div(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return div(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_div(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).div(img); + } + + //! Raise each pixel value to a specified power. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$. + \param p Exponent value. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img0("reference.jpg"), // Load reference color image + img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8 + img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5 + (img0,img1,img2).display(); + \endcode + **/ + CImg& pow(const double p) { + if (is_empty()) return *this; + if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; } + if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; } + if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; } + if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; } + if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; } + if (p==0) return fill((T)1); + if (p==0.5) return sqrt(); + if (p==1) return *this; + if (p==2) return sqr(); + if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; } + if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; } + cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024); + return *this; + } + + //! Raise each pixel value to a specified power \newinstance. + CImg get_pow(const double p) const { + return CImg(*this,false).pow(p); + } + + //! Raise each pixel value to a power, specified from an expression. + /** + Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. + **/ + CImg& pow(const char *const expression) { + return pow((+*this)._fill(expression,true,1,0,0,"pow",this)); + } + + //! Raise each pixel value to a power, specified from an expression \newinstance. + CImg get_pow(const char *const expression) const { + return CImg(*this,false).pow(expression); + } + + //! Raise each pixel value to a power, pointwisely specified from another image. + /** + Similar to operator+=(const CImg& img), except that it performs an exponentiation instead of an addition. + **/ + template + CImg& pow(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return pow(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_pow(const CImg& img) const { + return CImg(*this,false).pow(img); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const unsigned int n=1) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::rol(*ptr,n),32768); + return *this; + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const unsigned int n=1) const { + return (+*this).rol(n); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const char *const expression) { + return rol((+*this)._fill(expression,true,1,0,0,"rol",this)); + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const char *const expression) const { + return (+*this).rol(expression); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const CImg&), except that it performs a left rotation instead of a left shift. + **/ + template + CImg& rol(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return rol(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_rol(const CImg& img) const { + return (+*this).rol(img); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const unsigned int n=1) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::ror(*ptr,n),32768); + return *this; + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const unsigned int n=1) const { + return (+*this).ror(n); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const char *const expression) { + return ror((+*this)._fill(expression,true,1,0,0,"ror",this)); + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const char *const expression) const { + return (+*this).ror(expression); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const CImg&), except that it performs a right rotation instead of a right shift. + **/ + template + CImg& ror(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return ror(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_ror(const CImg& img) const { + return (+*this).ror(img); + } + + //! Pointwise min operator between instance image and a value. + /** + \param val Value used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& min(const T& value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,std::min(*ptr,value),65536); + return *this; + } + + //! Pointwise min operator between instance image and a value \newinstance. + CImg get_min(const T& value) const { + return (+*this).min(value); + } + + //! Pointwise min operator between two images. + /** + \param img Image used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& min(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return min(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_min(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).min(img); + } + + //! Pointwise min operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& min(const char *const expression) { + return min((+*this)._fill(expression,true,1,0,0,"min",this)); + } + + //! Pointwise min operator between an image and an expression \newinstance. + CImg get_min(const char *const expression) const { + return CImg(*this,false).min(expression); + } + + //! Pointwise max operator between instance image and a value. + /** + \param val Value used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& max(const T& value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,std::max(*ptr,value),65536); + return *this; + } + + //! Pointwise max operator between instance image and a value \newinstance. + CImg get_max(const T& value) const { + return (+*this).max(value); + } + + //! Pointwise max operator between two images. + /** + \param img Image used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& max(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return max(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_max(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).max(img); + } + + //! Pointwise max operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& max(const char *const expression) { + return max((+*this)._fill(expression,true,1,0,0,"max",this)); + } + + //! Pointwise max operator between an image and an expression \newinstance. + CImg get_max(const char *const expression) const { + return CImg(*this,false).max(expression); + } + + //! Pointwise minabs operator between instance image and a value. + /** + \param val Value used as the reference argument of the minabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& minabs(const T& value) { + if (is_empty()) return *this; + const T absvalue = cimg::abs(value); + cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536); + return *this; + } + + //! Pointwise minabs operator between instance image and a value \newinstance. + CImg get_minabs(const T& value) const { + return (+*this).minabs(value); + } + + //! Pointwise minabs operator between two images. + /** + \param img Image used as the reference argument of the minabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& minabs(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return minabs(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_minabs(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).minabs(img); + } + + //! Pointwise minabs operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& minabs(const char *const expression) { + return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this)); + } + + //! Pointwise minabs operator between an image and an expression \newinstance. + CImg get_minabs(const char *const expression) const { + return CImg(*this,false).minabs(expression); + } + + //! Pointwise maxabs operator between instance image and a value. + /** + \param val Value used as the reference argument of the maxabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& maxabs(const T& value) { + if (is_empty()) return *this; + const T absvalue = cimg::abs(value); + cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536); + return *this; + } + + //! Pointwise maxabs operator between instance image and a value \newinstance. + CImg get_maxabs(const T& value) const { + return (+*this).maxabs(value); + } + + //! Pointwise maxabs operator between two images. + /** + \param img Image used as the reference argument of the maxabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& maxabs(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return maxabs(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_maxabs(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).maxabs(img); + } + + //! Pointwise maxabs operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& maxabs(const char *const expression) { + return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this)); + } + + //! Pointwise maxabs operator between an image and an expression \newinstance. + CImg get_maxabs(const char *const expression) const { + return CImg(*this,false).maxabs(expression); + } + + //! Return a reference to the minimum pixel value. + /** + **/ + T& min() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min; + cimg_for(*this,ptrs,T) if (*ptrsmax_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value \const. + const T& max() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max; + cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value in absolute value. + /** + **/ + T& maxabs() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "maxabs(): Empty instance.", + cimg_instance); + T *ptr_maxabs = _data; + T maxabs_value = *ptr_maxabs; + cimg_for(*this,ptrs,T) { + const T ma = cimg::abs(*ptrs); + if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; } + } + return *ptr_maxabs; + } + + //! Return a reference to the maximum pixel value in absolute value \const. + const T& maxabs() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "maxabs(): Empty instance.", + cimg_instance); + const T *ptr_maxabs = _data; + T maxabs_value = *ptr_maxabs; + cimg_for(*this,ptrs,T) { + const T ma = cimg::abs(*ptrs); + if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; } + } + return *ptr_maxabs; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value. + /** + \param[out] max_val Maximum pixel value. + **/ + template + T& min_max(t& max_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value \const. + template + const T& min_max(t& max_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + const T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the maximum pixel value as well as the minimum pixel value. + /** + \param[out] min_val Minimum pixel value. + **/ + template + T& max_min(t& min_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val=size()) return max(); + CImg arr(*this,false); + ulongT l = 0, ir = size() - 1; + for ( ; ; ) { + if (ir<=l + 1) { + if (ir==l + 1 && arr[ir]>1; + cimg::swap(arr[mid],arr[l + 1]); + if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); + if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]); + if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]); + ulongT i = l + 1, j = ir; + const T pivot = arr[l + 1]; + for ( ; ; ) { + do ++i; while (arr[i]pivot); + if (j=k) ir = j - 1; + if (j<=k) l = i; + } + } + } + + //! Return the median pixel value. + /** + **/ + T median() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "median(): Empty instance.", + cimg_instance); + const ulongT s = size(); + switch (s) { + case 1 : return _data[0]; + case 2 : return cimg::median(_data[0],_data[1]); + case 3 : return cimg::median(_data[0],_data[1],_data[2]); + case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]); + case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]); + case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]); + case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8], + _data[9],_data[10],_data[11],_data[12]); + } + const T res = kth_smallest(s>>1); + return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2); + } + + //! Return the product of all the pixel values. + /** + **/ + double product() const { + if (is_empty()) return 0; + double res = 1; + cimg_for(*this,ptrs,T) res*=(double)*ptrs; + return res; + } + + //! Return the sum of all the pixel values. + /** + **/ + double sum() const { + double res = 0; + cimg_for(*this,ptrs,T) res+=(double)*ptrs; + return res; + } + + //! Return the average pixel value. + /** + **/ + double mean() const { + double res = 0; + cimg_for(*this,ptrs,T) res+=(double)*ptrs; + return res/size(); + } + + //! Return the variance of the pixel values. + /** + \param variance_method Method used to estimate the variance. Can be: + - \c 0: Second moment, computed as + \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = + 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$ + with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$. + - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$. + - \c 2: Least median of squares. + - \c 3: Least trimmed of squares. + **/ + double variance(const unsigned int variance_method=1) const { + double foo; + return variance_mean(variance_method,foo); + } + + //! Return the variance as well as the average of the pixel values. + /** + \param variance_method Method used to estimate the variance (see variance(const unsigned int) const). + \param[out] mean Average pixel value. + **/ + template + double variance_mean(const unsigned int variance_method, t& mean) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_mean(): Empty instance.", + cimg_instance); + + double variance = 0, average = 0; + const ulongT siz = size(); + switch (variance_method) { + case 0 : { // Least mean square (standard definition) + double S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } + variance = (S2 - S*S/siz)/siz; + average = S; + } break; + case 1 : { // Least mean square (robust definition) + double S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } + variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + average = S; + } break; + case 2 : { // Least Median of Squares (MAD) + CImg buf(*this,false); + buf.sort(); + const ulongT siz2 = siz>>1; + const double med_i = (double)buf[siz2]; + cimg_for(buf,ptrs,Tfloat) { + const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; + } + buf.sort(); + const double sig = (double)(1.4828*buf[siz2]); + variance = sig*sig; + } break; + default : { // Least trimmed of Squares + CImg buf(*this,false); + const ulongT siz2 = siz>>1; + cimg_for(buf,ptrs,Tfloat) { + const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; + } + buf.sort(); + double a = 0; + const Tfloat *ptrs = buf._data; + for (ulongT j = 0; j0?variance:0; + } + + //! Return estimated variance of the noise. + /** + \param variance_method Method used to compute the variance (see variance(const unsigned int) const). + \note Because of structures such as edges in images it is + recommended to use a robust variance estimation. The variance of the + noise is estimated by computing the variance of the Laplacian \f$(\Delta + I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]= + \sigma^2\f$ where \f$\sigma\f$ is the noise variance. + **/ + double variance_noise(const unsigned int variance_method=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_noise(): Empty instance.", + cimg_instance); + + const ulongT siz = size(); + if (!siz || !_data) return 0; + if (variance_method>1) { // Compute a scaled version of the Laplacian + CImg tmp(*this,false); + if (_depth==1) { + const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3(I,T); + cimg_for3x3(*this,x,y,0,c,I,T) { + tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn + + (double)Icp - 4*(double)Icc); + } + } + } else { + const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,T); + cimg_for3x3x3(*this,x,y,z,c,I,T) { + tmp(x,y,z,c) = cste*( + (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); + } + } + } + return tmp.variance(variance_method); + } + + // Version that doesn't need intermediate images. + double variance = 0, S = 0, S2 = 0; + if (_depth==1) { + const double cste = 1./std::sqrt(20.); + CImg_3x3(I,T); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { + const double val = cste*((double)Inc + (double)Ipc + + (double)Icn + (double)Icp - 4*(double)Icc); + S+=val; S2+=val*val; + } + } else { + const double cste = 1./std::sqrt(42.); + CImg_3x3x3(I,T); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { + const double val = cste * + ((double)Incc + (double)Ipcc + (double)Icnc + + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); + S+=val; S2+=val*val; + } + } + if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + else variance = (S2 - S*S/siz)/siz; + return variance>0?variance:0; + } + + //! Compute the MSE (Mean-Squared Error) between two images. + /** + \param img Image used as the second argument of the MSE operator. + **/ + template + double MSE(const CImg& img) const { + if (img.size()!=size()) + throw CImgArgumentException(_cimg_instance + "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + double vMSE = 0; + const t* ptr2 = img._data; + cimg_for(*this,ptr1,T) { + const double diff = (double)*ptr1 - (double)*(ptr2++); + vMSE+=diff*diff; + } + const ulongT siz = img.size(); + if (siz) vMSE/=siz; + return vMSE; + } + + //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images. + /** + \param img Image used as the second argument of the PSNR operator. + \param max_value Maximum theoretical value of the signal. + **/ + template + double PSNR(const CImg& img, const double max_value=255) const { + const double vMSE = (double)std::sqrt(MSE(img)); + return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type::max()); + } + + //! Evaluate math formula. + /** + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); + } + + // Fast function to pre-evaluate common expressions. + // (return 'true' in case of success, and set value of 'res'). + template + bool __eval(const char *const expression, t &res) const { + if (!expression || !*expression) { res = (t)0; return true; } + const char c = *expression; + bool is_success = false; + char c1, end; + double val; + if (c>='0' && c<='9') { // Possible value + if (!expression[1]) { // Single digit + res = (t)(c - '0'); + is_success = true; + } else if (std::sscanf(expression,"%lf%c",&val,&end)==1) { // Single value + res = (t)val; + is_success = true; + } + } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value + (c1=expression[1])>='0' && c1<='0') { + if (!expression[2]) { // [+-!] + Single digit + const int ival = c1 - '0'; + res = (t)(c=='+'?ival:c=='-'?-ival:!ival); + is_success = true; + } else if (std::sscanf(expression + 1,"%lf%c",&val,&end)==1) { // [+-!] Single value + res = (t)(c=='+'?val:c=='-'?-val:(double)!val); + is_success = true; + } + } else if (!expression[1]) switch (*expression) { // Other common single-char expressions + case 'w' : res = (t)_width; is_success = true; break; + case 'h' : res = (t)_height; is_success = true; break; + case 'd' : res = (t)_depth; is_success = true; break; + case 's' : res = (t)_spectrum; is_success = true; break; + case 'r' : res = (t)_is_shared; is_success = true; break; + } + return is_success; + } + + double _eval(CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression || !*expression) return 0; + double _val = 0; + if (__eval(expression,_val)) return _val; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'),"eval", + *this,img_output,list_inputs,list_outputs,false); + mp.begin_t(); + const double val = mp(x,y,z,c); + mp.end_t(); + mp.end(); + return val; + } + + //! Evaluate math formula. + /** + \param[out] output Contains values of output vector returned by the evaluated expression + (or is empty if the returned type is scalar). + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + void eval(CImg &output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + template + void eval(CImg& output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); + } + + template + void _eval(CImg& output, CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression || !*expression) { output.assign(1); *output = 0; return; } + double _val = 0; + if (__eval(expression,_val)) { output.assign(1); *output = _val; return; } + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'),"eval", + *this,img_output,list_inputs,list_outputs,false); + output.assign(1,std::max(1U,mp.result_dim)); + mp.begin_t(); + mp(x,y,z,c,output._data); + mp.end_t(); + mp.end(); + } + + //! Evaluate math formula on a set of variables. + /** + \param expression Math formula, as a C-string. + \param xyzc Set of values (x,y,z,c) used for the evaluation. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,xyzc,list_inputs,list_outputs); + } + + //! Evaluate math formula on a set of variables \const. + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,xyzc,list_inputs,list_outputs); + } + + template + CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + CImg res(1,xyzc.size()/4); + if (!expression || !*expression) return res.fill(0); + _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false); + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel if (res._height>=512)) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_eval)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + cimg_pragma_openmp(for) + for (int i = 0; i[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]. + **/ + CImg get_stats(const unsigned int variance_method=1) const { + if (is_empty()) return CImg(); + const ulongT siz = size(); + const longT off_end = (longT)siz; + double S = 0, S2 = 0, P = 1; + longT offm = 0, offM = 0; + T m = *_data, M = m; + + cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) { + longT loffm = 0, loffM = 0; + T lm = *_data, lM = lm; + cimg_pragma_openmp(for) + for (longT off = 0; offlM) { lM = val; loffM = off; } + S+=_val; + S2+=_val*_val; + P*=_val; + } + cimg_pragma_openmp(critical(get_stats)) { + if (lmM || (lM==M && loffM1?(S2 - S*S/siz)/(siz - 1):0): + variance(variance_method)), + variance_value = _variance_value>0?_variance_value:0; + int + xm = 0, ym = 0, zm = 0, cm = 0, + xM = 0, yM = 0, zM = 0, cM = 0; + contains(_data[offm],xm,ym,zm,cm); + contains(_data[offM],xM,yM,zM,cM); + return CImg(1,14).fill((double)m,(double)M,mean_value,variance_value, + (double)xm,(double)ym,(double)zm,(double)cm, + (double)xM,(double)yM,(double)zM,(double)cM, + S,P); + } + + //! Compute statistics vector from the pixel values \inplace. + CImg& stats(const unsigned int variance_method=1) { + return get_stats(variance_method).move_to(*this); + } + + //@} + //------------------------------------- + // + //! \name Vector / Matrix Operations + //@{ + //------------------------------------- + + //! Compute norm of the image, viewed as a matrix. + /** + \param magnitude_type Norm type. Can be: + - \c -1: Linf-norm + - \c 0: L0-norm + - \c 1: L1-norm + - \c 2: L2-norm + **/ + double magnitude(const int magnitude_type=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "magnitude(): Empty instance.", + cimg_instance); + const ulongT siz = size(); + double res = 0; + switch (magnitude_type) { + case -1 : { + cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } + } break; + case 1 : { + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); + } break; + default : { + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]); + res = (double)std::sqrt(res); + } + } + return res; + } + + //! Compute the trace of the image, viewed as a matrix. + /** + **/ + double trace() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "trace(): Empty instance.", + cimg_instance); + double res = 0; + cimg_forX(*this,k) res+=(double)(*this)(k,k); + return res; + } + + //! Compute the determinant of the image, viewed as a matrix. + /** + **/ + double det() const { + if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "det(): Instance is not a square matrix.", + cimg_instance); + + switch (_width) { + case 1 : return (double)((*this)(0,0)); + case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0)); + case 3 : { + const double + a = (double)_data[0], d = (double)_data[1], g = (double)_data[2], + b = (double)_data[3], e = (double)_data[4], h = (double)_data[5], + c = (double)_data[6], f = (double)_data[7], i = (double)_data[8]; + return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; + } + default : { + CImg lu(*this,false); + CImg indx; + bool d; + lu._LU(indx,d); + double res = d?(double)1:(double)-1; + cimg_forX(lu,i) res*=lu(i,i); + return res; + } + } + } + + //! Compute the dot product between instance and argument, viewed as matrices. + /** + \param img Image used as a second argument of the dot product. + **/ + template + double dot(const CImg& img) const { + const ulongT nb = std::min(size(),img.size()); + double res = 0; + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192)) + for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off]; + return res; + } + + //! Get vector-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + CImg res; + if (res._height!=_spectrum) res.assign(1,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + const T *ptrs = data(x,y,z); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get (square) matrix-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \note - The spectrum() of the image must be a square. + **/ + CImg get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + const int n = (int)cimg::round(std::sqrt((double)_spectrum)); + const T *ptrs = data(x,y,z,0); + const ulongT whd = (ulongT)_width*_height*_depth; + CImg res(n,n); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get tensor-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + const T *ptrs = data(x,y,z,0); + const ulongT whd = (ulongT)_width*_height*_depth; + if (_spectrum==6) + return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd)); + if (_spectrum==3) + return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd)); + return tensor(*ptrs); + } + + //! Set vector-valued pixel at specified position. + /** + \param vec Vector to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_vector_at(const CImg& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { + if (x<_width && y<_height && z<_depth) { + const t *ptrs = vec._data; + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = data(x,y,z); + for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) { + *ptrd = (T)*(ptrs++); ptrd+=whd; + } + } + return *this; + } + + //! Set (square) matrix-valued pixel at specified position. + /** + \param mat Matrix to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_matrix_at(const CImg& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + return set_vector_at(mat,x,y,z); + } + + //! Set tensor-valued pixel at specified position. + /** + \param ten Tensor to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + T *ptrd = data(x,y,z,0); + const ulongT siz = (ulongT)_width*_height*_depth; + if (ten._height==2) { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[3]; + } + else { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[2]; ptrd+=siz; + *ptrd = (T)ten[4]; ptrd+=siz; + *ptrd = (T)ten[5]; ptrd+=siz; + *ptrd = (T)ten[8]; + } + return *this; + } + + //! Resize image to become a diagonal matrix. + /** + \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient. + **/ + CImg& diagonal() { + return get_diagonal().move_to(*this); + } + + //! Resize image to become a diagonal matrix \newinstance. + CImg get_diagonal() const { + if (is_empty()) return *this; + const unsigned int siz = (unsigned int)size(); + CImg res(siz,siz,1,1,0); + cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off]; + return res; + } + + //! Replace the image by an identity matrix. + /** + \note If the instance image is not square, it is resized to a square matrix using its maximum + dimension as a reference. + **/ + CImg& identity_matrix() { + return identity_matrix(std::max(_width,_height)).move_to(*this); + } + + //! Replace the image by an identity matrix \newinstance. + CImg get_identity_matrix() const { + return identity_matrix(std::max(_width,_height)); + } + + //! Fill image with a linear sequence of values. + /** + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + CImg& sequence(const T& a0, const T& a1) { + if (is_empty()) return *this; + const ulongT siz = size() - 1; + T* ptr = _data; + if (siz) { + const double delta = (double)a1 - (double)a0; + cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); + } else *ptr = a0; + return *this; + } + + //! Fill image with a linear sequence of values \newinstance. + CImg get_sequence(const T& a0, const T& a1) const { + return (+*this).sequence(a0,a1); + } + + //! Transpose the image, viewed as a matrix. + /** + \note Equivalent to \code permute_axes("yxzc"); \endcode. + **/ + CImg& transpose() { + if (_width==1) { _width = _height; _height = 1; return *this; } + if (_height==1) { _height = _width; _width = 1; return *this; } + if (_width==_height) { + cimg_forYZC(*this,y,z,c) for (int x = y; x get_transpose() const { + return get_permute_axes("yxzc"); + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors. + /** + \param img Image used as the second argument of the cross product. + \note The first argument of the cross product is \c *this. + **/ + template + CImg& cross(const CImg& img) { + if (_width!=1 || _height<3 || img._width!=1 || img._height<3) + throw CImgInstanceException(_cimg_instance + "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + + const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; + (*this)[0] = (T)(y*img[2] - z*img[1]); + (*this)[1] = (T)(z*img[0] - x*img[2]); + (*this)[2] = (T)(x*img[1] - y*img[0]); + return *this; + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance. + template + CImg<_cimg_Tt> get_cross(const CImg& img) const { + return CImg<_cimg_Tt>(*this).cross(img); + } + + //! Invert the instance image, viewed as a matrix. + /** + \param use_LU Choose the inverting algorithm. Can be: + - \c true: LU-based matrix inversion. + - \c false: SVD-based matrix inversion. + **/ + CImg& invert(const bool use_LU=true) { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "invert(): Instance is not a square matrix.", + cimg_instance); + const double dete = _width>3?-1.:det(); + if (dete!=0. && _width==2) { + const double + a = _data[0], c = _data[1], + b = _data[2], d = _data[3]; + _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete); + _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete); + } else if (dete!=0. && _width==3) { + const double + a = _data[0], d = _data[1], g = _data[2], + b = _data[3], e = _data[4], h = _data[5], + c = _data[6], f = _data[7], i = _data[8]; + _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete); + _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete); + _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete); + } else { + +#ifdef cimg_use_lapack + int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N]; + Tfloat + *const lapA = new Tfloat[N*N], + *const WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetri_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] WORK; +#else + if (use_LU) { // LU-based + CImg A(*this,false), indx; + bool d; + A._LU(indx,d); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16)) + cimg_forX(*this,j) { + CImg col(1,_width,1,1,0); + col(j) = 1; + col._solve(A,indx); + cimg_forX(*this,i) (*this)(j,i) = (T)col(i); + } + } else pseudoinvert(false); // SVD-based +#endif + } + return *this; + } + + //! Invert the instance image, viewed as a matrix \newinstance. + CImg get_invert(const bool use_LU=true) const { + return CImg(*this,false).invert(use_LU); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. + /** + **/ + CImg& pseudoinvert(const bool use_LU=false) { + return get_pseudoinvert(use_LU).move_to(*this); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. + CImg get_pseudoinvert(const bool use_LU=false) const { + + // LU-based method. + if (use_LU) { + CImg AtA(width(),width()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128)) + cimg_forY(AtA,i) + for (int j = 0; j<=i; ++j) { + double res = 0; + cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k); + AtA(j,i) = AtA(i,j) = (Tfloat)res; + } + AtA.invert(true); + return AtA*get_transpose(); + } + + // SVD-based method. + CImg U, S, V; + SVD(U,S,V,false); + const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max(); + cimg_forX(V,x) { + const Tfloat s = S(x), invs = s>epsilon?1/s:0; + cimg_forY(V,y) V(x,y)*=invs; + } + return V*U.transpose(); + } + + //! Solve a system of linear equations. + /** + \param A Matrix of the linear system. + \param use_LU In case of non square system (least-square solution), + choose between SVD-based (\c false) or LU-based (\c true) method. + LU method is faster for large matrices, but numerically less stable. + \note Solve \c AX = B where \c B=*this. + **/ + template + CImg& solve(const CImg& A, const bool use_LU=false) { + if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + + if (A.size()==1) return (*this)/=A[0]; + if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system + const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3], + fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d), + det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd); + if (fM==fa) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det; + (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y; + } else if (fM==fc) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det; + (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y; + } else if (fM==fb) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det; + (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det; + (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d); + } + return *this; + } + + if (A._width==A._height) { // Square linear system +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N]; + Ttfloat + *const lapA = new Ttfloat[N*N], + *const lapB = new Ttfloat[N], + *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l)); + cimg_forX(*this,i) { + cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrs_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0; + } + delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; +#else + CImg lu(A,false); + CImg indx; + bool d; + lu._LU(indx,d); + CImg res(_width,A._width); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16)) + cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx)); + res.move_to(*this); +#endif + } else { // Least-square solution for non-square systems + +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width; + Ttfloat WORK_QUERY; + Ttfloat + * const lapA = new Ttfloat[M*N], + * const lapB = new Ttfloat[M*NRHS]; + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO); + LWORK = (int) WORK_QUERY; + Ttfloat *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l)); + cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l)); + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO); + if (INFO != 0) + cimg::warn(_cimg_instance + "solve(): LAPACK library function sgels() returned error code %d.", + cimg_instance, + INFO); + assign(NRHS, N); + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l]; + else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); + delete[] lapA; delete[] lapB; delete[] WORK; +#else + (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); +#endif + } + return *this; + } + + //! Solve a system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve(const CImg& A, const bool use_LU=false) const { + typedef _cimg_Ttfloat Ttfloat; + return CImg(*this,false).solve(A,use_LU); + } + + template + CImg& _solve(const CImg& A, const CImg& indx) { + typedef _cimg_Ttfloat Ttfloat; + const int N = height(); + int ii = -1; + Ttfloat sum; + for (int i = 0; i=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j); + else if (sum!=0) ii = i; + (*this)(i) = (T)sum; + } + for (int i = N - 1; i>=0; --i) { + sum = (*this)(i); + for (int j = i + 1; j + CImg& solve_tridiagonal(const CImg& A) { + const unsigned int siz = (unsigned int)size(); + if (A._width!=3 || A._height!=siz) + throw CImgArgumentException(_cimg_instance + "solve_tridiagonal(): Instance and tridiagonal matrix " + "(%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = 1e-4f; + CImg B = A.get_column(1), V(*this,false); + for (int i = 1; i<(int)siz; ++i) { + const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon); + B[i] -= m*A(2,i - 1); + V[i] -= m*V[i - 1]; + } + (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon)); + for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i + 1])/(B[i]?B[i]:epsilon)); + return *this; + } + + //! Solve a tridiagonal system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg& A) const { + return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A); + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. + **/ + template + const CImg& eigen(CImg& val, CImg &vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + + if (val.size()<(ulongT)_width) val.assign(1,_width); + if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width); + switch (_width) { + case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break; + case 2 : { + const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d; + double f = e*e - 4*(a*d - b*c); + if (f<0) cimg::warn(_cimg_instance + "eigen(): Complex eigenvalues found.", + cimg_instance); + f = std::sqrt(f); + const double + l1 = 0.5*(e - f), + l2 = 0.5*(e + f), + b2 = b*b, + norm1 = std::sqrt(cimg::sqr(l2 - a) + b2), + norm2 = std::sqrt(cimg::sqr(l1 - a) + b2); + val[0] = (t)l2; + val[1] = (t)l1; + if (norm1>0) { vec(0,0) = (t)(b/norm1); vec(0,1) = (t)((l2 - a)/norm1); } else { vec(0,0) = 1; vec(0,1) = 0; } + if (norm2>0) { vec(1,0) = (t)(b/norm2); vec(1,1) = (t)((l1 - a)/norm2); } else { vec(1,0) = 1; vec(1,1) = 0; } + } break; + default : + throw CImgInstanceException(_cimg_instance + "eigen(): Eigenvalues computation of general matrices is limited " + "to 2x2 matrices.", + cimg_instance); + } + } + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \return A list of two images [val; vec], whose meaning is similar as in eigen(CImg&,CImg&) const. + **/ + CImgList get_eigen() const { + CImgList res(2); + eigen(res[0],res[1]); + return res; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. + **/ + template + const CImg& symmetric_eigen(CImg& val, CImg& vec) const { + if (is_empty()) { val.assign(); vec.assign(); return *this; } + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + val.assign(1,_width); + vec.assign(_width,_width); + + if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; } + if (_width==2) { + const double + a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], + e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)), + l1 = 0.5*(e - f), l2 = 0.5*(e + f), + n = std::sqrt(cimg::sqr(l2 - a) + b*b); + val[0] = (t)l2; + val[1] = (t)l1; + if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; } + vec[1] = -vec[2]; + vec[3] = vec[0]; + return *this; + } + +#ifdef cimg_use_lapack + char JOB = 'V', UPLO = 'U'; + int N = _width, LWORK = 4*N, INFO; + Tfloat + *const lapA = new Tfloat[N*N], + *const lapW = new Tfloat[N], + *const WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); + cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.", + cimg_instance, + INFO); + if (!INFO) { + cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i]; + cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]); + } else { val.fill(0); vec.fill(0); } + delete[] lapA; delete[] lapW; delete[] WORK; + +#else + CImg V(_width,_width); + Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M)); + (CImg(*this,false)/=maxabs).SVD(vec,val,V,false); + if (maxabs!=1) val*=maxabs; + + bool is_ambiguous = false; + float eig = 0; + cimg_forY(val,p) { // Check for ambiguous cases + if (val[p]>eig) eig = (float)val[p]; + t scal = 0; + cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); + if (cimg::abs(scal)<0.9f) is_ambiguous = true; + if (scal<0) val[p] = -val[p]; + } + if (is_ambiguous) { + ++(eig*=2); + SVD(vec,val,V,false,40,eig); + val-=eig; + } + + CImg permutations; // Sort eigenvalues in decreasing order + CImg tmp(_width); + val.sort(permutations,false); + cimg_forY(vec,k) { + cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k); + std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width); + } +#endif + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \return A list of two images [val; vec], whose meaning are similar as in + symmetric_eigen(CImg&,CImg&) const. + **/ + CImgList get_symmetric_eigen() const { + CImgList res(2); + symmetric_eigen(res[0],res[1]); + return res; + } + + //! Sort pixel values and get sorting permutations. + /** + \param[out] permutations Permutation map used for the sorting. + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + **/ + template + CImg& sort(CImg& permutations, const bool is_increasing=true) { + permutations.assign(_width,_height,_depth,_spectrum); + if (is_empty()) return *this; + cimg_foroff(permutations,off) permutations[off] = (t)off; + return _quicksort(0,size() - 1,permutations,is_increasing,true); + } + + //! Sort pixel values and get sorting permutations \newinstance. + template + CImg get_sort(CImg& permutations, const bool is_increasing=true) const { + return (+*this).sort(permutations,is_increasing); + } + + //! Sort pixel values. + /** + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + \param axis Tells if the value sorting must be done along a specific axis. Can be: + - \c 0: All pixel values are sorted, independently on their initial position. + - \c 'x': Image columns are sorted, according to the first value in each column. + - \c 'y': Image rows are sorted, according to the first value in each row. + - \c 'z': Image slices are sorted, according to the first value in each slice. + - \c 'c': Image channels are sorted, according to the first value in each channel. + **/ + CImg& sort(const bool is_increasing=true, const char axis=0) { + if (is_empty()) return *this; + CImg perm; + switch (cimg::lowercase(axis)) { + case 0 : + _quicksort(0,size() - 1,perm,is_increasing,false); + break; + case 'x' : { + perm.assign(_width); + get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c); + } break; + case 'y' : { + perm.assign(_height); + get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c); + } break; + case 'z' : { + perm.assign(_depth); + get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c); + } break; + case 'c' : { + perm.assign(_spectrum); + get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]); + } break; + default : + throw CImgArgumentException(_cimg_instance + "sort(): Invalid specified axis '%c' " + "(should be { x | y | z | c }).", + cimg_instance,axis); + } + return *this; + } + + //! Sort pixel values \newinstance. + CImg get_sort(const bool is_increasing=true, const char axis=0) const { + return (+*this).sort(is_increasing,axis); + } + + template + CImg& _quicksort(const long indm, const long indM, CImg& permutations, + const bool is_increasing, const bool is_permutations) { + if (indm(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]>(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]>(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } else { + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]<(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } + if (indM - indm>=3) { + const T pivot = (*this)[mid]; + long i = indm, j = indM; + if (is_increasing) { + do { + while ((*this)[i]pivot) --j; + if (i<=j) { + if (is_permutations) cimg::swap(permutations[i],permutations[j]); + cimg::swap((*this)[i++],(*this)[j--]); + } + } while (i<=j); + } else { + do { + while ((*this)[i]>pivot) ++i; + while ((*this)[j] A; // Input matrix (assumed to contain some values) + CImg<> U,S,V; + A.SVD(U,S,V) + \endcode + **/ + template + const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = (Ttfloat)1e-25; + + if (is_empty()) { U.assign(); S.assign(); V.assign(); } + else if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "SVD(): Instance has invalid dimensions (depth or channels different from 1).", + cimg_instance); + else { + U = *this; + if (lambda!=0) { + const unsigned int delta = std::min(U._width,U._height); + for (unsigned int i = 0; i rv1(_width); + Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0; + int l = 0; + + cimg_forX(U,i) { + l = i + 1; + rv1[i] = scale*g; + g = s = scale = 0; + if (i=0?-1:1)*std::sqrt(s)); + h = f*g - s; + U(i,i) = f - g; + for (int j = l; j=0?-1:1)*std::sqrt(s)); + h = f*g - s; + U(l,i) = f - g; + for (int k = l; k=0; --i) { + if (i=0; --i) { + l = i + 1; + g = S[i]; + for (int j = l; j=0; --k) { + int nm = 0; + for (unsigned int its = 0; its=1; --l) { + nm = l - 1; + if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; } + if ((cimg::abs(S[nm]) + anorm)==anorm) break; + } + if (flag) { + c = 0; + s = 1; + for (int i = l; i<=k; ++i) { + f = s*rv1[i]; + rv1[i] = c*rv1[i]; + if ((cimg::abs(f) + anorm)==anorm) break; + g = S[i]; + h = cimg::_hypot(f,g); + S[i] = h; + h = 1/h; + c = g*h; + s = -f*h; + cimg_forY(U,j) { + const t y = U(nm,j), z = U(i,j); + U(nm,j) = y*c + z*s; + U(i,j) = z*c - y*s; + } + } + } + + const t z = S[k]; + if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } + nm = k - 1; + t x = S[l], y = S[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y); + g = cimg::_hypot(f,(Ttfloat)1); + f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x); + c = s = 1; + for (int j = l; j<=nm; ++j) { + const int i = j + 1; + g = rv1[i]; + h = s*g; + g = c*g; + t y1 = S[i], z1 = cimg::_hypot(f,h); + rv1[j] = z1; + c = f/std::max(epsilon,(Ttfloat)z1); + s = h/std::max(epsilon,(Ttfloat)z1); + f = x*c + g*s; + g = g*c - x*s; + h = y1*s; + y1*=c; + cimg_forX(U,jj) { + const t x2 = V(j,jj), z2 = V(i,jj); + V(j,jj) = x2*c + z2*s; + V(i,jj) = z2*c - x2*s; + } + z1 = cimg::_hypot(f,h); + S[j] = z1; + if (z1) { + z1 = 1/std::max(epsilon,(Ttfloat)z1); + c = f*z1; + s = h*z1; + } + f = c*g + s*y1; + x = c*y1 - s*g; + cimg_forY(U,jj) { + const t y2 = U(j,jj), z2 = U(i,jj); + U(j,jj) = y2*c + z2*s; + U(i,jj) = z2*c - y2*s; + } + } + rv1[l] = 0; + rv1[k] = f; + S[k] = x; + } + } + + if (sorting) { + CImg permutations; + CImg tmp(_width); + S.sort(permutations,false); + cimg_forY(U,k) { + cimg_forY(permutations,y) tmp(y) = U(permutations(y),k); + std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width); + } + cimg_forY(V,k) { + cimg_forY(permutations,y) tmp(y) = V(permutations(y),k); + std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width); + } + } + } + return *this; + } + + //! Compute the SVD of the instance image, viewed as a general matrix. + /** + \return A list of three images [U; S; V], whose meaning is similar as in + SVD(CImg&,CImg&,CImg&,bool,unsigned int,float) const. + **/ + CImgList get_SVD(const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + CImgList res(3); + SVD(res[0],res[1],res[2],sorting,max_iteration,lambda); + return res; + } + + // [internal] Compute the LU decomposition of a permuted matrix. + template + CImg& _LU(CImg& indx, bool& d) { + const int N = width(); + int imax = 0; + CImg vv(N); + indx.assign(N); + d = true; + + bool return0 = false; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512)) + cimg_forX(*this,i) { + Tfloat vmax = 0; + cimg_forX(*this,j) { + const Tfloat tmp = cimg::abs((*this)(j,i)); + if (tmp>vmax) vmax = tmp; + } + if (vmax==0) return0 = true; else vv[i] = 1/vmax; + } + if (return0) { indx.fill(0); return fill(0); } + + cimg_forX(*this,j) { + for (int i = 0; i=vmax) { vmax = tmp; imax = i; } + } + if (j!=imax) { + cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); + d = !d; + vv[imax] = vv[j]; + } + indx[j] = (t)imax; + if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; + if (j=3 = orthogonal matching pursuit where an orthogonal projection step is performed + every 'method-2' iterations. + \param max_iter Sets the max number of iterations processed for each signal. + If set to '0' (default), 'max_iter' is set to the number of dictionary columns. + (only meaningful for matching pursuit and its variants). + \param max_residual Gives a stopping criterion on signal reconstruction accuracy. + (only meaningful for matching pursuit and its variants). + \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column. + Thus, the matrix product D*W is an approximation of the input matrix. + **/ + template + CImg& project_matrix(const CImg& dictionary, const unsigned int method=0, + const unsigned int max_iter=0, const double max_residual=1e-6) { + return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this); + } + + template + CImg get_project_matrix(const CImg& dictionary, const unsigned int method=0, + const unsigned int max_iter=0, const double max_residual=1e-6) const { + if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "project_matrix(): Instance image is not a matrix.", + cimg_instance); + if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.", + cimg_instance, + dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum); + + if (!method) return get_solve(dictionary,true); + CImg W(_width,dictionary._width,1,1,0); + + // Compute dictionary norm and normalize it. + CImg D(dictionary,false), Dnorm(D._width); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32)) + cimg_forX(Dnorm,d) { + Tfloat norm = 0; + cimg_forY(D,y) norm+=cimg::sqr(D(d,y)); + Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm)); + } + cimg_forXY(D,d,y) D(d,y)/=Dnorm[d]; + + // Matching pursuit. + const unsigned int proj_step = method<3?1:method - 2; + bool is_orthoproj = false; + + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32)) + cimg_forX(*this,x) { + CImg S = get_column(x); + const CImg S0 = method<2?CImg():S; + Tfloat residual = S.magnitude()/S._height; + const unsigned int nmax = max_iter?max_iter:D._width; + + for (unsigned int n = 0; nmax_residual; ++n) { + + // Find best matching column in D. + int dmax = 0; + Tfloat absdotmax = 0, dotmax = 0; + cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32)) + cimg_forX(D,d) { + Tfloat _dot = 0; + cimg_forY(D,y) _dot+=S[y]*D(d,y); + Tfloat absdot = cimg::abs(_dot); + cimg_pragma_openmp(critical(get_project_matrix)) { + if (absdot>absdotmax) { + absdotmax = absdot; + dotmax = _dot; + dmax = d; + } + } + } + + if (!n || method<3 || n%proj_step) { + // Matching Pursuit: Subtract component to signal. + W(x,dmax)+=dotmax; + residual = 0; + cimg_forY(S,y) { + S[y]-=dotmax*D(dmax,y); + residual+=cimg::sqr(S[y]); + } + residual = std::sqrt(residual)/S._height; + is_orthoproj = false; + + } else { + // Orthogonal Matching Pursuit: Orthogonal projection step. + W(x,dmax) = 1; // Used as a marker only. + unsigned int nbW = 0; + cimg_forY(W,d) if (W(x,d)) ++nbW; + CImg sD(nbW,D._height); + CImg inds(nbW); + int sd = 0; + cimg_forY(W,d) if (W(x,d)) { + cimg_forY(sD,y) sD(sd,y) = D(d,y); + inds[sd++] = d; + } + S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights + + // Recompute residual signal. + S = S0; + cimg_forY(sD,k) { + const Tfloat weight = sD[k]; + const unsigned int ind = inds[k]; + W(x,ind) = weight; + cimg_forY(S,y) S[y]-=weight*D(ind,y); + } + residual = S.magnitude()/S._height; + is_orthoproj = true; + } + } + + // Perform last orthoprojection step if needed. + if (method>=2 && !is_orthoproj) { + unsigned int nbW = 0; + cimg_forY(W,d) if (W(x,d)) ++nbW; + if (nbW) { // Avoid degenerated case where 0 coefs are used + CImg sD(nbW,D._height); + CImg inds(nbW); + int sd = 0; + cimg_forY(W,d) if (W(x,d)) { + cimg_forY(sD,y) sD(sd,y) = D(d,y); + inds[sd++] = d; + } + S0.get_solve(sD,true).move_to(sD); + cimg_forY(sD,k) W(x,inds[k]) = sD[k]; + } + } + } + + // Normalize resulting coefficients according to initial (non-normalized) dictionary. + cimg_forXY(W,x,y) W(x,y)/=Dnorm[y]; + return W; + } + + //! Compute minimal path in a graph, using the Dijkstra algorithm. + /** + \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance + between two nodes (i,j). + \param nb_nodes Number of graph nodes. + \param starting_node Index of the starting node. + \param ending_node Index of the ending node (set to ~0U to ignore ending node). + \param previous_node Array that gives the previous node index in the path to the starting node + (optional parameter). + \return Array of distances of each node to the starting node. + **/ + template + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + if (starting_node>=nb_nodes) + throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher " + "than number of nodes %u.", + pixel_type(),starting_node,nb_nodes); + CImg dist(1,nb_nodes,1,1,cimg::type::max()); + dist(starting_node) = 0; + previous_node.assign(1,nb_nodes,1,1,(t)-1); + previous_node(starting_node) = (t)starting_node; + CImg Q(nb_nodes); + cimg_forX(Q,u) Q(u) = (unsigned int)u; + cimg::swap(Q(starting_node),Q(0)); + unsigned int sizeQ = nb_nodes; + while (sizeQ) { + // Update neighbors from minimal vertex + const unsigned int umin = Q(0); + if (umin==ending_node) sizeQ = 0; + else { + const T dmin = dist(umin); + const T infty = cimg::type::max(); + for (unsigned int q = 1; qdist(Q(left))) || + (rightdist(Q(right)));) { + if (right + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node=~0U) { + CImg foo; + return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + /** + \param starting_node Index of the starting node. + \param ending_node Index of the ending node. + \param previous_node Array that gives the previous node index in the path to the starting node + (optional parameter). + \return Array of distances of each node to the starting node. + \note image instance corresponds to the adjacency matrix of the graph. + **/ + template + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + template + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) const { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "dijkstra(): Instance is not a graph adjacency matrix.", + cimg_instance); + + return dijkstra(*this,_width,starting_node,ending_node,previous_node); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { + return get_dijkstra(starting_node,ending_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { + CImg foo; + return get_dijkstra(starting_node,ending_node,foo); + } + + //! Return an image containing the character codes of specified string. + /** + \param str input C-string to encode as an image. + \param is_last_zero Tells if the ending \c '0' character appear in the resulting image. + \param is_shared Return result that shares its buffer with \p str. + **/ + static CImg string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) { + if (!str) return CImg(); + return CImg(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared); + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg row_vector(const T& a0) { + return vector(a0); + } + + //! Return a \c 2x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg row_vector(const T& a0, const T& a1) { + CImg r(2,1); + r[0] = a0; r[1] = a1; + return r; + } + + //! Return a \c 3x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg row_vector(const T& a0, const T& a1, const T& a2) { + CImg r(3,1); + r[0] = a0; r[1] = a1; r[2] = a2; + return r; + } + + //! Return a \c 4x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3) { + CImg r(4,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; + return r; + } + + //! Return a \c 5x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + CImg r(5,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; + return r; + } + + //! Return a \c 6x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + CImg r(6,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; + return r; + } + + //! Return a \c 7x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + CImg r(7,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; + return r; + } + + //! Return a \c 8x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + CImg r(8,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; + return r; + } + + //! Return a \c 9x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + CImg r(9,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; + return r; + } + + //! Return a \c 10x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + CImg r(10,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + return r; + } + + //! Return a \c 11x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + CImg r(11,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; + return r; + } + + //! Return a \c 12x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + CImg r(12,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; + return r; + } + + //! Return a \c 13x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + CImg r(13,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; + return r; + } + + //! Return a \c 14x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + CImg r(14,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; + return r; + } + + //! Return a \c 15x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + CImg r(15,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; + return r; + } + + //! Return a \c 16x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(16,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; + return r; + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg vector(const T& a0) { + CImg r(1,1); + r[0] = a0; + return r; + } + + //! Return a \c 1x2 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg vector(const T& a0, const T& a1) { + CImg r(1,2); + r[0] = a0; r[1] = a1; + return r; + } + + //! Return a \c 1x3 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2) { + CImg r(1,3); + r[0] = a0; r[1] = a1; r[2] = a2; + return r; + } + + //! Return a \c 1x4 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { + CImg r(1,4); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; + return r; + } + + //! Return a \c 1x5 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + CImg r(1,5); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; + return r; + } + + //! Return a \c 1x6 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + CImg r(1,6); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; + return r; + } + + //! Return a \c 1x7 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + CImg r(1,7); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; + return r; + } + + //! Return a \c 1x8 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + CImg r(1,8); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; + return r; + } + + //! Return a \c 1x9 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + CImg r(1,9); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; + return r; + } + + //! Return a \c 1x10 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + CImg r(1,10); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + return r; + } + + //! Return a \c 1x11 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + CImg r(1,11); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; + return r; + } + + //! Return a \c 1x12 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + CImg r(1,12); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; + return r; + } + + //! Return a \c 1x13 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + CImg r(1,13); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; + return r; + } + + //! Return a \c 1x14 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + CImg r(1,14); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; + return r; + } + + //! Return a \c 1x15 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + CImg r(1,15); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; + return r; + } + + //! Return a \c 1x16 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(1,16); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; + return r; + } + + //! Return a 1x1 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg matrix(const T& a0) { + return vector(a0); + } + + //! Return a 2x2 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, + const T& a2, const T& a3) { + CImg r(2,2); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; + *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a 3x3 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + \param a4 Fifth matrix value. + \param a5 Sixth matrix value. + \param a6 Seventh matrix value. + \param a7 Eighth matrix value. + \param a8 Ninth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, const T& a2, + const T& a3, const T& a4, const T& a5, + const T& a6, const T& a7, const T& a8) { + CImg r(3,3); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; + return r; + } + + //! Return a 4x4 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(4,4); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 5x5 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, + const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, + const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, + const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, + const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { + CImg r(5,5); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; + *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; + *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; + return r; + } + + //! Return a 1x1 symmetric matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg tensor(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 symmetric matrix tensor containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2) { + return matrix(a0,a1,a1,a2); + } + + //! Return a 3x3 symmetric matrix containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5); + } + + //! Return a 1x1 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1) { + return matrix(a0,0,0,a1); + } + + //! Return a 3x3 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2) { + return matrix(a0,0,0,0,a1,0,0,0,a2); + } + + //! Return a 4x4 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { + return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); + } + + //! Return a 5x5 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); + } + + //! Return a NxN identity matrix. + /** + \param N Dimension of the matrix. + **/ + static CImg identity_matrix(const unsigned int N) { + CImg res(N,N,1,1,0); + cimg_forX(res,x) res(x,x) = 1; + return res; + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1. + /** + \param N Size of the resulting vector. + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + static CImg sequence(const unsigned int N, const T& a0, const T& a1) { + if (N) return CImg(1,N).sequence(a0,a1); + return CImg(); + } + + //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or third quaternion coordinate. + \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). + **/ + static CImg rotation_matrix(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) { + double X, Y, Z, W, N; + if (is_quaternion) { + N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w); + if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; } + else { X = Y = Z = 0; W = 1; } + return CImg::matrix((T)(X*X + Y*Y - Z*Z - W*W),(T)(2*Y*Z - 2*X*W),(T)(2*X*Z + 2*Y*W), + (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y), + (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W)); + } + N = cimg::hypot((double)x,(double)y,(double)z); + if (N>0) { X = x/N; Y = y/N; Z = z/N; } + else { X = Y = 0; Z = 1; } + const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang); + return CImg::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s), + (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s), + (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c)); + } + + //@} + //----------------------------------- + // + //! \name Value Manipulation + //@{ + //----------------------------------- + + //! Fill all pixel values with specified value. + /** + \param val Fill value. + **/ + CImg& fill(const T& val) { + if (is_empty()) return *this; + if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val; + else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*) + return *this; + } + + //! Fill all pixel values with specified value \newinstance. + CImg get_fill(const T& val) const { + return CImg(_width,_height,_depth,_spectrum).fill(val); + } + + //! Fill sequentially all pixel values with specified values. + /** + \param val0 First fill value. + \param val1 Second fill value. + **/ + CImg& fill(const T& val0, const T& val1) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 1; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 2; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 3; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 4; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 5; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 6; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 7; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 8; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 9; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 10; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 11; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 12; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 13; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 14; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14, const T& val15) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 15; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14, const T& val15) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14,val15); + } + + //! Fill sequentially pixel values according to a given expression. + /** + \param expression C-string describing a math formula, or a sequence of values. + \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling. + \param allow_formula Tells that mathematical formulas are authorized for the filling. + \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. + \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression. + **/ + CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0); + } + + // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula | + // 2 = allow formula and do not fill image values }. + CImg& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode, + const CImgList *const list_inputs, CImgList *const list_outputs, + const char *const calling_function, const CImg *provides_copy) { + if (is_empty() || !expression || !*expression) return *this; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + CImg is_error; + bool is_value_sequence = false; + cimg_abort_init; + + if (formula_mode) { + + // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser. + double value; + char sep; + const int err = cimg_sscanf(expression,"%lf %c",&value,&sep); + if (err==1 || (err==2 && sep==',')) { + if (err==1) { if (formula_mode==2) return *this; return fill((T)value); } + else is_value_sequence = true; + } + + // Try to fill values according to a formula. + _cimg_abort_init_openmp; + if (!is_value_sequence) try { + CImg base = provides_copy?provides_copy->get_shared():get_shared(); + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'), + calling_function,base,this,list_inputs,list_outputs,true); + if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && + mp.need_input_copy) + base.assign().assign(*this,false); // Needs input copy + + // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation). + unsigned int M; + if (mp.result_dim) { + M = cimg::max(_width,_height,_depth); + M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height); + } else { + M = cimg::max(_width,_height,_depth,_spectrum); + M = M==_width?cimg::max(_height,_depth,_spectrum): + M==_height?cimg::max(_width,_depth,_spectrum): + M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth); + } + + bool do_in_parallel = false; +#if cimg_use_openmp!=0 + cimg_openmp_if(*expression=='*' || *expression==':' || + (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2)) + do_in_parallel = true; +#endif + if (mp.result_dim) { // Vector-valued expression + const unsigned int N = std::min(mp.result_dim,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; + if (*expression=='<') { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_rofYZ(*this,y,z) { + cimg_abort_test; + if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0); + else cimg_rofX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); + + } else if (*expression=='>' || !do_in_parallel) { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_forYZ(*this,y,z) { + cimg_abort_test; + if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0); + else cimg_forX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); + + } else { + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + lmp.is_fill = true; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + +#define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \ + cimg_pragma_openmp(for cimg_openmp_collapse(2)) \ + cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \ + else { \ + CImg res(1,lmp.result_dim); \ + T *__ptrd = data(_sx,_sy,_sz,0); \ + const ulongT off = (ulongT)_off; \ + cimg_for##_X(*this,_x) { \ + lmp(x,y,z,0,res._data); \ + const double *ptrs = res._data; \ + T *_ptrd = __ptrd; \ + for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \ + __ptrd+=off; \ + } \ + } \ + } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp + + if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } + else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } + else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) } + + lmp.end_t(); + cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); } + if (&lmp!=&mp) delete &lmp; + } +#endif + } + + } else { // Scalar-valued expression + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') { + mp.begin_t(); + if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); } + else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } + mp.end_t(); + + } else if (*expression=='>' || !do_in_parallel) { + mp.begin_t(); + if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } + else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } + mp.end_t(); + + } else { + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + lmp.is_fill = true; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + +#define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \ + cimg_pragma_openmp(for cimg_openmp_collapse(3)) \ + cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \ + else { \ + T *_ptrd = data(_sx,_sy,_sz,_sc); \ + const ulongT off = (ulongT)_off; \ + cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \ + } \ + } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp + + if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } + else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } + else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } + else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) } + + lmp.end_t(); + cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); } + if (&lmp!=&mp) delete &lmp; + } +#endif + } + } + mp.end(); + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + } + + // Try to fill values according to a value sequence. + if (!formula_mode || is_value_sequence || is_error) { + CImg item(256); + char sep = 0; + const char *nexpression = expression; + ulongT nb = 0; + const ulongT siz = size(); + T *ptrd = _data; + for (double val = 0; *nexpression && nb0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { + nexpression+=std::strlen(item) + (err>1); + *(ptrd++) = (T)val; + } else break; + } + cimg::exception_mode(omode); + if (nb get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs); + } + + //! Fill sequentially pixel values according to the values found in another image. + /** + \param values Image containing the values used for the filling. + \param repeat_values In case there are less values than necessary in \c values, tells if these values must be + repeated for the filling. + **/ + template + CImg& fill(const CImg& values, const bool repeat_values=true) { + if (is_empty() || !values) return *this; + T *ptrd = _data, *ptre = ptrd + size(); + for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs + CImg get_fill(const CImg& values, const bool repeat_values=true) const { + return repeat_values?CImg(_width,_height,_depth,_spectrum).fill(values,repeat_values): + (+*this).fill(values,repeat_values); + } + + //! Fill pixel values along the X-axis at a specified pixel position. + /** + \param y Y-coordinate of the filled column. + \param z Z-coordinate of the filled column. + \param c C-coordinate of the filled column. + \param a0 First fill value. + **/ + CImg& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) { +#define _cimg_fill1(x,y,z,c,off,siz,t) { \ + va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \ + for (unsigned int k = 1; k& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) { + if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position. + /** + \param x X-coordinate of the filled row. + \param z Z-coordinate of the filled row. + \param c C-coordinate of the filled row. + \param a0 First fill value. + **/ + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position \overloading. + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position. + /** + \param x X-coordinate of the filled slice. + \param y Y-coordinate of the filled slice. + \param c C-coordinate of the filled slice. + \param a0 First fill value. + **/ + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) { + const ulongT wh = (ulongT)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position \overloading. + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) { + const ulongT wh = (ulongT)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position. + /** + \param x X-coordinate of the filled channel. + \param y Y-coordinate of the filled channel. + \param z Z-coordinate of the filled channel. + \param a0 First filling value. + **/ + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { + const ulongT whd = (ulongT)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position \overloading. + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { + const ulongT whd = (ulongT)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double); + return *this; + } + + //! Discard specified sequence of values in the image buffer, along a specific axis. + /** + \param values Sequence of values to discard. + \param axis Axis along which the values are discarded. If set to \c 0 (default value) + the method does it for all the buffer values and returns a one-column vector. + \note Discarded values will change the image geometry, so the resulting image + is returned as a one-column vector. + **/ + template + CImg& discard(const CImg& values, const char axis=0) { + if (is_empty() || !values) return *this; + return get_discard(values,axis).move_to(*this); + } + + template + CImg get_discard(const CImg& values, const char axis=0) const { + if (!values) return +*this; + CImg res; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + ulongT j = 0; + unsigned int k = 0; + int i0 = 0; + res.assign(width(),height(),depth(),spectrum()); + switch (_axis) { + case 'x' : { + cimg_forX(*this,i) { + if ((*this)(i)!=(T)values[j]) { + if (j) --i; + res.draw_image(k,get_columns(i0,i)); + k+=i - i0 + 1; i0 = i + 1; j = 0; + } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = (int)i + 1; }} + } + if ((ulongT)i0& discard(const char axis=0) { + return get_discard(axis).move_to(*this); + } + + //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance. + CImg get_discard(const char axis=0) const { + CImg res; + if (is_empty()) return res; + const char _axis = cimg::lowercase(axis); + T current = *_data?(T)0:(T)1; + int j = 0; + res.assign(width(),height(),depth(),spectrum()); + switch (_axis) { + case 'x' : { + cimg_forX(*this,i) + if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); } + res.resize(j,-100,-100,-100,0); + } break; + case 'y' : { + cimg_forY(*this,i) + if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); } + res.resize(-100,j,-100,-100,0); + } break; + case 'z' : { + cimg_forZ(*this,i) + if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); } + res.resize(-100,-100,j,-100,0); + } break; + case 'c' : { + cimg_forC(*this,i) + if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); } + res.resize(-100,-100,-100,j,0); + } break; + default : { + res.unroll('y'); + cimg_foroff(*this,i) { + const T val = (*this)[i]; + if (val!=current) res[j++] = current = val; + } + res.resize(-100,j,-100,-100,0); + } + } + return res; + } + + //! Invert endianness of all pixel values. + /** + **/ + CImg& invert_endianness() { + cimg::invert_endianness(_data,size()); + return *this; + } + + //! Invert endianness of all pixel values \newinstance. + CImg get_invert_endianness() const { + return (+*this).invert_endianness(); + } + + //! Fill image with random values in specified range. + /** + \param val_min Minimal authorized random value. + \param val_max Maximal authorized random value. + \note Random variables are uniformly distributed in [val_min,val_max]. + **/ + CImg& rand(const T& val_min, const T& val_max) { + const float delta = (float)val_max - (float)val_min + (cimg::type::is_float()?0:1); + if (cimg::type::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng)); + cimg::srand(rng); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng))); + cimg::srand(rng); + } + return *this; + } + + //! Fill image with random values in specified range \newinstance. + CImg get_rand(const T& val_min, const T& val_max) const { + return (+*this).rand(val_min,val_max); + } + + //! Round pixel values. + /** + \param y Rounding precision. + \param rounding_type Rounding type. Can be: + - \c -1: Backward. + - \c 0: Nearest. + - \c 1: Forward. + **/ + CImg& round(const double y=1, const int rounding_type=0) { + if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192); + return *this; + } + + //! Round pixel values \newinstance. + CImg get_round(const double y=1, const unsigned int rounding_type=0) const { + return (+*this).round(y,rounding_type); + } + + //! Add random noise to pixel values. + /** + \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the + global value range. + \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, + \p 3=Poisson or \p 4=Rician). + \return A reference to the modified image instance. + \note + - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on + the image value itself. + - Function \p CImg::get_noise() is also defined. It returns a non-shared modified copy of the image instance. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_noise(40); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_noise.jpg + **/ + CImg& noise(const double sigma, const unsigned int noise_type=0) { + if (is_empty()) return *this; + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0; + if (nsigma==0 && noise_type!=3) return *this; + if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M); + if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.); + switch (noise_type) { + case 0 : { // Gaussian noise + cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) { + Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng)); + if (val>vmax) val = vmax; + if (valvmax) val = vmax; + if (val::is_float()) { --m; ++M; } + else { m = (Tfloat)cimg::type::min(); M = (Tfloat)cimg::type::max(); } + } + cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) if (cimg::rand(100,&rng)vmax) val = vmax; + if (val get_noise(const double sigma, const unsigned int noise_type=0) const { + return (+*this).noise(sigma,noise_type); + } + + //! Linearly normalize pixel values. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \param constant_case_ratio In case of instance image having a constant value, tell what ratio + of [min_value,max_value] is used to fill the normalized image + (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(160,220); + (img,res).display(); + \endcode + \image html ref_normalize2.jpg + **/ + CImg& normalize(const T& min_value, const T& max_value, + const float constant_case_ratio=0) { + if (is_empty()) return *this; + const T a = min_value get_normalize(const T& min_value, const T& max_value, + const float ratio_if_constant_image=0) const { + return CImg(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image); + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm. + /** + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_normalize.jpg + **/ + CImg& normalize() { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + T *ptrd = data(0,y,z,0); + cimg_forX(*this,x) { + const T *ptrs = ptrd; + float n = 0; + cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; } + n = (float)std::sqrt(n); + T *_ptrd = ptrd++; + if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; } + else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; } + } + } + return *this; + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance. + CImg get_normalize() const { + return CImg(*this,false).normalize(); + } + + //! Compute Lp-norm of each multi-valued pixel of the image instance. + /** + \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_norm(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_norm.jpg + **/ + CImg& norm(const int norm_type=2) { + if (_spectrum==1 && norm_type) return abs(); + return get_norm(norm_type).move_to(*this); + } + + //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance. + CImg get_norm(const int norm_type=2) const { + if (is_empty()) return *this; + if (_spectrum==1 && norm_type) return get_abs(); + const ulongT whd = (ulongT)_width*_height*_depth; + CImg res(_width,_height,_depth); + switch (norm_type) { + case -1 : { // Linf-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; } + *(ptrd++) = n; + } + } + } break; + case 0 : { // L0-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + unsigned int n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; } + *(ptrd++) = (Tfloat)n; + } + } + } break; + case 1 : { // L1-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } + *(ptrd++) = n; + } + } + } break; + case 2 : { // L2-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); + } + } + } break; + default : { // Linf-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type); + } + } + } + } + return res; + } + + //! Cut pixel values in specified range. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_cut(160,220); + (img,res).display(); + \endcode + \image html ref_cut.jpg + **/ + CImg& cut(const T& min_value, const T& max_value) { + if (is_empty()) return *this; + const T a = min_value get_cut(const T& min_value, const T& max_value) const { + return (+*this).cut(min_value,max_value); + } + + //! Uniformly quantize pixel values. + /** + \param nb_levels Number of quantization levels. + \param keep_range Tells if resulting values keep the same range as the original ones. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_quantize(4); + (img,res).display(); + \endcode + \image html ref_quantize.jpg + **/ + CImg& quantize(const unsigned int nb_levels, const bool keep_range=true) { + if (!nb_levels) + throw CImgArgumentException(_cimg_instance + "quantize(): Invalid quantization request with 0 values.", + cimg_instance); + + if (is_empty()) return *this; + Tfloat m, M = (Tfloat)max_min(m), range = M - m; + if (range>0) { + if (keep_range) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); + _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); + _data[off] = (T)std::min(val,nb_levels - 1); + } + } + return *this; + } + + //! Uniformly quantize pixel values \newinstance. + CImg get_quantize(const unsigned int n, const bool keep_range=true) const { + return (+*this).quantize(n,keep_range); + } + + //! Threshold pixel values. + /** + \param value Threshold value + \param soft_threshold Tells if soft thresholding must be applied (instead of hard one). + \param strict_threshold Tells if threshold value is strict. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_threshold(128); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_threshold.jpg + **/ + CImg& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) { + if (is_empty()) return *this; + if (strict_threshold) { + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const T v = _data[off]; + _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) + cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0; + } else { + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const T v = _data[off]; + _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) + cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0; + } + return *this; + } + + //! Threshold pixel values \newinstance. + CImg get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const { + return (+*this).threshold(value,soft_threshold,strict_threshold); + } + + //! Compute the histogram of pixel values. + /** + \param nb_levels Number of desired histogram levels. + \param min_value Minimum pixel value considered for the histogram computation. + All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. + All pixel values higher than \p max_value will not be counted. + \note + - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x + in the image I. + - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional. + \par Example + \code + const CImg img = CImg("reference.jpg").histogram(256); + img.display_graph(0,3); + \endcode + \image html ref_histogram.jpg + **/ + CImg& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) { + return get_histogram(nb_levels,min_value,max_value).move_to(*this); + } + + //! Compute the histogram of pixel values \overloading. + CImg& histogram(const unsigned int nb_levels) { + return get_histogram(nb_levels).move_to(*this); + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const { + if (!nb_levels || is_empty()) return CImg(); + const double + vmin = (double)(min_value res(nb_levels,1,1,1,0); + cimg_rof(*this,ptrs,T) { + const T val = *ptrs; + if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))]; + } + return res; + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels) const { + if (!nb_levels || is_empty()) return CImg(); + T vmax = 0, vmin = min_max(vmax); + return get_histogram(nb_levels,vmin,vmax); + } + + //! Equalize histogram of pixel values. + /** + \param nb_levels Number of histogram levels used for the equalization. + \param min_value Minimum pixel value considered for the histogram computation. + All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. + All pixel values higher than \p max_value will not be counted. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_equalize(256); + (img,res).display(); + \endcode + \image html ref_equalize.jpg + **/ + CImg& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) { + if (!nb_levels || is_empty()) return *this; + const T + vmin = min_value hist = get_histogram(nb_levels,vmin,vmax); + ulongT cumul = 0; + cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } + if (!cumul) cumul = 1; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576)) + cimg_rofoff(*this,off) { + const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin)); + if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul); + } + return *this; + } + + //! Equalize histogram of pixel values \overloading. + CImg& equalize(const unsigned int nb_levels) { + if (!nb_levels || is_empty()) return *this; + T vmax = 0, vmin = min_max(vmax); + return equalize(nb_levels,vmin,vmax); + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const { + return (+*this).equalize(nblevels,val_min,val_max); + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels) const { + return (+*this).equalize(nblevels); + } + + //! Index multi-valued pixels regarding to a specified colormap. + /** + \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing. + \param dithering Level of dithering (0=disable, 1=standard level). + \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors. + \note + - \p img.index(colormap,dithering,1) is equivalent to img.index(colormap,dithering,0).map(colormap). + \par Example + \code + const CImg img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); + const CImg res = img.get_index(colormap,1,true); + (img,res).display(); + \endcode + \image html ref_index.jpg + **/ + template + CImg& index(const CImg& colormap, const float dithering=1, const bool map_indexes=false) { + return get_index(colormap,dithering,map_indexes).move_to(*this); + } + + //! Index multi-valued pixels regarding to a specified colormap \newinstance. + template + CImg::Tuint> + get_index(const CImg& colormap, const float dithering=1, const bool map_indexes=true) const { + if (colormap._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "index(): Instance and specified colormap (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + typedef typename CImg::Tuint tuint; + if (is_empty()) return CImg(); + const ulongT + whd = (ulongT)_width*_height*_depth, + pwhd = (ulongT)colormap._width*colormap._height*colormap._depth; + CImg res(_width,_height,_depth,map_indexes?_spectrum:1); + if (dithering>0) { // Dithered versions + tuint *ptrd = res._data; + const float ndithering = cimg::cut(dithering,0,1)/16; + Tfloat valm = 0, valM = (Tfloat)max_min(valm); + if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; } + CImg cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1); + Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0); + const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth; + switch (_spectrum) { + case 1 : { // Optimized for scalars + cimg_forYZ(*this,y,z) { + if (yvalM?valM:_val0; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1, + _val2 = (Tfloat)*ptrs2, val2 = _val2valM?valM:_val2; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrpvalM?valM:_val; + dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd; + } + if (dist=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z); + for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd; + for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd; + for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, + *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z); + for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp img("reference.jpg"), + colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), + colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), + res = img.get_index(colormap1,0).map(colormap2); + (img,res).display(); + \endcode + \image html ref_map.jpg + **/ + template + CImg& map(const CImg& colormap, const unsigned int boundary_conditions=0) { + return get_map(colormap,boundary_conditions).move_to(*this); + } + + //! Map predefined colormap on the scalar (indexed) image instance \newinstance. + template + CImg get_map(const CImg& colormap, const unsigned int boundary_conditions=0) const { + const ulongT + whd = (ulongT)_width*_height*_depth, siz = size(), + cwhd = (ulongT)colormap._width*colormap._height*colormap._depth, + cwhd2 = 2*cwhd; + CImg res(_width,_height,_depth,_spectrum*colormap._spectrum); + switch (colormap._spectrum) { + + case 1 : { // Optimized for scalars + switch (boundary_conditions) { + case 3 : // Mirror + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256)) + for (longT off = 0; off<(longT)siz; ++off) { + const ulongT ind = ((ulongT)_data[off])%cwhd2; + res[off] = colormap[ind& label(const bool is_high_connectivity=false, const Tfloat tolerance=0, + const bool is_L2_norm=true) { + if (is_empty()) return *this; + return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this); + } + + //! Label connected components \newinstance. + CImg get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0, + const bool is_L2_norm=true) const { + if (is_empty()) return CImg(); + + // Create neighborhood tables. + int dx[13], dy[13], dz[13], nb = 0; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0; + dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0; + if (is_high_connectivity) { + dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0; + } + if (_depth>1) { // 3D version + dx[nb] = 0; dy[nb] = 0; dz[nb++]=1; + if (is_high_connectivity) { + dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1; + dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1; + + dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1; + } + } + return _label(nb,dx,dy,dz,tolerance,is_L2_norm); + } + + //! Label connected components \overloading. + /** + \param connectivity_mask Mask of the neighboring pixels. + \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region. + \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used. + **/ + template + CImg& label(const CImg& connectivity_mask, const Tfloat tolerance=0, + const bool is_L2_norm=true) { + if (is_empty()) return *this; + return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this); + } + + //! Label connected components \newinstance. + template + CImg get_label(const CImg& connectivity_mask, const Tfloat tolerance=0, + const bool is_L2_norm=true) const { + if (is_empty()) return CImg(); + int nb = 0; + cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb; + CImg dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0); + nb = 0; + cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) && + connectivity_mask(x,y,z)) { + dx[nb] = x; dy[nb] = y; dz[nb++] = z; + } + return _label(nb,dx,dy,dz,tolerance,is_L2_norm); + } + + CImg _label(const unsigned int nb, const int *const dx, + const int *const dy, const int *const dz, + const Tfloat tolerance, const bool is_L2_norm) const { + CImg res(_width,_height,_depth); + const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance; + + // Init label numbers. + ulongT *ptr = res.data(); + cimg_foroff(res,p) *(ptr++) = p; + + // For each neighbour-direction, label. + for (unsigned int n = 0; n& _system_strescape() { +#define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\ + move_to(list); \ + CImg(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break + CImgList list; + const T *ptrs = _data; + cimg_for(*this,p,T) switch ((int)*p) { + cimg_system_strescape('\\',"\\\\"); + cimg_system_strescape('\"',"\\\""); + cimg_system_strescape('!',"\"\\!\""); + cimg_system_strescape('`',"\\`"); + cimg_system_strescape('$',"\\$"); + } + if (ptrs(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list); + return (list>'x').move_to(*this); + } + + //@} + //--------------------------------- + // + //! \name Color Base Management + //@{ + //--------------------------------- + + //! Return colormap \e "default", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_default.jpg + **/ + static const CImg& default_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,256,1,3); + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + colormap(0,index,0) = (Tuchar)r; + colormap(0,index,1) = (Tuchar)g; + colormap(0,index++,2) = (Tuchar)b; + } + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "HSV", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hsv.jpg + **/ + static const CImg& HSV_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + CImg tmp(1,256,1,3,1); + tmp.get_shared_channel(0).sequence(0,359); + colormap = tmp.HSVtoRGB(); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "lines", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_lines.jpg + **/ + static const CImg& lines_LUT256() { + static const unsigned char pal[] = { + 0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255, + 53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125, + 206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138, + 101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125, + 16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0, + 97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121, + 227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85, + 210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255, + 20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49, + 210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121, + 166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190, + 85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81, + 247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0, + 170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81, + 215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121, + 0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125, + 0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219, + 251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239, + 89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57, + 202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125, + 32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142, + 8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65, + 194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210, + 154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85, + 146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125, + 0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49, + 89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125, + 69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69, + 40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194, + 125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0, + 125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93, + 16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85, + 0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97, + 255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49, + 162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130, + 36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125, + 219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125, + 202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210, + 85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255, + 97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255, + 85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239, + 89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166, + 0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0, + 174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69, + 162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81, + 158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97, + 57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4, + 73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 }; + static const CImg colormap(pal,1,256,1,3,false); + return colormap; + } + + //! Return colormap \e "hot", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hot.jpg + **/ + static const CImg& hot_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "cool", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cool.jpg + **/ + static const CImg& cool_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3); + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "jet", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_jet.jpg + **/ + static const CImg& jet_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "flag", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_flag.jpg + **/ + static const CImg& flag_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255; + colormap.resize(1,256,1,3,0,2); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "cube", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cube.jpg + **/ + static const CImg& cube_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,8,1,3,(T)0); + colormap[1] = colormap[3] = colormap[5] = colormap[7] = + colormap[10] = colormap[11] = colormap[12] = colormap[13] = + colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Convert pixel values from sRGB to RGB color spaces. + CImg& sRGBtoRGB() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) + cimg_rofoff(*this,off) { + const Tfloat + sval = (Tfloat)_data[off]/255, + val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f)); + _data[off] = (T)cimg::cut(val*255,0,255); + } + return *this; + } + + //! Convert pixel values from sRGB to RGB color spaces \newinstance. + CImg get_sRGBtoRGB() const { + return CImg(*this,false).sRGBtoRGB(); + } + + //! Convert pixel values from RGB to sRGB color spaces. + CImg& RGBtosRGB() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) + cimg_rofoff(*this,off) { + const Tfloat + val = (Tfloat)_data[off]/255, + sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f); + _data[off] = (T)cimg::cut(sval*255,0,255); + } + return *this; + } + + //! Convert pixel values from RGB to sRGB color spaces \newinstance. + CImg get_RGBtosRGB() const { + return CImg(*this,false).RGBtosRGB(); + } + + //! Convert pixel values from RGB to HSI color spaces. + CImg& RGBtoHSI() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSI(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSI() const { + return CImg(*this,false).RGBtoHSI(); + } + + //! Convert pixel values from HSI to RGB color spaces. + CImg& HSItoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSItoRGB(): Instance is not a HSI image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSItoRGB() const { + return CImg< Tuchar>(*this,false).HSItoRGB(); + } + + //! Convert pixel values from RGB to HSL color spaces. + CImg& RGBtoHSL() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSL(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSL() const { + return CImg(*this,false).RGBtoHSL(); + } + + //! Convert pixel values from HSL to RGB color spaces. + CImg& HSLtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSLtoRGB(): Instance is not a HSL image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSLtoRGB() const { + return CImg(*this,false).HSLtoRGB(); + } + + //! Convert pixel values from RGB to HSV color spaces. + CImg& RGBtoHSV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSV() const { + return CImg(*this,false).RGBtoHSV(); + } + + //! Convert pixel values from HSV to RGB color spaces. + CImg& HSVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSVtoRGB(): Instance is not a HSV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSVtoRGB() const { + return CImg(*this,false).HSVtoRGB(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& RGBtoYCbCr() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYCbCr(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) + for (longT N = 0; N get_RGBtoYCbCr() const { + return CImg(*this,false).RGBtoYCbCr(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& YCbCrtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YCbCrtoRGB(): Instance is not a YCbCr image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) + for (longT N = 0; N get_YCbCrtoRGB() const { + return CImg(*this,false).YCbCrtoRGB(); + } + + //! Convert pixel values from RGB to YUV color spaces. + CImg& RGBtoYUV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYUV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) + for (longT N = 0; N get_RGBtoYUV() const { + return CImg(*this,false).RGBtoYUV(); + } + + //! Convert pixel values from YUV to RGB color spaces. + CImg& YUVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YUVtoRGB(): Instance is not a YUV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) + for (longT N = 0; N get_YUVtoRGB() const { + return CImg< Tuchar>(*this,false).YUVtoRGB(); + } + + //! Convert pixel values from RGB to CMY color spaces. + CImg& RGBtoCMY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoCMY(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_RGBtoCMY() const { + return CImg(*this,false).RGBtoCMY(); + } + + //! Convert pixel values from CMY to RGB color spaces. + CImg& CMYtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoRGB(): Instance is not a CMY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_CMYtoRGB() const { + return CImg(*this,false).CMYtoRGB(); + } + + //! Convert pixel values from CMY to CMYK color spaces. + CImg& CMYtoCMYK() { + return get_CMYtoCMYK().move_to(*this); + } + + //! Convert pixel values from CMY to CMYK color spaces \newinstance. + CImg get_CMYtoCMYK() const { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoCMYK(): Instance is not a CMY image.", + cimg_instance); + + CImg res(_width,_height,_depth,4); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) + for (longT N = 0; N=255) C = M = Y = 0; + else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } + pd1[N] = (Tfloat)cimg::cut(C,0,255), + pd2[N] = (Tfloat)cimg::cut(M,0,255), + pd3[N] = (Tfloat)cimg::cut(Y,0,255), + pd4[N] = (Tfloat)cimg::cut(K,0,255); + } + return res; + } + + //! Convert pixel values from CMYK to CMY color spaces. + CImg& CMYKtoCMY() { + return get_CMYKtoCMY().move_to(*this); + } + + //! Convert pixel values from CMYK to CMY color spaces \newinstance. + CImg get_CMYKtoCMY() const { + if (_spectrum!=4) + throw CImgInstanceException(_cimg_instance + "CMYKtoCMY(): Instance is not a CMYK image.", + cimg_instance); + + CImg res(_width,_height,_depth,3); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) + for (longT N = 0; N& RGBtoXYZ(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoXYZ(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_RGBtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).RGBtoXYZ(use_D65); + } + + //! Convert pixel values from XYZ to RGB color spaces. + /** + \param use_D65 Tell to use the D65 illuminant (D50 otherwise). + **/ + CImg& XYZtoRGB(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoRGB(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_XYZtoRGB(const bool use_D65=true) const { + return CImg(*this,false).XYZtoRGB(use_D65); + } + + //! Convert pixel values from XYZ to Lab color spaces. + CImg& XYZtoLab(const bool use_D65=true) { +#define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116) + + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoLab(): Instance is not a XYZ image.", + cimg_instance); + const CImg white = CImg(1,1,1,3,255).RGBtoXYZ(use_D65); + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) + for (longT N = 0; N get_XYZtoLab(const bool use_D65=true) const { + return CImg(*this,false).XYZtoLab(use_D65); + } + + //! Convert pixel values from Lab to XYZ color spaces. + CImg& LabtoXYZ(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "LabtoXYZ(): Instance is not a Lab image.", + cimg_instance); + const CImg white = CImg(1,1,1,3,255).RGBtoXYZ(use_D65); + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) + for (longT N = 0; N216?cX*cX*cX:(116*cX - 16)*27/24389), + Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389), + Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389); + p1[N] = (T)(X*white[0]); + p2[N] = (T)(Y*white[1]); + p3[N] = (T)(Z*white[2]); + } + return *this; + } + + //! Convert pixel values from Lab to XYZ color spaces \newinstance. + CImg get_LabtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).LabtoXYZ(use_D65); + } + + //! Convert pixel values from XYZ to xyY color spaces. + CImg& XYZtoxyY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoxyY(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) + for (longT N = 0; N0?sum:1; + p1[N] = (T)(X/nsum); + p2[N] = (T)(Y/nsum); + p3[N] = (T)Y; + } + return *this; + } + + //! Convert pixel values from XYZ to xyY color spaces \newinstance. + CImg get_XYZtoxyY() const { + return CImg(*this,false).XYZtoxyY(); + } + + //! Convert pixel values from xyY pixels to XYZ color spaces. + CImg& xyYtoXYZ() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "xyYtoXYZ(): Instance is not a xyY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) + for (longT N = 0; N0?py:1; + p1[N] = (T)(px*Y/ny); + p2[N] = (T)Y; + p3[N] = (T)((1 - px - py)*Y/ny); + } + return *this; + } + + //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance. + CImg get_xyYtoXYZ() const { + return CImg(*this,false).xyYtoXYZ(); + } + + //! Convert pixel values from RGB to Lab color spaces. + CImg& RGBtoLab(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoLab(use_D65); + } + + //! Convert pixel values from RGB to Lab color spaces \newinstance. + CImg get_RGBtoLab(const bool use_D65=true) const { + return CImg(*this,false).RGBtoLab(use_D65); + } + + //! Convert pixel values from Lab to RGB color spaces. + CImg& LabtoRGB(const bool use_D65=true) { + return LabtoXYZ().XYZtoRGB(use_D65); + } + + //! Convert pixel values from Lab to RGB color spaces \newinstance. + CImg get_LabtoRGB(const bool use_D65=true) const { + return CImg(*this,false).LabtoRGB(use_D65); + } + + //! Convert pixel values from RGB to xyY color spaces. + CImg& RGBtoxyY(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoxyY(); + } + + //! Convert pixel values from RGB to xyY color spaces \newinstance. + CImg get_RGBtoxyY(const bool use_D65=true) const { + return CImg(*this,false).RGBtoxyY(use_D65); + } + + //! Convert pixel values from xyY to RGB color spaces. + CImg& xyYtoRGB(const bool use_D65=true) { + return xyYtoXYZ().XYZtoRGB(use_D65); + } + + //! Convert pixel values from xyY to RGB color spaces \newinstance. + CImg get_xyYtoRGB(const bool use_D65=true) const { + return CImg(*this,false).xyYtoRGB(use_D65); + } + + //! Convert pixel values from RGB to CMYK color spaces. + CImg& RGBtoCMYK() { + return RGBtoCMY().CMYtoCMYK(); + } + + //! Convert pixel values from RGB to CMYK color spaces \newinstance. + CImg get_RGBtoCMYK() const { + return CImg(*this,false).RGBtoCMYK(); + } + + //! Convert pixel values from CMYK to RGB color spaces. + CImg& CMYKtoRGB() { + return CMYKtoCMY().CMYtoRGB(); + } + + //! Convert pixel values from CMYK to RGB color spaces \newinstance. + CImg get_CMYKtoRGB() const { + return CImg(*this,false).CMYKtoRGB(); + } + + //@} + //------------------------------------------ + // + //! \name Geometric / Spatial Manipulation + //@{ + //------------------------------------------ + + static float _cimg_lanczos(const float x) { + if (x<=-2 || x>=2) return 0; + const float a = (float)cimg::PI*x, b = 0.5f*a; + return (float)(x?std::sin(a)*std::sin(b)/(a*b):1); + } + + //! Resize image to new dimensions. + /** + \param size_x Number of columns (new size along the X-axis). + \param size_y Number of rows (new size along the Y-axis). + \param size_z Number of slices (new size along the Z-axis). + \param size_c Number of vector-channels (new size along the C-axis). + \param interpolation_type Method of interpolation: + - -1 = no interpolation: raw memory resizing. + - 0 = no interpolation: additional space is filled according to \p boundary_conditions. + - 1 = nearest-neighbor interpolation. + - 2 = moving average interpolation. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = cubic interpolation. + - 6 = lanczos interpolation. + \param boundary_conditions Type of boundary conditions used if necessary. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const int size_x, const int size_y=-100, + const int size_z=-100, const int size_c=-100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + if (!size_x || !size_y || !size_z || !size_c) return assign(); + const unsigned int + _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x), + _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y), + _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z), + _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c), + sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1; + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this; + if (is_empty()) return assign(sx,sy,sz,sc,(T)0); + if (interpolation_type==-1 && sx*sy*sz*sc==size()) { + _width = sx; _height = sy; _depth = sz; _spectrum = sc; + return *this; + } + return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c).move_to(*this); + } + + //! Resize image to new dimensions \newinstance. + CImg get_resize(const int size_x, const int size_y = -100, + const int size_z = -100, const int size_c = -100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 || + centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1) + throw CImgArgumentException(_cimg_instance + "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].", + cimg_instance, + centering_x,centering_y,centering_z,centering_c); + + if (!size_x || !size_y || !size_z || !size_c) return CImg(); + const unsigned int + sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)), + sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)), + sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)), + sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100)); + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this; + if (is_empty()) return CImg(sx,sy,sz,sc,(T)0); + CImg res; + switch (interpolation_type) { + + // Raw resizing. + // + case -1 : + std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc)); + break; + + // No interpolation. + // + case 0 : { + const int + xc = (int)(centering_x*((int)sx - width())), + yc = (int)(centering_y*((int)sy - height())), + zc = (int)(centering_z*((int)sz - depth())), + cc = (int)(centering_c*((int)sc - spectrum())); + + switch (boundary_conditions) { + case 3 : { // Mirror + res.assign(sx,sy,sz,sc); + const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024)) + cimg_forXYZC(res,x,y,z,c) { + const int + mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2), + mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2); + res(x,y,z,c) = (*this)(mx sprite; + if (xc>0) { // X-backward + res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite); + } + if (xc + width()<(int)sx) { // X-forward + res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1, + zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite); + } + if (yc>0) { // Y-backward + res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite); + } + if (yc + height()<(int)sy) { // Y-forward + res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1, + zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite); + } + if (zc>0) { // Z-backward + res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite); + for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite); + } + if (zc + depth()<(int)sz) { // Z-forward + res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite); + } + if (cc>0) { // C-backward + res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite); + for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite); + } + if (cc + spectrum()<(int)sc) { // C-forward + res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite); + for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); + } + } break; + default : // Dirichlet + res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this); + } + break; + } break; + + // Nearest neighbor interpolation. + // + case 1 : { + res.assign(sx,sy,sz,sc); + CImg off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1); + const ulongT + wh = (ulongT)_width*_height, + whd = (ulongT)_width*_height*_depth, + sxy = (ulongT)sx*sy, + sxyz = (ulongT)sx*sy*sz, + one = (ulongT)1; + if (sx==_width) off_x.fill(1); + else { + ulongT *poff_x = off_x._data, curr = 0; + cimg_forX(res,x) { + const ulongT old = curr; + curr = (x + one)*_width/sx; + *(poff_x++) = curr - old; + } + } + if (sy==_height) off_y.fill(_width); + else { + ulongT *poff_y = off_y._data, curr = 0; + cimg_forY(res,y) { + const ulongT old = curr; + curr = (y + one)*_height/sy; + *(poff_y++) = _width*(curr - old); + } + *poff_y = 0; + } + if (sz==_depth) off_z.fill(wh); + else { + ulongT *poff_z = off_z._data, curr = 0; + cimg_forZ(res,z) { + const ulongT old = curr; + curr = (z + one)*_depth/sz; + *(poff_z++) = wh*(curr - old); + } + *poff_z = 0; + } + if (sc==_spectrum) off_c.fill(whd); + else { + ulongT *poff_c = off_c._data, curr = 0; + cimg_forC(res,c) { + const ulongT old = curr; + curr = (c + one)*_spectrum/sc; + *(poff_c++) = whd*(curr - old); + } + *poff_c = 0; + } + + T *ptrd = res._data; + const T* ptrc = _data; + const ulongT *poff_c = off_c._data; + for (unsigned int c = 0; c_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,_height,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256)) + cimg_forYZC(tmp,y,z,v) { + for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; + if (!b) { tmp(t++,y,z,v)/=_width; b = _width; } + if (!c) { ++s; c = sx; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sy!=_height) { + if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256)) + cimg_forXZC(tmp,x,z,v) { + for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; + else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; + if (!b) { tmp(x,t++,z,v)/=_height; b = _height; } + if (!c) { ++s; c = sy; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sz!=_depth) { + if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,sz,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256)) + cimg_forXYC(tmp,x,y,v) { + for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; + else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; + if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; } + if (!c) { ++s; c = sz; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sc!=_spectrum) { + if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res); + else { + CImg tmp(sx,sy,sz,sc,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sc>=256 && _width*_height*_depth>=256)) + cimg_forXYZ(tmp,x,y,z) { + for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; + else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; + if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; } + if (!c) { ++s; c = sc; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + } break; + + // Linear interpolation. + // + case 3 : { + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1; + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrs resx, resy, resz, resc; + if (sx!=_width) { + if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + resx.assign(sx,_height,_depth,_spectrum,(T)0); + const int dx = (int)(2*sx), dy = 2*width(); + int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0; + cimg_forX(resx,x) if ((err-=dy)<=0) { + cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c); + ++xs; + err+=dx; + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + resy.assign(sx,sy,_depth,_spectrum,(T)0); + const int dx = (int)(2*sy), dy = 2*height(); + int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0; + cimg_forY(resy,y) if ((err-=dy)<=0) { + cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c); + ++ys; + err+=dx; + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + resz.assign(sx,sy,sz,_spectrum,(T)0); + const int dx = (int)(2*sz), dy = 2*depth(); + int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0; + cimg_forZ(resz,z) if ((err-=dy)<=0) { + cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c); + ++zs; + err+=dx; + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + resc.assign(sx,sy,sz,sc,(T)0); + const int dx = (int)(2*sc), dy = 2*spectrum(); + int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0; + cimg_forC(resc,c) if ((err-=dy)<=0) { + cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs); + ++cs; + err+=dx; + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Cubic interpolation. + // + case 5 : { + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2); + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1, + val3 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Lanczos interpolation. + // + case 6 : { + const double vmin = (double)cimg::type::min(), vmax = (double)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, + *const ptrsmax = ptrs0 + (_width - 2); + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2, + val4 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, + *const ptrsmax = ptrs0 + (_height - 2)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, + *const ptrsmax = ptrs0 + (_depth - 2)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, + *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Unknown interpolation. + // + default : + throw CImgArgumentException(_cimg_instance + "resize(): Invalid specified interpolation %d " + "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | " + "5=cubic | 6=lanczos }).", + cimg_instance, + interpolation_type); + } + return res; + } + + //! Resize image to dimensions of another image. + /** + \param src Reference image used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + template + CImg& resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of another image \newinstance. + template + CImg get_resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window. + /** + \param disp Reference display window used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + CImg& resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window \newinstance. + CImg get_resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to half-size along XY axes, using an optimized filter. + CImg& resize_halfXY() { + return get_resize_halfXY().move_to(*this); + } + + //! Resize image to half-size along XY axes, using an optimized filter \newinstance. + CImg get_resize_halfXY() const { + if (is_empty()) return *this; + static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + CImg I(9), res(_width/2,_height/2,_depth,_spectrum); + T *ptrd = res._data; + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) + if (x%2 && y%2) *(ptrd++) = (T) + (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] + + I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] + + I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]); + return res; + } + + //! Resize image to double-size, using the Scale2X algorithm. + /** + \note Use anisotropic upscaling algorithm + described here. + **/ + CImg& resize_doubleXY() { + return get_resize_doubleXY().move_to(*this); + } + + //! Resize image to double-size, using the Scale2X algorithm \newinstance. + CImg get_resize_doubleXY() const { +#define _cimg_gs2x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width) + +#define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs2x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(_width<<1,_height<<1,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width; + _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) { + if (Icp!=Icn && Ipc!=Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = Ipc==Icn?Ipc:Icc; + *(ptrd2++) = Icn==Inc?Inc:Icc; + } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; } + } + } + return res; + } + + //! Resize image to triple-size, using the Scale3X algorithm. + /** + \note Use anisotropic upscaling algorithm + described here. + **/ + CImg& resize_tripleXY() { + return get_resize_tripleXY().move_to(*this); + } + + //! Resize image to triple-size, using the Scale3X algorithm \newinstance. + CImg get_resize_tripleXY() const { +#define _cimg_gs3x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width) + +#define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs3x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(3*_width,3*_height,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width, + *ptrd3 = ptrd2 + res._width; + _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) { + if (Icp != Icn && Ipc != Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc; + *(ptrd2++) = Icc; + *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc; + *(ptrd3++) = Ipc==Icn?Ipc:Icc; + *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc; + *(ptrd3++) = Icn==Inc?Inc:Icc; + } else { + *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc; + *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; + *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; + } + } + } + return res; + } + + //! Mirror image content along specified axis. + /** + \param axis Mirror axis + **/ + CImg& mirror(const char axis) { + if (is_empty()) return *this; + T *pf, *pb, *buf = 0; + switch (cimg::lowercase(axis)) { + case 'x' : { + pf = _data; pb = data(_width - 1); + const unsigned int width2 = _width/2; + for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) { + for (unsigned int x = 0; x get_mirror(const char axis) const { + return (+*this).mirror(axis); + } + + //! Mirror image content along specified axes. + /** + \param axes Mirror axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" + **/ + CImg& mirror(const char *const axes) { + for (const char *s = axes; *s; ++s) mirror(*s); + return *this; + } + + //! Mirror image content along specified axes \newinstance. + CImg get_mirror(const char *const axes) const { + return (+*this).mirror(axes); + } + + //! Shift image content. + /** + \param delta_x Amount of displacement along the X-axis. + \param delta_y Amount of displacement along the Y-axis. + \param delta_z Amount of displacement along the Z-axis. + \param delta_c Amount of displacement along the C-axis. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const unsigned int boundary_conditions=0) { + if (is_empty()) return *this; + if (boundary_conditions==3) + return get_crop(-delta_x,-delta_y,-delta_z,-delta_c, + width() - delta_x - 1, + height() - delta_y - 1, + depth() - delta_z - 1, + spectrum() - delta_c - 1,3).move_to(*this); + if (delta_x) // Shift along X-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width()); + if (!ndelta_x) return *this; + CImg buf(cimg::abs(ndelta_x)); + if (ndelta_x>0) cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T)); + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T)); + std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T)); + std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_x<0) { + const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(_width - 1,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width())?width() - 1:delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(0,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width()) return fill((T)0); + if (delta_x<0) cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T)); + std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T)); + std::memset(data(0,y,z,c),0,delta_x*sizeof(T)); + } + } + + if (delta_y) // Shift along Y-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height()); + if (!ndelta_y) return *this; + CImg buf(width(),cimg::abs(ndelta_y)); + if (ndelta_y>0) cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T)); + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T)); + std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T)); + std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_y<0) { + const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c); + for (int l = 0; l=height())?height() - 1:delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c); + for (int l = 0; l=height()) return fill((T)0); + if (delta_y<0) cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T)); + std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T)); + std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T)); + } + } + + if (delta_z) // Shift along Z-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth()); + if (!ndelta_z) return *this; + CImg buf(width(),height(),cimg::abs(ndelta_z)); + if (ndelta_z>0) cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T)); + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T)); + } else cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T)); + std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_z<0) { + const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c); + for (int l = 0; l=depth())?depth() - 1:delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c); + for (int l = 0; l=depth()) return fill((T)0); + if (delta_z<0) cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T)); + std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T)); + } else cimg_forC(*this,c) { + std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T)); + std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T)); + } + } + + if (delta_c) // Shift along C-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum()); + if (!ndelta_c) return *this; + CImg buf(width(),height(),depth(),cimg::abs(ndelta_c)); + if (ndelta_c>0) { + std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T)); + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T)); + } else { + std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T)); + std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T)); + std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_c<0) { + const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c; + if (!ndelta_c) return *this; + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1); + for (int l = 0; l=spectrum())?spectrum() - 1:delta_c; + if (!ndelta_c) return *this; + std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,1); + for (int l = 0; l=spectrum()) return fill((T)0); + if (delta_c<0) { + std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T)); + std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T)); + } else { + std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T)); + std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T)); + } + } + return *this; + } + + //! Shift image content \newinstance. + CImg get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const unsigned int boundary_conditions=0) const { + return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions); + } + + //! Permute axes order. + /** + \param axes_order Axes permutations, as a C-string of 4 characters. + This function permutes image content regarding the specified axes permutation. + **/ + CImg& permute_axes(const char *const axes_order) { + return get_permute_axes(axes_order).move_to(*this); + } + + //! Permute axes order \newinstance. + CImg get_permute_axes(const char *const axes_order) const { + const T foo = (T)0; + return _permute_axes(axes_order,foo); + } + + template + CImg _permute_axes(const char *const axes_order, const t&) const { + if (is_empty() || !axes_order) return CImg(*this,false); + CImg res; + const T* ptrs = _data; + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } + else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; } + } + if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { + const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); + ulongT wh, whd; + switch (code) { + case 0x0123 : // xyzc + return +*this; + case 0x0132 : // xycz + res.assign(_width,_height,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0213 : // xzyc + res.assign(_width,_depth,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x0231 : // xzcy + res.assign(_width,_depth,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x0312 : // xcyz + res.assign(_width,_spectrum,_height,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0321 : // xczy + res.assign(_width,_spectrum,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x1023 : // yxzc + res.assign(_height,_width,_depth,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1032 : // yxcz + res.assign(_height,_width,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1203 : // yzxc + res.assign(_height,_depth,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1230 : // yzcx + res.assign(_height,_depth,_spectrum,_width); + switch (_width) { + case 1 : { + t *ptr_r = res.data(0,0,0,0); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); + } + } break; + case 2 : { + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + ptrs+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + ptrs+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA + t + *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), + *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + *(ptr_a++) = (t)ptrs[3]; + ptrs+=4; + } + } break; + default : { + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); + return res; + } + } + break; + case 0x1302 : // ycxz + res.assign(_height,_spectrum,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1320 : // yczx + res.assign(_height,_spectrum,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2013 : // zxyc + res.assign(_depth,_width,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2031 : // zxcy + res.assign(_depth,_width,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2103 : // zyxc + res.assign(_depth,_height,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2130 : // zycx + res.assign(_depth,_height,_spectrum,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2301 : // zcxy + res.assign(_depth,_spectrum,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2310 : // zcyx + res.assign(_depth,_spectrum,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3012 : // cxyz + res.assign(_spectrum,_width,_height,_depth); + switch (_spectrum) { + case 1 : { + const T *ptr_r = data(0,0,0,0); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); + } break; + case 2 : { + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd[3] = (t)*(ptr_a++); + ptrd+=4; + } + } break; + default : { + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); + } + } + break; + case 0x3021 : // cxzy + res.assign(_spectrum,_width,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3102 : // cyxz + res.assign(_spectrum,_height,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x3120 : // cyzx + res.assign(_spectrum,_height,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3201 : // czxy + res.assign(_spectrum,_depth,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3210 : // czyx + res.assign(_spectrum,_depth,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); + break; + } + } + if (!res) + throw CImgArgumentException(_cimg_instance + "permute_axes(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + return res; + } + + //! Unroll pixel values along specified axis. + /** + \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c'). + **/ + CImg& unroll(const char axis) { + const unsigned int siz = (unsigned int)size(); + if (siz) switch (cimg::lowercase(axis)) { + case 'x' : _width = siz; _height = _depth = _spectrum = 1; break; + case 'y' : _height = siz; _width = _depth = _spectrum = 1; break; + case 'z' : _depth = siz; _width = _height = _spectrum = 1; break; + case 'c' : _spectrum = siz; _width = _height = _depth = 1; break; + } + return *this; + } + + //! Unroll pixel values along specified axis \newinstance. + CImg get_unroll(const char axis) const { + return (+*this).unroll(axis); + } + + //! Rotate image with arbitrary angle. + /** + \param angle Rotation angle, in degrees. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note The size of the image is modified. + **/ + CImg& rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate image with arbitrary angle \newinstance. + CImg get_rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res; + const float nangle = cimg::mod(angle,360.f); + if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles + const int wm1 = width() - 1, hm1 = height() - 1; + const int iangle = (int)nangle/90; + switch (iangle) { + case 1 : { // 90 deg + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c); + } break; + case 2 : { // 180 deg + res.assign(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c); + } break; + case 3 : { // 270 deg + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c); + } break; + default : // 0 deg + return *this; + } + } else { // Generic angle + const float + rad = (float)(nangle*cimg::PI/180.), + ca = (float)std::cos(rad), sa = (float)std::sin(rad), + ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa), + vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca), + w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1); + res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum); + const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1); + _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2); + } + return res; + } + + //! Rotate image with arbitrary angle, around a center point. + /** + \param angle Rotation angle, in degrees. + \param cx X-coordinate of the rotation center. + \param cy Y-coordinate of the rotation center. + \param interpolation Type of interpolation, { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions, { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& rotate(const float angle, const float cx, const float cy, + const unsigned int interpolation, const unsigned int boundary_conditions=0) { + return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate image with arbitrary angle, around a center point \newinstance. + CImg get_rotate(const float angle, const float cx, const float cy, + const unsigned int interpolation, const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy); + return res; + } + + // [internal] Perform 2D rotation with arbitrary angle. + void _rotate(CImg& res, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions, + const float w2, const float h2, + const float rw2, const float rh2) const { + const float + rad = (float)(angle*cimg::PI/180.), + ca = (float)std::cos(rad), sa = (float)std::sin(rad); + + switch (boundary_conditions) { + case 3 : { // Mirror + + switch (interpolation) { + case 2 : { // Cubic interpolation + const float ww = 2.f*width(), hh = 2.f*height(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048)) + cimg_forXYZC(res,x,y,z,c) { + const float xc = x - rw2, yc = y - rh2, + mx = cimg::mod(w2 + xc*ca + yc*sa,ww), + my = cimg::mod(h2 - xc*sa + yc*ca,hh); + res(x,y,z,c) = _cubic_atXY_c(mx{ 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note Most of the time, size of the image is modified. + **/ + CImg rotate(const float u, const float v, const float w, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate volumetric image with arbitrary angle and axis \newinstance. + CImg get_rotate(const float u, const float v, const float w, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions) const { + if (is_empty()) return *this; + CImg res; + const float + w1 = _width - 1, h1 = _height - 1, d1 = _depth -1, + w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1; + CImg R = CImg::rotation_matrix(u,v,w,angle); + const CImg + X = R*CImg(8,3,1,1, + 0.f,w1,w1,0.f,0.f,w1,w1,0.f, + 0.f,0.f,h1,h1,0.f,0.f,h1,h1, + 0.f,0.f,0.f,0.f,d1,d1,d1,d1); + float + xm, xM = X.get_shared_row(0).max_min(xm), + ym, yM = X.get_shared_row(1).max_min(ym), + zm, zM = X.get_shared_row(2).max_min(zm); + const int + dx = (int)cimg::round(xM - xm), + dy = (int)cimg::round(yM - ym), + dz = (int)cimg::round(zM - zm); + R.transpose(); + res.assign(1 + dx,1 + dy,1 + dz,_spectrum); + const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz; + _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2); + return res; + } + + //! Rotate volumetric image with arbitrary angle and axis, around a center point. + /** + \param u X-coordinate of the 3D rotation axis. + \param v Y-coordinate of the 3D rotation axis. + \param w Z-coordinate of the 3D rotation axis. + \param angle Rotation angle, in degrees. + \param cx X-coordinate of the rotation center. + \param cy Y-coordinate of the rotation center. + \param cz Z-coordinate of the rotation center. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic }. + \note Most of the time, size of the image is modified. + **/ + CImg rotate(const float u, const float v, const float w, const float angle, + const float cx, const float cy, const float cz, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance. + CImg get_rotate(const float u, const float v, const float w, const float angle, + const float cx, const float cy, const float cz, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + CImg R = CImg::rotation_matrix(u,v,w,-angle); + _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz); + return res; + } + + // [internal] Perform 3D rotation with arbitrary axis and angle. + void _rotate(CImg& res, const CImg& R, + const unsigned int interpolation, const unsigned int boundary_conditions, + const float w2, const float h2, const float d2, + const float rw2, const float rh2, const float rd2) const { + switch (boundary_conditions) { + case 3 : // Mirror + switch (interpolation) { + case 2 : { // Cubic interpolation + const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048)) + cimg_forXYZ(res,x,y,z) { + const float + xc = x - rw2, yc = y - rh2, zc = z - rd2, + X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), + Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), + Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); + cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X{ 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + template + CImg& warp(const CImg& p_warp, const unsigned int mode=0, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this); + } + + //! Warp image content by a warping field \newinstance + template + CImg get_warp(const CImg& p_warp, const unsigned int mode=0, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty() || !p_warp) return *this; + if (mode && !is_sameXYZ(p_warp)) + throw CImgArgumentException(_cimg_instance + "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) " + "have different XYZ dimensions.", + cimg_instance, + p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data); + + CImg res(p_warp._width,p_warp._height,p_warp._depth,_spectrum); + + if (p_warp._spectrum==1) { // 1D warping + if (mode>=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = x + (int)cimg::round(*(ptrs0++)); + if (X>=0 && X=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = (int)cimg::round(*(ptrs0++)); + if (X>=0 && X=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++)); + if (X>=0 && X=0 && Y=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++)); + if (X>=0 && X=0 && Y=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++), + z + (float)*(ptrs2++),c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int + X = x + (int)cimg::round(*(ptrs0++)), + Y = y + (int)cimg::round(*(ptrs1++)), + Z = z + (int)cimg::round(*(ptrs2++)); + if (X>=0 && X=0 && Y=0 && Z=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int + X = (int)cimg::round(*(ptrs0++)), + Y = (int)cimg::round(*(ptrs1++)), + Z = (int)cimg::round(*(ptrs2++)); + if (X>=0 && X=0 && Y=0 && Z get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const { + if (is_empty() || _depth<2) return +*this; + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + const CImg + img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1), + img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc"). + resize(_depth,_height,1,-100,-1), + img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1); + return CImg(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())). + draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy). + draw_image(0,img_xy._height,img_xz); + } + + //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace. + CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) { + if (_depth<2) return *this; + return get_projections2d(x0,y0,z0).move_to(*this); + } + + //! Crop image region. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param z0 = Z-coordinate of the upper-left crop rectangle corner. + \param c0 = C-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param z1 = Z-coordinate of the lower-right crop rectangle corner. + \param c1 = C-coordinate of the lower-right crop rectangle corner. + \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const unsigned int boundary_conditions=0) { + return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const unsigned int boundary_conditions=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "crop(): Empty instance.", + cimg_instance); + const int + nx0 = x0=0 && nx1=0 && ny1=0 && nz1=0 && nc1 res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0); + if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) + switch (_boundary_conditions) { + case 3 : { // Mirror + const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) { + const int + mx = cimg::mod(nx0 + x,w2), + my = cimg::mod(ny0 + y,h2), + mz = cimg::mod(nz0 + z,d2), + mc = cimg::mod(nc0 + c,s2); + res(x,y,z,c) = (*this)(mx=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) { + res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()), + cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum())); + } + } break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c); + break; + default : // Dirichlet + res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this); + } + else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this); + return res; + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const unsigned int boundary_conditions=0) { + return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, + const int x1, const int y1, + const unsigned int boundary_conditions=0) { + return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, + const int x1, const int y1, + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) { + return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const { + return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Autocrop image region, regarding the specified background value. + CImg& autocrop(const T& value, const char *const axes="czyx") { + if (is_empty()) return *this; + for (const char *s = axes; *s; ++s) { + const char axis = cimg::lowercase(*s); + const CImg coords = _autocrop(value,axis); + if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels + else switch (axis) { + case 'x' : { + const int x0 = coords[0], x1 = coords[1]; + if (x0>=0 && x1>=0) crop(x0,x1); + } break; + case 'y' : { + const int y0 = coords[0], y1 = coords[1]; + if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1); + } break; + case 'z' : { + const int z0 = coords[0], z1 = coords[1]; + if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1); + } break; + default : { + const int c0 = coords[0], c1 = coords[1]; + if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background value \newinstance. + CImg get_autocrop(const T& value, const char *const axes="czyx") const { + return (+*this).autocrop(value,axes); + } + + //! Autocrop image region, regarding the specified background color. + /** + \param color Color used for the crop. If \c 0, color is guessed. + \param axes Axes used for the crop. + **/ + CImg& autocrop(const T *const color=0, const char *const axes="zyx") { + if (is_empty()) return *this; + if (!color) { // Guess color + const CImg col1 = get_vector_at(0,0,0); + const unsigned int w = _width, h = _height, d = _depth, s = _spectrum; + autocrop(col1,axes); + if (_width==w && _height==h && _depth==d && _spectrum==s) { + const CImg col2 = get_vector_at(w - 1,h - 1,d - 1); + autocrop(col2,axes); + } + return *this; + } + for (const char *s = axes; *s; ++s) { + const char axis = cimg::lowercase(*s); + switch (axis) { + case 'x' : { + int x0 = width(), x1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'x'); + const int nx0 = coords[0], nx1 = coords[1]; + if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); } + } + if (x0==width() && x1==-1) return assign(); else crop(x0,x1); + } break; + case 'y' : { + int y0 = height(), y1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'y'); + const int ny0 = coords[0], ny1 = coords[1]; + if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); } + } + if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1); + } break; + default : { + int z0 = depth(), z1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'z'); + const int nz0 = coords[0], nz1 = coords[1]; + if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); } + } + if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background color \newinstance. + CImg get_autocrop(const T *const color=0, const char *const axes="zyx") const { + return (+*this).autocrop(color,axes); + } + + CImg _autocrop(const T& value, const char axis) const { + CImg res; + switch (cimg::lowercase(axis)) { + case 'x' : { + int x0 = -1, x1 = -1; + cimg_forX(*this,x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); } + if (x0>=0) { + for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); } + } + res = CImg::vector(x0,x1); + } break; + case 'y' : { + int y0 = -1, y1 = -1; + cimg_forY(*this,y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); } + if (y0>=0) { + for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); } + } + res = CImg::vector(y0,y1); + } break; + case 'z' : { + int z0 = -1, z1 = -1; + cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); } + if (z0>=0) { + for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); } + } + res = CImg::vector(z0,z1); + } break; + default : { + int c0 = -1, c1 = -1; + cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); } + if (c0>=0) { + for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; } + } + res = CImg::vector(c0,c1); + } + } + return res; + } + + //! Return specified image column. + /** + \param x0 Image column. + **/ + CImg get_column(const int x0) const { + return get_columns(x0,x0); + } + + //! Return specified image column \inplace. + CImg& column(const int x0) { + return columns(x0,x0); + } + + //! Return specified range of image columns. + /** + \param x0 Starting image column. + \param x1 Ending image column. + **/ + CImg& columns(const int x0, const int x1) { + return get_columns(x0,x1).move_to(*this); + } + + //! Return specified range of image columns \inplace. + CImg get_columns(const int x0, const int x1) const { + return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1); + } + + //! Return specified image row. + CImg get_row(const int y0) const { + return get_rows(y0,y0); + } + + //! Return specified image row \inplace. + /** + \param y0 Image row. + **/ + CImg& row(const int y0) { + return rows(y0,y0); + } + + //! Return specified range of image rows. + /** + \param y0 Starting image row. + \param y1 Ending image row. + **/ + CImg get_rows(const int y0, const int y1) const { + return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1); + } + + //! Return specified range of image rows \inplace. + CImg& rows(const int y0, const int y1) { + return get_rows(y0,y1).move_to(*this); + } + + //! Return specified image slice. + /** + \param z0 Image slice. + **/ + CImg get_slice(const int z0) const { + return get_slices(z0,z0); + } + + //! Return specified image slice \inplace. + CImg& slice(const int z0) { + return slices(z0,z0); + } + + //! Return specified range of image slices. + /** + \param z0 Starting image slice. + \param z1 Ending image slice. + **/ + CImg get_slices(const int z0, const int z1) const { + return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1); + } + + //! Return specified range of image slices \inplace. + CImg& slices(const int z0, const int z1) { + return get_slices(z0,z1).move_to(*this); + } + + //! Return specified image channel. + /** + \param c0 Image channel. + **/ + CImg get_channel(const int c0) const { + return get_channels(c0,c0); + } + + //! Return specified image channel \inplace. + CImg& channel(const int c0) { + return channels(c0,c0); + } + + //! Return specified range of image channels. + /** + \param c0 Starting image channel. + \param c1 Ending image channel. + **/ + CImg get_channels(const int c0, const int c1) const { + return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1); + } + + //! Return specified range of image channels \inplace. + CImg& channels(const int c0, const int c1) { + return get_channels(c0,c1).move_to(*this); + } + + //! Return stream line of a 2D or 3D vector field. + CImg get_streamline(const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false) const { + if (_spectrum!=2 && _spectrum!=3) + throw CImgInstanceException(_cimg_instance + "streamline(): Instance is not a 2D or 3D vector field.", + cimg_instance); + if (_spectrum==2) { + if (is_oriented_only) { + typename CImg::_functor4d_streamline2d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.f,_height - 1.f,0.f); + } else { + typename CImg::_functor4d_streamline2d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.f,_height - 1.f,0.f); + } + } + if (is_oriented_only) { + typename CImg::_functor4d_streamline3d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); + } + typename CImg::_functor4d_streamline3d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); + } + + //! Return stream line of a 3D vector field. + /** + \param func Vector field function. + \param x X-coordinate of the starting point of the streamline. + \param y Y-coordinate of the starting point of the streamline. + \param z Z-coordinate of the starting point of the streamline. + \param L Streamline length. + \param dl Streamline length increment. + \param interpolation_type Type of interpolation. + Can be { 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }. + \param is_backward_tracking Tells if the streamline is estimated forward or backward. + \param is_oriented_only Tells if the direction of the vectors must be ignored. + \param x0 X-coordinate of the first bounding-box vertex. + \param y0 Y-coordinate of the first bounding-box vertex. + \param z0 Z-coordinate of the first bounding-box vertex. + \param x1 X-coordinate of the second bounding-box vertex. + \param y1 Y-coordinate of the second bounding-box vertex. + \param z1 Z-coordinate of the second bounding-box vertex. + **/ + template + static CImg streamline(const tfunc& func, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + if (dl<=0) + throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g " + "(should be >0).", + pixel_type(), + dl); + + const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1); + if (L<=0 || (is_bounded && (xx1 || yy1 || zz1))) return CImg(); + const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1); + CImg coordinates(size_L,3); + const float dl2 = dl/2; + float + *ptr_x = coordinates.data(0,0), + *ptr_y = coordinates.data(0,1), + *ptr_z = coordinates.data(0,2), + pu = (float)(dl*func(x,y,z,0)), + pv = (float)(dl*func(x,y,z,1)), + pw = (float)(dl*func(x,y,z,2)), + X = x, Y = y, Z = z; + + switch (interpolation_type) { + case 0 : { // Nearest integer interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + const int + xi = (int)(X>0?X + 0.5f:X - 0.5f), + yi = (int)(Y>0?Y + 0.5f:Y - 0.5f), + zi = (int)(Z>0?Z + 0.5f:Z - 0.5f); + float + u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)), + v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)), + w = (float)(dl*func((float)xi,(float)yi,(float)zi,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 1 : { // First-order interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u = (float)(dl*func(X,Y,Z,0)), + v = (float)(dl*func(X,Y,Z,1)), + w = (float)(dl*func(X,Y,Z,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 2 : { // Second order interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)), + v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)), + w = (float)(dl*func(X + u0,Y + v0,Z + w0,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + default : { // Fourth order interpolation + cimg_forX(coordinates,k) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)), + v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)), + w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2)); + if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; } + float + u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)), + v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)), + w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; } + float + u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)), + v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)), + w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; } + const float + u = (u0 + u3)/3 + (u1 + u2)/1.5f, + v = (v0 + v3)/3 + (v1 + v2)/1.5f, + w = (w0 + w3)/3 + (w1 + w2)/1.5f; + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } + } + if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0); + return coordinates; + } + + //! Return stream line of a 3D vector field \overloading. + static CImg streamline(const char *const expression, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=true, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + _functor4d_streamline_expr func(expression); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1); + } + + struct _functor4d_streamline2d_directed { + const CImg& ref; + _functor4d_streamline2d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0; + } + }; + + struct _functor4d_streamline3d_directed { + const CImg& ref; + _functor4d_streamline3d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref._linear_atXYZ(x,y,z,c); + } + }; + + struct _functor4d_streamline2d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline2d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,1,2); } + ~_functor4d_streamline2d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign2d(i,j) \ + if (I(i,j,0)*I(0,0,0) + I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z; + const float + dx = x - xi, + dy = y - yi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; + if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width() - 1; + if (nxi>=ref.width()) nxi = ref.width() - 1; + if (yi<0) yi = 0; + if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height() - 1; + if (nyi>=ref.height()) nyi = ref.height() - 1; + I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1); + I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1); + I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1); + I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1); + _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1); + } + return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0; + } + }; + + struct _functor4d_streamline3d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline3d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,2,3); } + ~_functor4d_streamline3d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0) + I(i,j,k,1)*I(0,0,0,1) + I(i,j,k,2)*I(0,0,0,2)<0) { \ + I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z - (z>=0?0:1), nzi = zi + 1; + const float + dx = x - xi, + dy = y - yi, + dz = z - zi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; + if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width() - 1; + if (nxi>=ref.width()) nxi = ref.width() - 1; + if (yi<0) yi = 0; + if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height() - 1; + if (nyi>=ref.height()) nyi = ref.height() - 1; + if (zi<0) zi = 0; + if (nzi<0) nzi = 0; + if (zi>=ref.depth()) zi = ref.depth() - 1; + if (nzi>=ref.depth()) nzi = ref.depth() - 1; + I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); + I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0); + I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2); + I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); + I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0); + I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2); + I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1); + I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0); + I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2); + I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1); + I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0); + I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2); + _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0); + _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1); + } + return (float)pI->_linear_atXYZ(dx,dy,dz,c); + } + }; + + struct _functor4d_streamline_expr { + _cimg_math_parser *mp; + ~_functor4d_streamline_expr() { mp->end(); delete mp; } + _functor4d_streamline_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,"streamline",CImg::const_empty(),0); + } + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)(*mp)(x,y,z,c); + } + }; + + //! Return a shared-memory image referencing a range of pixels of the image instance. + /** + \param x0 X-coordinate of the starting pixel. + \param x1 X-coordinate of the ending pixel. + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(x0,y0,z0,c0), + end = (ulongT)offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of pixels of the image instance \const. + const CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(x0,y0,z0,c0), + end = (ulongT)offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance. + /** + \param y0 Y-coordinate of the starting row. + \param y1 Y-coordinate of the ending row. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(0,y0,z0,c0), + end = (ulongT)offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", + cimg_instance, + _width - 1,y0,y1,z0,c0); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance \const. + const CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(0,y0,z0,c0), + end = (ulongT)offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", + cimg_instance, + _width - 1,y0,y1,z0,c0); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); + } + + //! Return a shared-memory image referencing one row of the image instance. + /** + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared-memory image referencing one row of the image instance \const. + const CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared memory image referencing a range of slices of the image instance. + /** + \param z0 Z-coordinate of the starting slice. + \param z1 Z-coordinate of the ending slice. + \param c0 C-coordinate. + **/ + CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(0,0,z0,c0), + end = (ulongT)offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width - 1,_height - 1,z0,z1,c0); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); + } + + //! Return a shared memory image referencing a range of slices of the image instance \const. + const CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(0,0,z0,c0), + end = (ulongT)offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width - 1,_height - 1,z0,z1,c0); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); + } + + //! Return a shared-memory image referencing one slice of the image instance. + /** + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing one slice of the image instance \const. + const CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) const { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing a range of channels of the image instance. + /** + \param c0 C-coordinate of the starting channel. + \param c1 C-coordinate of the ending channel. + **/ + CImg get_shared_channels(const unsigned int c0, const unsigned int c1) { + const ulongT + beg = (ulongT)offset(0,0,0,c0), + end = (ulongT)offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width - 1,_height - 1,_depth - 1,c0,c1); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); + } + + //! Return a shared-memory image referencing a range of channels of the image instance \const. + const CImg get_shared_channels(const unsigned int c0, const unsigned int c1) const { + const ulongT + beg = (ulongT)offset(0,0,0,c0), + end = (ulongT)offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width - 1,_height - 1,_depth - 1,c0,c1); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); + } + + //! Return a shared-memory image referencing one channel of the image instance. + /** + \param c0 C-coordinate. + **/ + CImg get_shared_channel(const unsigned int c0) { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory image referencing one channel of the image instance \const. + const CImg get_shared_channel(const unsigned int c0) const { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory version of the image instance. + CImg get_shared() { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Return a shared-memory version of the image instance \const. + const CImg get_shared() const { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Split image into a list along specified axis. + /** + \param axis Splitting axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param nb Number of split parts. + \note + - If \c nb==0, instance image is split into blocs of egal values along the specified axis. + - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide. + - If \c nb>0, instance image is split into \c nb blocs. + **/ + CImgList get_split(const char axis, const int nb=-1) const { + CImgList res; + if (is_empty()) return res; + const char _axis = cimg::lowercase(axis); + + if (nb<0) { // Split by bloc size + const unsigned int dp = (unsigned int)(nb?-nb:1); + switch (_axis) { + case 'x': { + if (_width>dp) { + res.assign(_width/dp + (_width%dp?1:0),1,1); + const unsigned int pe = _width - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _height*_depth*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'y': { + if (_height>dp) { + res.assign(_height/dp + (_height%dp?1:0),1,1); + const unsigned int pe = _height - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_depth*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'z': { + if (_depth>dp) { + res.assign(_depth/dp + (_depth%dp?1:0),1,1); + const unsigned int pe = _depth - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_height*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'c' : { + if (_spectrum>dp) { + res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1); + const unsigned int pe = _spectrum - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_height*_depth>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]); + get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } + } + } else if (nb>0) { // Split by number of (non-homogeneous) blocs + const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0; + if ((unsigned int)nb>siz) + throw CImgArgumentException(_cimg_instance + "get_split(): Instance cannot be split along %c-axis into %u blocs.", + cimg_instance, + axis,nb); + if (nb==1) res.assign(*this); + else { + int err = (int)siz; + unsigned int _p = 0; + switch (_axis) { + case 'x' : { + cimg_forX(*this,p) if ((err-=nb)<=0) { + get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'y' : { + cimg_forY(*this,p) if ((err-=nb)<=0) { + get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'z' : { + cimg_forZ(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'c' : { + cimg_forC(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } + } + } + } else { // Split by egal values according to specified axis + T current = *_data; + switch (_axis) { + case 'x' : { + int i0 = 0; + cimg_forX(*this,i) + if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); } + get_columns(i0,width() - 1).move_to(res); + } break; + case 'y' : { + int i0 = 0; + cimg_forY(*this,i) + if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); } + get_rows(i0,height() - 1).move_to(res); + } break; + case 'z' : { + int i0 = 0; + cimg_forZ(*this,i) + if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); } + get_slices(i0,depth() - 1).move_to(res); + } break; + case 'c' : { + int i0 = 0; + cimg_forC(*this,i) + if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); } + get_channels(i0,spectrum() - 1).move_to(res); + } break; + default : { + longT i0 = 0; + cimg_foroff(*this,i) + if ((*this)[i]!=current) { + CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = (longT)i; current = (*this)[i]; + } + CImg(_data + i0,1,(unsigned int)(size() - i0)).move_to(res); + } + } + } + return res; + } + + //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis. + /** + \param values Splitting value sequence. + \param axis Axis along which the splitting is performed. Can be '0' to ignore axis. + \param keep_values Tells if the splitting sequence must be kept in the split blocs. + **/ + template + CImgList get_split(const CImg& values, const char axis=0, const bool keep_values=true) const { + typedef _cimg_Tt Tt; + + CImgList res; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + if (!vsiz) return CImgList(*this); + if (vsiz==1) { // Split according to a single value + const T value = (T)*values; + switch (_axis) { + case 'x' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_width && (*this)(i)==value) ++i; + if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; } + while (i<_width && (*this)(i)!=value) ++i; + if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; } + } while (i<_width); + } break; + case 'y' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_height && (*this)(0,i)==value) ++i; + if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; } + while (i<_height && (*this)(0,i)!=value) ++i; + if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; } + } while (i<_height); + } break; + case 'z' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_depth && (*this)(0,0,i)==value) ++i; + if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; } + while (i<_depth && (*this)(0,0,i)!=value) ++i; + if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; } + } while (i<_depth); + } break; + case 'c' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_spectrum && (*this)(0,0,0,i)==value) ++i; + if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; } + while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i; + if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; } + } while (i<_spectrum); + } break; + default : { + const ulongT siz = size(); + ulongT i0 = 0, i = 0; + do { + while (ii0) { + if (keep_values) CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = i; + } + while (ii0) { CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } + } while (i=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_columns(i0,i1 - 1).move_to(res); + if (keep_values) get_columns(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_width); + if (i0<_width) get_columns(i0,width() - 1).move_to(res); + } break; + case 'y' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_rows(i0,i1 - 1).move_to(res); + if (keep_values) get_rows(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_height); + if (i0<_height) get_rows(i0,height() - 1).move_to(res); + } break; + case 'z' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_slices(i0,i1 - 1).move_to(res); + if (keep_values) get_slices(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_depth); + if (i0<_depth) get_slices(i0,depth() - 1).move_to(res); + } break; + case 'c' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,0,0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_channels(i0,i1 - 1).move_to(res); + if (keep_values) get_channels(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_spectrum); + if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res); + } break; + default : { + const ulongT siz = size(); + ulongT i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)[i]==(Tt)*values) { + i1 = i; j = 0; + while (i=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) CImg(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res); + if (keep_values) CImg(_data + i1,1,(unsigned int)(i - i1)).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i(_data + i0,1,(unsigned int)(siz - i0)).move_to(res); + } break; + } + } + return res; + } + + //! Append two images along specified axis. + /** + \param img Image to append with instance image. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Append alignment in \c [0,1]. + **/ + template + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,true).insert(img).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \specialization. + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,img,true).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \const. + template + CImg<_cimg_Tt> get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align); + } + + //! Append two images along specified axis \specialization. + CImg get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList(*this,img,true).get_append(axis,align); + } + + //@} + //--------------------------------------- + // + //! \name Filtering / Transforms + //@{ + //--------------------------------------- + + //! Correlate image by a kernel. + /** + \param kernel = the correlation kernel. + \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param is_normalized = enable local normalization. + \param channel mode Channel processing mode. Can be { 0=sum inputs | 1=one-for-one | 2=expand } + \param xcenter X-coordinate of the kernel center (~0U means 'centered'). + \param xstart Starting X-coordinate of the instance image. + \param xend Ending X-coordinate of the instance image. + \param xstride Stride along the X-axis. + \param xdilation Dilation along the X-axis. + \param ycenter Y-coordinate of the kernel center (~0U means 'centered'). + \param ystart Starting Y-coordinate of the instance image. + \param yend Ending Y-coordinate of the instance image. + \param ystride Stride along the Y-axis. + \param ydilation Dilation along the Y-axis. + \param zcenter Z-coordinate of the kernel center (~0U means 'centered'). + \param zstart Starting Z-coordinate of the instance image. + \param zend Ending Z-coordinate of the instance image. + \param zstride Stride along the Z-axis. + \param zdilation Dilation along the Z-axis. + \note + - The correlation of the image instance \p *this by the kernel \p kernel is defined to be: + res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j - + c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k). + **/ + template + CImg& correlate(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) { + if (is_empty() || !kernel) return *this; + return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + } + + template + CImg<_cimg_Ttfloat> get_correlate(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, + const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, + const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + } + + //! Correlate image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> _correlate(const CImg& kernel, const unsigned int boundary_conditions, + const bool is_normalized, const unsigned int channel_mode, + const unsigned int xcenter, const unsigned int ycenter, const unsigned int zcenter, + const unsigned int xstart, const unsigned int ystart, const unsigned zstart, + const unsigned int xend, const unsigned int yend, const unsigned int zend, + const float xstride, const float ystride, const float zstride, + const float xdilation, const float ydilation, const float zdilation, + const bool is_convolve) const { + if (is_empty() || !kernel) return *this; + typedef _cimg_Ttfloat Ttfloat; + CImg res; + _cimg_abort_init_openmp; + cimg_abort_init; + + if (xstart>xend || ystart>yend || zstart>zend) + throw CImgArgumentException(_cimg_instance + "%s(): Invalid xyz-start/end arguments (start = (%u,%u,%u), end = (%u,%u,%u)).", + cimg_instance, + is_convolve?"convolve":"correlate", + xstart,ystart,zstart,xend,yend,zend); + if (xstride<=0 || ystride<=0 || zstride<=0) + throw CImgArgumentException(_cimg_instance + "%s(): Invalid stride arguments (%g,%g,%g).", + cimg_instance, + is_convolve?"convolve":"correlate", + xstride,ystride,zstride); + const int + _xstart = (int)std::min(xstart,_width - 1), + _ystart = (int)std::min(ystart,_height - 1), + _zstart = (int)std::min(zstart,_depth - 1), + _xend = (int)std::min(xend,_width - 1), + _yend = (int)std::min(yend,_height - 1), + _zend = (int)std::min(zend,_depth - 1), + nwidth = 1 + (int)std::floor((_xend - _xstart)/xstride), + nheight = 1 + (int)std::floor((_yend - _ystart)/ystride), + ndepth = 1 + (int)std::floor((_zend + _zstart)/zstride), + _xstride = (int)cimg::round(xstride), + _ystride = (int)cimg::round(ystride), + _zstride = (int)cimg::round(zstride); + + const ulongT + res_whd = (ulongT)nwidth*nheight*ndepth, + res_size = res_whd*res._spectrum; + const bool + is_inner_parallel = res_whd>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + + int + _xcenter = xcenter==~0U?kernel.width()/2 - 1 + (kernel.width()%2):(int)std::min(xcenter,kernel._width - 1), + _ycenter = ycenter==~0U?kernel.height()/2 - 1 + (kernel.height()%2):(int)std::min(ycenter,kernel._height - 1), + _zcenter = zcenter==~0U?kernel.depth()/2 - 1 + (kernel.depth()%2):(int)std::min(zcenter,kernel._depth - 1), + _xdilation = (int)cimg::round(xdilation), + _ydilation = (int)cimg::round(ydilation), + _zdilation = (int)cimg::round(zdilation); + + const bool is_int_stride_dilation = + xstride==_xstride && ystride==_ystride && zstride==_zstride && + xdilation==_xdilation && ydilation==_ydilation && zdilation==_zdilation; + + CImg _kernel; + if (is_convolve) { // If convolution, go back to correlation + _kernel = CImg(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). + get_mirror('x').resize(kernel,-1); + _xcenter = kernel.width() - 1 - _xcenter; + _ycenter = kernel.height() - 1 - _ycenter; + _zcenter = kernel.depth() - 1 - _zcenter; + } else _kernel = kernel.get_shared(); + + if (_kernel._width==_kernel._height && _kernel._width>1 && _kernel._height>1 && + ((_kernel._depth==1 && _kernel._width<=5) || (_kernel._depth==_kernel._width && _kernel._width<=3)) && + boundary_conditions<=1 && channel_mode && + _xcenter==_kernel.width()/2 - 1 + (_kernel.width()%2) && + _ycenter==_kernel.height()/2 - 1 + (_kernel.height()%2) && + _zcenter==_kernel.depth()/2 - 1 + (_kernel.depth()%2) && + is_int_stride_dilation && _xdilation>=0 && _ydilation>=0 && _zdilation>=0) { + + // Optimized versions for centered 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernels. + const int dw = 1 - (_kernel.width()%2), dh = 1 - (_kernel.height()%2), dd = 1 - (_kernel.depth()%2); + if (dw || dh || dd) // Force kernel size to be odd + _kernel.get_resize(_kernel.width() + dw,_kernel.height() + dh,_kernel.depth() + dd,_kernel.spectrum(), + 0,0,1,1,1).move_to(_kernel.assign()); + + if (!boundary_conditions) { // Dirichlet -> Add a 1px zero border, then use _correlate() with Neumann + const int + dx = _kernel._width==1?0:1, + dy = _kernel._height==1?0:1, + dz = _kernel._depth==1?0:1; + return get_crop(-dx,-dy,-dz,width() - 1 + dx,height() - 1 + dy,depth() - 1 + dz). + _correlate(_kernel,true,is_normalized,channel_mode,_xcenter,_ycenter,_zcenter, + _xstart + dx,_ystart + dy,_zstart + dz,_xend + dx,_yend + dy,_zend + dz, + xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + + } else { // Neumann boundaries + res.assign(nwidth,nheight,ndepth,std::max(_spectrum,_kernel._spectrum)); + + switch (_kernel._depth) { + case 3 : { // 3x3x3 centered kernel + cimg_forC(res,c) { + cimg_abort_test; + const CImg I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + const int w1 = I.width() - 1, h1 = I.height() - 1, d1 = I.depth() - 1; + CImg _res = res.get_shared_channel(c); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,Z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, z = _zstart + _zstride*Z, + px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + const int w1 = I.width() - 1, h1 = I.height() - 1; + CImg _res = res.get_shared_channel(c); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, + px = x - _xdilation>0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, + nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, + ny = y + _ydilation0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, + nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, + ny = y + _ydilation I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg _res = res.get_shared_channel(c); + const int w1 = I.width() - 1, h1 = I.height() - 1; + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, + px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation res0 = res.get_shared_channel(0); + for (int c = 1; c K = _kernel.get_shared_channel(kc%_kernel._spectrum); + int w2 = 0, h2 = 0, d2 = 0; + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } + if (boundary_conditions>=3) { w2 = 2*width(); h2 = 2*height(); d2 = 2*depth(); } + res.fill(0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZC(res,x,y,z,c) { + Ttfloat _val, val = 0, N = 0; + + if (is_int_stride_dilation) + cimg_forXYZ(_kernel,p,q,r) { + const int + ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), + iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), + iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); + switch (boundary_conditions) { + case 0 : _val = atXYZ(ix,iy,iz,c,0); break; // Dirichlet + case 1 : _val = _atXYZ(ix,iy,iz,c); break; // Neumann + case 2 : _val = (*this)(cimg::mod(ix,width()),cimg::mod(iy,height()), // Periodic + cimg::mod(iz,depth()),c); break; + default : { // Mirror + const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); + _val = (*this)(mx I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(channel_mode==1?c%_kernel._spectrum:c/_spectrum); + int w2 = 0, h2 = 0, d2 = 0; + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } + if (boundary_conditions>=3) { w2 = 2*I.width(); h2 = 2*I.height(); d2 = 2*I.depth(); } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZ(res,x,y,z) { + Ttfloat _val, val = 0, N = 0; + + if (is_int_stride_dilation) + cimg_forXYZ(_kernel,p,q,r) { + const int + ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), + iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), + iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); + switch (boundary_conditions) { + case 0 : _val = I.atXYZ(ix,iy,iz,0,0); break; // Dirichlet + case 1 : _val = I._atXYZ(ix,iy,iz); break; // Neumann + case 2 : _val = I(cimg::mod(ix,I.width()),cimg::mod(iy,I.height()), // Periodic + cimg::mod(iz,I.depth())); break; + default : { // Mirror + const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); + _val = I(mx + CImg& convolve(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) { + if (is_empty() || !kernel) return *this; + return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + } + + //! Convolve image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> get_convolve(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, + const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, + const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation,true); + } + + //! Cumulate image values, optionally along specified axis. + /** + \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account. + **/ + CImg& cumulate(const char axis=0) { + switch (cimg::lowercase(axis)) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + Tlong cumul = (Tlong)0; + cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; } + } + break; + case 'y' : { + const ulongT w = (ulongT)_width; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && + _width*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) { + T *ptrd = data(x,0,z,c); + Tlong cumul = (Tlong)0; + cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; } + } + } break; + case 'z' : { + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && + _width*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) { + T *ptrd = data(x,y,0,c); + Tlong cumul = (Tlong)0; + cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; } + } + } break; + case 'c' : { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16)) + cimg_forXYZ(*this,x,y,z) { + T *ptrd = data(x,y,z,0); + Tlong cumul = (Tlong)0; + cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; } + } + } break; + default : { // Global cumulation + Tlong cumul = (Tlong)0; + cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; } + } + } + return *this; + } + + //! Cumulate image values, optionally along specified axis \newinstance. + CImg get_cumulate(const char axis=0) const { + return CImg(*this,false).cumulate(axis); + } + + //! Cumulate image values, along specified axes. + /** + \param axes Cumulation axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" + **/ + CImg& cumulate(const char *const axes) { + for (const char *s = axes; *s; ++s) cumulate(*s); + return *this; + } + + //! Cumulate image values, along specified axes \newinstance. + CImg get_cumulate(const char *const axes) const { + return CImg(*this,false).cumulate(axes); + } + + //! Erode image by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& erode(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) { + if (is_empty() || !kernel) return *this; + return get_erode(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Erode image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_erode(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) const { + if (is_empty() || !kernel) return *this; + if (!is_real && kernel==0) return CImg(width(),height(),depth(),spectrum(),0); + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + const int + mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, + mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1, + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_openmp; + cimg_abort_init; + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_openmp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real erosion + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); + if (cval::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + if (cval& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis + const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forYZC(*this,y,z,c) { + T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _height>1) { // Along Y-axis + const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXZC(*this,x,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _depth>1) { // Along Z-axis + const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXYC(*this,x,y,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).erode(sx,sy,sz); + } + + //! Erode the image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& erode(const unsigned int s) { + return erode(s,s,s); + } + + //! Erode the image by a square structuring element of specified size \newinstance. + CImg get_erode(const unsigned int s) const { + return (+*this).erode(s); + } + + //! Dilate image by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& dilate(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) { + if (is_empty() || !kernel) return *this; + return get_dilate(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Dilate image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_dilate(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) const { + if (is_empty() || !kernel || (!is_real && kernel==0)) return *this; + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + const int + mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2, + mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1, + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_openmp; + cimg_abort_init; + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_openmp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real dilation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } _cimg_abort_catch_openmp2 + if (boundary_conditions) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } else { // Binary dilation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } _cimg_abort_catch_openmp2 + if (boundary_conditions) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } + } _cimg_abort_catch_openmp + cimg_abort_test; + return res; + } + + //! Dilate image by a rectangular structuring element of specified size. + /** + \param sx Width of the structuring element. + \param sy Height of the structuring element. + \param sz Depth of the structuring element. + **/ + CImg& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis + const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forYZC(*this,y,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + + if (sy>1 && _height>1) { // Along Y-axis + const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXZC(*this,x,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + + if (sz>1 && _depth>1) { // Along Z-axis + const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXYC(*this,x,y,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + return *this; + } + + //! Dilate image by a rectangular structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).dilate(sx,sy,sz); + } + + //! Dilate image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& dilate(const unsigned int s) { + return dilate(s,s,s); + } + + //! Dilate image by a square structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int s) const { + return (+*this).dilate(s); + } + + //! Compute watershed transform. + /** + \param priority Priority map. + \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity + in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case. + \note Non-zero values of the instance instance are propagated to zero-valued ones according to + specified the priority map. + **/ + template + CImg& watershed(const CImg& priority, const bool is_high_connectivity=false) { +#define _cimg_watershed_init(cond,X,Y,Z) \ + if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds) + +#define _cimg_watershed_propagate(cond,X,Y,Z) \ + if (cond) { \ + if ((*this)(X,Y,Z)) { \ + ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \ + d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \ + if (d labels(_width,_height,_depth,1,0), seeds(64,3); + CImg::type> Q; + unsigned int sizeQ = 0; + int px, nx, py, ny, pz, nz; + bool is_px, is_nx, is_py, is_ny, is_pz, is_nz; + const bool is_3d = _depth>1; + + // Find seed points and insert them in priority queue. + unsigned int nb_seeds = 0; + const T *ptrs = _data; + cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version + if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0); + seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z; + px = x - 1; nx = x + 1; + py = y - 1; ny = y + 1; + pz = z - 1; nz = z + 1; + is_px = px>=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz::inf(); + T nlabel = (T)0; + _cimg_watershed_propagate(is_px,px,y,z); + _cimg_watershed_propagate(is_nx,nx,y,z); + _cimg_watershed_propagate(is_py,x,py,z); + _cimg_watershed_propagate(is_ny,x,ny,z); + if (is_3d) { + _cimg_watershed_propagate(is_pz,x,y,pz); + _cimg_watershed_propagate(is_nz,x,y,nz); + } + if (is_high_connectivity) { + _cimg_watershed_propagate(is_px && is_py,px,py,z); + _cimg_watershed_propagate(is_nx && is_py,nx,py,z); + _cimg_watershed_propagate(is_px && is_ny,px,ny,z); + _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z); + if (is_3d) { + _cimg_watershed_propagate(is_px && is_pz,px,y,pz); + _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz); + _cimg_watershed_propagate(is_px && is_nz,px,y,nz); + _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz); + _cimg_watershed_propagate(is_py && is_pz,x,py,pz); + _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz); + _cimg_watershed_propagate(is_py && is_nz,x,py,nz); + _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz); + _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz); + _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz); + _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz); + _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz); + _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz); + _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz); + _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz); + _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz); + } + } + (*this)(x,y,z) = nlabel; + labels(x,y,z) = ++nmin; + } + return *this; + } + + //! Compute watershed transform \newinstance. + template + CImg get_watershed(const CImg& priority, const bool is_high_connectivity=false) const { + return (+*this).watershed(priority,is_high_connectivity); + } + + // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. + template + bool _priority_queue_insert(CImg& is_queued, unsigned int& siz, const tv value, + const unsigned int x, const unsigned int y, const unsigned int z, + const unsigned int n=1) { + if (is_queued(x,y,z)) return false; + is_queued(x,y,z) = (tq)n; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz - 1,0) = (T)value; + (*this)(siz - 1,1) = (T)x; + (*this)(siz - 1,2) = (T)y; + (*this)(siz - 1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); + cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); + cimg::swap((*this)(pos,3),(*this)(par,3)); + } + return true; + } + + CImg& _priority_queue_remove(unsigned int& siz) { + (*this)(0,0) = (*this)(--siz,0); + (*this)(0,1) = (*this)(siz,1); + (*this)(0,2) = (*this)(siz,2); + (*this)(0,3) = (*this)(siz,3); + const float value = (*this)(0,0); + unsigned int pos = 0, swap = 0; + do { + const unsigned int left = 2*pos + 1, right = left + 1; + if (right(*this)(right,0)?left:right; + else if (left{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + **/ + CImg& deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) { +#define _cimg_deriche_apply \ + CImg Y(N); \ + double *ptrY = Y._data, yb = 0, yp = 0; \ + T xp = (T)0; \ + if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \ + for (int m = 0; m=0; --n) { \ + const T xc = *(ptrX-=off); \ + const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \ + xa = xn; xn = xc; ya = yn; yn = yc; \ + *ptrX = (T)(*(--ptrY)+yc); \ + } + const char naxis = cimg::lowercase(axis); + const double nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width: + naxis=='y'?_height: + naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.1f && !order)) return *this; + const double + nnsigma = nsigma<0.1f?0.1f:nsigma, + alpha = 1.695f/nnsigma, + ema = std::exp(-alpha), + ema2 = std::exp(-2*alpha), + b1 = -2*ema, + b2 = ema2; + double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; + switch (order) { + case 0 : { + const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2); + a0 = k; + a1 = k*(alpha - 1)*ema; + a2 = k*(alpha + 1)*ema; + a3 = -k*ema2; + } break; + case 1 : { + const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema); + a0 = a3 = 0; + a1 = k*ema; + a2 = -a1; + } break; + case 2 : { + const double + ea = std::exp(-alpha), + k = -(ema2 - 1)/(2*alpha*ema), + kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea)); + a0 = kn; + a1 = -kn*(1 + k*alpha)*ema; + a2 = kn*(1 - k*alpha)*ema; + a3 = -kn*ema2; + } break; + default : + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified filter order %u " + "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", + cimg_instance, + order); + } + coefp = (a0 + a1)/(1 + b1 + b2); + coefn = (a2 + a3)/(1 + b1 + b2); + switch (naxis) { + case 'x' : { + const int N = width(); + const ulongT off = 1U; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; } + } break; + case 'y' : { + const int N = height(); + const ulongT off = (ulongT)_width; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; } + } break; + case 'z' : { + const int N = depth(); + const ulongT off = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; } + } break; + default : { + const int N = spectrum(); + const ulongT off = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; } + } + } + return *this; + } + + //! Apply recursive Deriche filter \newinstance. + CImg get_deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).deriche(sigma,order,axis,boundary_conditions); + } + + // [internal] Apply a recursive filter (used by CImg::vanvliet()). + /* + \param ptr the pointer of the data + \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3]. + \param N size of the data + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005). + */ + static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off, + const unsigned int order, const bool boundary_conditions) { + double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] + const double + sumsq = filter[0], sum = sumsq * sumsq, + a1 = filter[1], a2 = filter[2], a3 = filter[3], + scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) ); + double M[9]; // Triggs matrix + M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2); + M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1); + M[2] = scaleM * a3 * (a1 + a3 * a2); + M[3] = scaleM * (a1 + a3 * a2); + M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1); + M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.); + M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2); + M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3); + M[8] = scaleM * a3 * (a1 + a3 * a2); + switch (order) { + case 0 : { + const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0); + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0); + } else { + // Apply Triggs boundary conditions + const double + uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3), + unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) val[k] = val[k - 1]; + } + if (!pass) data -= off; + } + } break; + case 1 : { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + } else { data-=off;} + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + case 2: { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + case 3: { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + } + } + + //! Van Vliet recursive Gaussian filter. + /** + \param sigma standard deviation of the Gaussian filter + \param order the order of the filter 0,1,2,3 + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note dirichlet boundary condition has a strange behavior + + I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering. + IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002. + + (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995) + + Boundary conditions (only for order 0) using Triggs matrix, from + B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet + recursive filtering. IEEE Trans. Signal Processing, + vol. 54, pp. 2365-2367, 2006. + **/ + CImg& vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) { + if (is_empty()) return *this; + if (!cimg::type::is_float()) + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this); + const char naxis = cimg::lowercase(axis); + const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.5f && !order)) return *this; + const double + nnsigma = nsigma<0.5f?0.5f:nsigma, + m0 = 1.16680, m1 = 1.10783, m2 = 1.40586, + m1sq = m1 * m1, m2sq = m2 * m2, + q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)), + qsq = q * q, + scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq), + b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale, + b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale, + b3 = -qsq * q / scale, + B = ( m0 * (m1sq + m2sq) ) / scale; + double filter[4]; + filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3; + switch (naxis) { + case 'x' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) + _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); + } break; + case 'y' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) + _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions); + } break; + case 'z' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) + _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height, + order,boundary_conditions); + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) + _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth, + order,boundary_conditions); + } + } + return *this; + } + + //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. + CImg get_vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions); + } + + //! Blur image. + /** + \param sigma_x Standard deviation of the blur, along the X-axis. + \param sigma_y Standard deviation of the blur, along the Y-axis. + \param sigma_z Standard deviation of the blur, along the Z-axis. + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel. + \note + - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian). + - This is a recursive algorithm, not depending on the values of the standard deviations. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=true) { + if (is_empty()) return *this; + if (is_gaussian) { + if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions); + if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions); + if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions); + } else { + if (_width>1) deriche(sigma_x,0,'x',boundary_conditions); + if (_height>1) deriche(sigma_y,0,'y',boundary_conditions); + if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions); + } + return *this; + } + + //! Blur image \newinstance. + CImg get_blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=true) const { + return CImg(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically. + /** + \param sigma Standard deviation of the blur. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) { + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically \newinstance. + CImg get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) const { + return CImg(*this,false).blur(sigma,boundary_conditions,is_gaussian); + } + + //! Blur image anisotropically, directed by a field of diffusion tensors. + /** + \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing. + \param amplitude Amplitude of the smoothing. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. + Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=1) { + + // Check arguments and init variables + if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6)) + throw CImgArgumentException(_cimg_instance + "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).", + cimg_instance, + G._width,G._height,G._depth,G._spectrum,G._data); + if (is_empty() || dl<0) return *this; + const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100; + unsigned int iamplitude = cimg::round(namplitude); + const bool is_3d = (G._spectrum==6); + T val_min, val_max = max_min(val_min); + _cimg_abort_init_openmp; + cimg_abort_init; + + if (da<=0) { // Iterated oriented Laplacians + CImg velocity(_width,_height,_depth,_spectrum); + for (unsigned int iteration = 0; iterationveloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + else // 2D version + cimg_forZC(*this,z,c) { + cimg_abort_test; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + if (veloc_max>0) *this+=(velocity*=dl/veloc_max); + } + } else { // LIC-based smoothing + const ulongT whd = (ulongT)_width*_height*_depth; + const float sqrt2amplitude = (float)std::sqrt(2*namplitude); + const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1; + CImg res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0); + int N = 0; + if (is_3d) { // 3D version + for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) { + const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), + da2 = datmp<1?360.f:datmp; + for (float theta = 0; theta<360; (theta+=da2),++N) { + const float + thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)*std::cos(phir)), + vy = (float)(std::sin(thetar)*std::cos(phir)), + vz = (float)std::sin(phir); + const t + *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2), + *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3); + cimg_forXYZ(G,xg,yg,zg) { + const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++); + const float + u = (float)(a*vx + b*vy + c*vz), + v = (float)(b*vx + d*vy + e*vz), + w = (float)(c*vx + e*vy + f*vz), + n = 1e-5f + cimg::hypot(u,v,w), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)(w*dln); + *(pd3++) = (Tfloat)n; + } + + cimg_abort_test; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2) + firstprivate(val)) + cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + cimg_forX(*this,x) { + val.fill(0); + const float + n = (float)W(x,y,z,3), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y, + Z = (float)z; + switch (interpolation_type) { + case 0 : { // Nearest neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)(X + 0.5f), + cy = (int)(Y + 0.5f), + cz = (int)(Z + 0.5f); + const float + u = (float)W(cx,cy,cz,0), + v = (float)W(cx,cy,cz,1), + w = (float)W(cx,cy,cz,2); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const float + u = (float)(W._linear_atXYZ(X,Y,Z,0)), + v = (float)(W._linear_atXYZ(X,Y,Z,1)), + w = (float)(W._linear_atXYZ(X,Y,Z,2)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + default : { // 2nd order Runge Kutta + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)), + v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)), + w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)), + u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)), + v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)), + w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + } + Tfloat *ptrd = res.data(x,y,z); + if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } + else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; } + } + } _cimg_abort_catch_openmp2 + } + } + } else { // 2D LIC algorithm + for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) { + const float thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); + const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2); + cimg_forXY(G,xg,yg) { + const t a = *(pa++), b = *(pb++), c = *(pc++); + const float + u = (float)(a*vx + b*vy), + v = (float)(b*vx + c*vy), + n = std::max(1e-5f,cimg::hypot(u,v)), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)n; + } + + cimg_abort_test; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2) + firstprivate(val)) + cimg_forY(*this,y) _cimg_abort_try_openmp2 { + cimg_abort_test2; + cimg_forX(*this,x) { + val.fill(0); + const float + n = (float)W(x,y,0,2), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y; + switch (interpolation_type) { + case 0 : { // Nearest-neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)(X + 0.5f), + cy = (int)(Y + 0.5f); + const float + u = (float)W(cx,cy,0,0), + v = (float)W(cx,cy,0,1); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u = (float)(W._linear_atXY(X,Y,0,0)), + v = (float)(W._linear_atXY(X,Y,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + default : { // 2nd-order Runge-kutta interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), + v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)), + u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)), + v = (float)(W._linear_atXY(X + u0,Y + v0,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } + } + Tfloat *ptrd = res.data(x,y); + if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } + else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; } + } + } _cimg_abort_catch_openmp2 + } + } + const Tfloat *ptrs = res._data; + cimg_for(*this,ptrd,T) { + const Tfloat _val = *(ptrs++)/N; + *ptrd = _valval_max?val_max:(T)_val); + } + } + cimg_abort_test; + return *this; + } + + //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance. + template + CImg get_blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return CImg(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way. + /** + \param amplitude Amplitude of the smoothing. + \param sharpness Sharpness. + \param anisotropy Anisotropy. + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. + Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) { + const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100; + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3), + amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way \newinstance. + CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30, const float gauss_prec=2, + const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return CImg(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec, + interpolation_type,is_fast_approx); + } + + //! Blur image, with the joint bilateral filter. + /** + \param guide Image used to model the smoothing weights. + \param sigma_x Amount of blur along the X-axis. + \param sigma_y Amount of blur along the Y-axis. + \param sigma_z Amount of blur along the Z-axis. + \param sigma_r Amount of blur along the value axis. + \param sampling_x Amount of downsampling along the X-axis used for the approximation. + Defaults (0) to sigma_x. + \param sampling_y Amount of downsampling along the Y-axis used for the approximation. + Defaults (0) to sigma_y. + \param sampling_z Amount of downsampling along the Z-axis used for the approximation. + Defaults (0) to sigma_z. + \param sampling_r Amount of downsampling along the value axis used for the approximation. + Defaults (0) to sigma_r. + \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 + (extended for 3D volumetric images). + It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m + **/ + template + CImg& blur_bilateral(const CImg& guide, + const float sigma_x, const float sigma_y, + const float sigma_z, const float sigma_r, + const float sampling_x, const float sampling_y, + const float sampling_z, const float sampling_r) { + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this; + T edge_min, edge_max = guide.max_min(edge_min); + if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z); + const float + edge_delta = (float)(edge_max - edge_min), + _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100, + _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100, + _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100, + _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100, + _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f), + _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f), + _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f), + _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256), + derived_sigma_x = _sigma_x / _sampling_x, + derived_sigma_y = _sigma_y / _sampling_y, + derived_sigma_z = _sigma_z / _sampling_z, + derived_sigma_r = _sigma_r / _sampling_r; + const int + padding_x = (int)(2*derived_sigma_x) + 1, + padding_y = (int)(2*derived_sigma_y) + 1, + padding_z = (int)(2*derived_sigma_z) + 1, + padding_r = (int)(2*derived_sigma_r) + 1; + const unsigned int + bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x), + by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y), + bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z), + br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r); + if (bx>0 || by>0 || bz>0 || br>0) { + const bool is_3d = (_depth>1); + if (is_3d) { // 3D version of the algorithm + CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); + cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); + bgrid.fill(0); bgridw.fill(0); + cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,c); + const float edge = (float)_guide(x,y,z); + const int + X = (int)cimg::round(x/_sampling_x) + padding_x, + Y = (int)cimg::round(y/_sampling_y) + padding_y, + Z = (int)cimg::round(z/_sampling_z) + padding_z, + R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; + bgrid(X,Y,Z,R)+=(float)val; + bgridw(X,Y,Z,R)+=1; + } + bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); + bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096)) + cimg_forXYZ(*this,x,y,z) { + const float edge = (float)_guide(x,y,z); + const float + X = x/_sampling_x + padding_x, + Y = y/_sampling_y + padding_y, + Z = z/_sampling_z + padding_z, + R = (edge - edge_min)/_sampling_r + padding_r; + const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R); + (*this)(x,y,z,c) = (T)(bval0/bval1); + } + } + } else { // 2D version of the algorithm + CImg bgrid(bx,by,br,2); + cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); + bgrid.fill(0); + cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,c); + const float edge = (float)_guide(x,y); + const int + X = (int)cimg::round(x/_sampling_x) + padding_x, + Y = (int)cimg::round(y/_sampling_y) + padding_y, + R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; + bgrid(X,Y,R,0)+=(float)val; + bgrid(X,Y,R,1)+=1; + } + bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096)) + cimg_forXY(*this,x,y) { + const float edge = (float)_guide(x,y); + const float + X = x/_sampling_x + padding_x, + Y = y/_sampling_y + padding_y, + R = (edge - edge_min)/_sampling_r + padding_r; + const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); + (*this)(x,y,c) = (T)(bval0/bval1); + } + } + } + } + return *this; + } + + //! Blur image, with the joint bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_x, const float sigma_y, + const float sigma_z, const float sigma_r, + const float sampling_x, const float sampling_y, + const float sampling_z, const float sampling_r) const { + return CImg(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r, + sampling_x,sampling_y,sampling_z,sampling_r); + } + + //! Blur image using the joint bilateral filter. + /** + \param guide Image used to model the smoothing weights. + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s. + \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r. + **/ + template + CImg& blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) { + const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; + return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r); + } + + //! Blur image using the bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) const { + return CImg(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r); + } + + // [internal] Apply a box filter (used by CImg::boxfilter() and CImg::blur_box()). + /* + \param ptr the pointer of the data + \param N size of the data + \param boxsize Size of the box filter (can be subpixel). + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + */ + static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off, + const int order, const bool boundary_conditions, + const unsigned int nb_iter) { + // Smooth. + if (boxsize>1 && nb_iter) { + const int w2 = (int)(boxsize - 1)/2; + const unsigned int winsize = 2*w2 + 1U; + const double frac = (boxsize - winsize)/2.; + CImg win(winsize); + for (unsigned int iter = 0; iter=N) return boundary_conditions?ptr[(N - 1)*off]:T(); + return ptr[x*off]; + } + + // Apply box filter of order 0,1,2. + /** + \param boxsize Size of the box window (can be subpixel) + \param order the order of the filter 0,1 or 2. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param nb_iter Number of filter iterations. + **/ + CImg& boxfilter(const float boxsize, const int order, const char axis='x', + const bool boundary_conditions=true, + const unsigned int nb_iter=1) { + if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this; + const char naxis = cimg::lowercase(axis); + const float nboxsize = boxsize>=0?boxsize:-boxsize* + (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + switch (naxis) { + case 'x' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) + _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter); + } break; + case 'y' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) + _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter); + } break; + case 'z' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) + _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter); + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) + _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth, + order,boundary_conditions,nb_iter); + } + } + return *this; + } + + // Apply box filter of order 0,1 or 2 \newinstance. + CImg get_boxfilter(const float boxsize, const int order, const char axis='x', + const bool boundary_conditions=true, + const unsigned int nb_iter=1) const { + return CImg(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter); + } + + //! Blur image with a box filter. + /** + \param boxsize_x Size of the box window, along the X-axis (can be subpixel). + \param boxsize_y Size of the box window, along the Y-axis (can be subpixel). + \param boxsize_z Size of the box window, along the Z-axis (can be subpixel). + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param nb_iter Number of filter iterations. + \note + - This is a recursive algorithm, not depending on the values of the box kernel size. + \see blur(). + **/ + CImg& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true, + const unsigned int nb_iter=1) { + if (is_empty()) return *this; + if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter); + if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter); + if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter); + return *this; + } + + //! Blur image with a box filter \newinstance. + CImg get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true) const { + return CImg(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions); + } + + //! Blur image with a box filter. + /** + \param boxsize Size of the box window (can be subpixel). + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \see deriche(), vanvliet(). + **/ + CImg& blur_box(const float boxsize, const bool boundary_conditions=true) { + const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100; + return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions); + } + + //! Blur image with a box filter \newinstance. + CImg get_blur_box(const float boxsize, const bool boundary_conditions=true) const { + return CImg(*this,false).blur_box(boxsize,boundary_conditions); + } + + //! Blur image, with the image guided filter. + /** + \param guide Image used to guide the smoothing process. + \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size. + \param regularization Regularization parameter. + If negative, it is expressed as a percentage of the guide value range. + \note This method implements the filtering algorithm described in: + He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence, + IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013 + **/ + template + CImg& blur_guided(const CImg& guide, const float radius, const float regularization) { + return get_blur_guided(guide,radius,regularization).move_to(*this); + } + + //! Blur image, with the image guided filter \newinstance. + template + CImg get_blur_guided(const CImg& guide, const float radius, const float regularization) const { + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || !radius) return *this; + const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100); + float _regularization = regularization; + if (regularization<0) { + T edge_min, edge_max = guide.max_min(edge_min); + if (edge_min==edge_max) return *this; + _regularization = -regularization*(edge_max - edge_min)/100; + } + _regularization = std::max(_regularization,0.01f); + const unsigned int psize = (unsigned int)(1 + 2*_radius); + CImg + mean_p = get_blur_box(psize,true), + mean_I = guide.get_blur_box(psize,true).resize(mean_p), + cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I), + var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(), + &a = cov_Ip.div(var_I+=_regularization), + &b = mean_p-=a.get_mul(mean_I); + a.blur_box(psize,true); + b.blur_box(psize,true); + return a.mul(guide)+=b; + } + + //! Blur image using patch-based space. + /** + \param guide Image used to model the smoothing weights. + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param patch_size Size of the patches. + \param lookup_size Size of the window to search similar patches. + \param smoothness Smoothness for the patch comparison. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_patch(const CImg& guide, + const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + if (is_empty() || !patch_size || !lookup_size) return *this; + return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this); + } + + //! Blur image using patch-based space \newinstance. + template + CImg get_blur_patch(const CImg& guide, + const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, + const bool is_fast_approx=true) const { + +#define _cimg_blur_patch3d_fast(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ + firstprivate(P,Q)) \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N##x##N(res,x,y,z); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + tfloat sum_weights = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \ + if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))3?0:1; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \ + } \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch3d(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ + firstprivate(P,Q)) \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N##x##N(res,x,y,z); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + tfloat sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \ + tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \ + tfloat distance2 = 0; \ + pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \ + alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch2d_fast(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ + firstprivate(P,Q)) \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N(res,x,y); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + tfloat sum_weights = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \ + if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))3?0:1; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \ + } \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch2d(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ + firstprivate(P,Q)) \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N(res,x,y); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + tfloat sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \ + tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \ + tfloat distance2 = 0; \ + pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \ + alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } _cimg_abort_catch_openmp } + + typedef _cimg_tfloat tfloat; + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || !patch_size || !lookup_size) return +*this; + Tfloat val_min, val_max = (Tfloat)max_min(val_min); + _cimg_abort_init_openmp; + cimg_abort_init; + + CImg res(_width,_height,_depth,_spectrum,0); + const CImg + __guide = guide?CImg(guide,guide.pixel_type()==cimg::type::string()): + CImg(*this,pixel_type()==cimg::type::string()), + _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared(); + CImg P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P); + + t guide_min = (t)0, guide_max = (t)0; + if (sigma_r<0) guide_max = guide.max_min(guide_min); + const float + guide_delta = (float)(guide_max - guide_min), + _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100, + _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100, + sigma_s2 = _sigma_s*_sigma_s, + sigma_r2 = _sigma_r*_sigma_r, + sigma_r3 = 3*_sigma_r, + Pnorm = P.size()*sigma_r2; + const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1; + const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size; + cimg::unused(N2,N3); + if (_depth>1) switch (patch_size) { // 3D + case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break; + default : { + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) + firstprivate(P,Q)) + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Fast + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + tfloat sum_weights = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) + if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))3?0:1; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); + } + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } _cimg_abort_catch_openmp + } else { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) + firstprivate(P,Q)) + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Exact + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + tfloat sum_weights = 0, weight_max = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { + (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P; + const tfloat + dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r, + distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), + weight = std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } _cimg_abort_catch_openmp + } + } + } else switch (patch_size) { // 2D + case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break; + case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break; + case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break; + case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break; + case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break; + case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break; + case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break; + default : { // Fast + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) { + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) + firstprivate(P,Q)) + cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Fast + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + tfloat sum_weights = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) + if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))3?0:1; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); + } + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } _cimg_abort_catch_openmp + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) + firstprivate(P,Q)) + cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Exact + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + tfloat sum_weights = 0, weight_max = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { + (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P; + const tfloat + dx = (tfloat)x - p, dy = (tfloat)y - q, + distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), + weight = std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } _cimg_abort_catch_openmp + } + } + } + return res.cut(val_min,val_max); + } + + //! Blur image using patch-based space \simplification. + CImg& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx); + } + + //! Blur image using patch-based space \simplification \newinstance. + CImg get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, + const bool is_fast_approx=true) const { + return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx); + } + + //! Blur image with the median filter. + /** + \param n Size of the median filter. + \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation. + **/ + CImg& blur_median(const unsigned int n, const float threshold=0) { + if (!n) return *this; + return get_blur_median(n,threshold).move_to(*this); + } + + //! Blur image with the median filter \newinstance. + CImg get_blur_median(const unsigned int n, const float threshold=0) const { + if (is_empty() || n<=1) return +*this; + CImg res(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg::unused(ptrd); + const int hr = (int)n/2, hl = n - hr - 1; + if (res._depth!=1) { // 3D + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(*this,x,y,z,c) { // With threshold + const int + x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; + const Tfloat val0 = (Tfloat)(*this)(x,y,z,c); + CImg values(n*n*n); + unsigned int nb_values = 0; + T *_ptrd = values.data(); + cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r) + if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; } + res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(*this,x,y,z,c) { // Without threshold + const int + x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; + res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); + } + } else { + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { // With threshold + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + const Tfloat val0 = (Tfloat)(*this)(x,y,c); + CImg values(n*n); + unsigned int nb_values = 0; + T *_ptrd = values.data(); + cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) + if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; } + res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c); + } + else { + const int + w1 = width() - 1, h1 = height() - 1, + w2 = width() - 2, h2 = height() - 2, + w3 = width() - 3, h3 = height() - 3, + w4 = width() - 4, h4 = height() - 4; + switch (n) { // Without threshold + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(9); + cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]); + cimg_for_borderXY(*this,x,y,1) + res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c, + std::min(w1,x + 1),std::min(h1,y + 1),0,c).median(); + } + } break; + case 5 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(25); + cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4], + I[5],I[6],I[7],I[8],I[9], + I[10],I[11],I[12],I[13],I[14], + I[15],I[16],I[17],I[18],I[19], + I[20],I[21],I[22],I[23],I[24]); + cimg_for_borderXY(*this,x,y,2) + res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c, + std::min(w1,x + 2),std::min(h1,y + 2),0,c).median(); + } + } break; + case 7 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(49); + cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6], + I[7],I[8],I[9],I[10],I[11],I[12],I[13], + I[14],I[15],I[16],I[17],I[18],I[19],I[20], + I[21],I[22],I[23],I[24],I[25],I[26],I[27], + I[28],I[29],I[30],I[31],I[32],I[33],I[34], + I[35],I[36],I[37],I[38],I[39],I[40],I[41], + I[42],I[43],I[44],I[45],I[46],I[47],I[48]); + cimg_for_borderXY(*this,x,y,3) + res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c, + std::min(w1,x + 3),std::min(h1,y + 3),0,c).median(); + } + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); + } + } + } + } + } + return res; + } + + //! Sharpen image. + /** + \param amplitude Sharpening amplitude + \param sharpen_type Select sharpening method. Can be { false=inverse diffusion | true=shock filters }. + \param edge Edge threshold (shock filters only). + \param alpha Gradient smoothness (shock filters only). + \param sigma Tensor smoothness (shock filters only). + **/ + CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, + const float alpha=0, const float sigma=0) { + if (is_empty()) return *this; + T val_min, val_max = max_min(val_min); + const float nedge = edge/2; + CImg velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum); + + if (_depth>1) { // 3D + if (sharpen_type) { // Shock filters + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && + _height*_depth>=16)) + cimg_forYZ(G,y,z) { + Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1), + *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3); + CImg val, vec; + cimg_forX(G,x) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + if (val[2]<0) val[2] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = vec(0,2); + *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + u = G(x,y,z,0), + v = G(x,y,z,1), + w = G(x,y,z,2), + amp = G(x,y,z,3), + ixx = Incc + Ipcc - 2*Iccc, + ixy = (Innc + Ippc - Inpc - Ipnc)/4, + ixz = (Incn + Ipcp - Incp - Ipcn)/4, + iyy = Icnc + Icpc - 2*Iccc, + iyz = (Icnn + Icpp - Icnp - Icpn)/4, + izz = Iccn + Iccp - 2*Iccc, + ixf = Incc - Iccc, + ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, + iyb = Iccc - Icpc, + izf = Iccn - Iccc, + izb = Iccc - Iccp, + itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb), + veloc = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else // Inverse diffusion + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else { // 2D + if (sharpen_type) { // Shock filters + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && + _height>=(cimg_openmp_sizefactor)*16)) + cimg_forY(G,y) { + CImg val, vec; + Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2); + cimg_forX(G,x) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + u = G(x,y,0), + v = G(x,y,1), + amp = G(x,y,2), + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + ixf = Inc - Icc, + ixb = Icc - Ipc, + iyf = Icn - Icc, + iyb = Icc - Icp, + itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb), + veloc = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else // Inverse diffusion + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } + const Tfloat veloc_max = _veloc_max.max(); + if (veloc_max<=0) return *this; + return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this); + } + + //! Sharpen image \newinstance. + CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, + const float alpha=0, const float sigma=0) const { + return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); + } + + //! Return image gradient. + /** + \param axes Axes considered for the gradient computation, as a C-string (e.g "xy"). + \param scheme = Numerical scheme used for the gradient computation: + - -1 = Backward finite differences + - 0 = Centered finite differences (default) + - 1 = Forward finite differences + - 2 = Using Sobel kernels + - 3 = Using rotation invariant kernels + - 4 = Using Deriche recursive filter. + - 5 = Using Van Vliet recursive filter. + **/ + CImgList get_gradient(const char *const axes=0, const int scheme=0) const { + CImgList res; + char __axes[4] = { 0 }; + const char *_axes = axes?axes:__axes; + if (!axes) { + unsigned int k = 0; + if (_width>1) __axes[k++] = 'x'; + if (_height>1) __axes[k++] = 'y'; + if (_depth>1) __axes[k++] = 'z'; + } + + CImg grad; + while (*_axes) { + const char axis = cimg::lowercase(*(_axes++)); + if (axis!='x' && axis!='y' && axis!='z') + throw CImgArgumentException(_cimg_instance + "get_gradient(): Invalid specified axes '%s'.", + cimg_instance, + axes); + const longT off = axis=='x'?1:axis=='y'?_width:_width*_height; + if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) { + grad.assign(_width,_height,_depth,_spectrum,0).move_to(res); + continue; + } + + const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme; + switch (_scheme) { + case -1 : { // Backward finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z)) + grad[pos] = 0; + else + grad[pos] = (Tfloat)_data[pos] - _data[pos - off]; + } + grad.move_to(res); + } break; + case 1 : { // Forward finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1)) + grad[pos] = 0; + else + grad[pos] = (Tfloat)_data[pos + off] - _data[pos]; + } + grad.move_to(res); + } break; + case 2 : { // Sobel scheme + grad.assign(_width,_height,_depth,_spectrum); + if (axis=='x') // X-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn; + } + else // Y-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn; + } + grad.move_to(res); + } break; + case 3 : { // Rotation invariant scheme + const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1)); + grad.assign(_width,_height,_depth,_spectrum); + if (axis=='x') // X-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; + } + else // Y-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; + } + grad.move_to(res); + } break; + case 4 : // Deriche filter + get_deriche(0,1,axis).move_to(res); + break; + case 5 : // Van Vliet filter + get_vanvliet(0,1,axis).move_to(res); + break; + default : { // Central finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z)) + grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2; + else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1)) + grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2; + else + grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2; + } + grad.move_to(res); + } break; + } + } + return res; + } + + //! Return image hessian. + /** + \param axes Axes considered for the hessian computation, as a C-string (e.g "xy"). + **/ + CImgList get_hessian(const char *const axes=0) const { + CImgList res; + char __axes[12] = { 0 }; + const char *_axes = axes?axes:__axes; + if (!axes) { + unsigned int k = 0; + if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; } + if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; } + if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; } + if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; } + if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; } + if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; } + } + const unsigned int len = (unsigned int)std::strlen(_axes); + if (len%2) + throw CImgArgumentException(_cimg_instance + "get_hessian(): Invalid specified axes '%s'.", + cimg_instance, + axes); + CImg hess; + for (unsigned int k = 0; k=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4; + } + else if (axis1=='x' && axis2=='z') // Ixz + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4; + } + else // Iyz + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4; + } + hess.move_to(res); + } + return res; + } + + //! Compute image Laplacian. + CImg& laplacian() { + return get_laplacian().move_to(*this); + } + + //! Compute image Laplacian \newinstance. + CImg get_laplacian() const { + if (is_empty()) return CImg(); + CImg res(_width,_height,_depth,_spectrum); + if (_depth>1) { // 3D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; + } + } else if (_height>1) { // 2D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; + } + } else { // 1D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 && + _height*_depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc; + } + } + return res; + } + + //! Compute the structure tensor field of an image. + /** + \param is_fwbw_scheme scheme. Can be { false=centered | true=forward-backward } + **/ + CImg& structure_tensors(const bool is_fwbw_scheme=false) { + return get_structure_tensors(is_fwbw_scheme).move_to(*this); + } + + //! Compute the structure tensor field of an image \newinstance. + CImg get_structure_tensors(const bool is_fwbw_scheme=false) const { + if (is_empty()) return *this; + CImg res; + if (_depth>1) { // 3D + res.assign(_width,_height,_depth,6,0); + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ix = (Incc - Ipcc)/2, + iy = (Icnc - Icpc)/2, + iz = (Iccn - Iccp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=ix*iz; + *(ptrd3++)+=iy*iy; + *(ptrd4++)+=iy*iz; + *(ptrd5++)+=iz*iz; + } + } + } else { // Forward/backward finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixf = Incc - Iccc, ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, iyb = Iccc - Icpc, + izf = Iccn - Iccc, izb = Iccc - Iccp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; + *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; + *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; + *(ptrd5++)+=(izf*izf + izb*izb)/2; + } + } + } + } else { // 2D + res.assign(_width,_height,_depth,3,0); + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ix = (Inc - Ipc)/2, + iy = (Icn - Icp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=iy*iy; + } + } + } else { // Forward/backward finite differences (version 2) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ixf = Inc - Icc, ixb = Icc - Ipc, + iyf = Icn - Icc, iyb = Icc - Icp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; + } + } + } + } + return res; + } + + //! Compute field of diffusion tensors for edge-preserving smoothing. + /** + \param sharpness Sharpness + \param anisotropy Anisotropy + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param is_sqrt Tells if the square root of the tensor field is computed instead. + **/ + CImg& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) { + CImg res; + const float + nsharpness = std::max(sharpness,1e-5f), + power1 = (is_sqrt?0.5f:1)*nsharpness, + power2 = power1/(1e-7f + 1 - anisotropy); + blur(alpha).normalize(0,(T)255); + + if (_depth>1) { // 3D + get_structure_tensors().move_to(res).blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth>=(cimg_openmp_sizefactor)*256)) + cimg_forYZ(*this,y,z) { + Tfloat + *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2), + *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5); + CImg val(3), vec(3,3); + cimg_forX(*this,x) { + res.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + const float + _l1 = val[2], _l2 = val[1], _l3 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0, + ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), + vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), + wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), + n1 = (float)std::pow(1 + l1 + l2 + l3,-power1), + n2 = (float)std::pow(1 + l1 + l2 + l3,-power2); + *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx; + *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy; + *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz; + *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy; + *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz; + *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz; + } + } + } else { // for 2D images + get_structure_tensors().move_to(res).blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height>=(cimg_openmp_sizefactor)*256)) + cimg_forY(*this,y) { + Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2); + CImg val(2), vec(2,2); + cimg_forX(*this,x) { + res.get_tensor_at(x,y).symmetric_eigen(val,vec); + const float + _l1 = val[1], _l2 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, + ux = vec(1,0), uy = vec(1,1), + vx = vec(0,0), vy = vec(0,1), + n1 = (float)std::pow(1 + l1 + l2,-power1), + n2 = (float)std::pow(1 + l1 + l2,-power2); + *(ptrd0++) = n1*ux*ux + n2*vx*vx; + *(ptrd1++) = n1*ux*uy + n2*vx*vy; + *(ptrd2++) = n1*uy*uy + n2*vy*vy; + } + } + } + return res.move_to(*this); + } + + //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance. + CImg get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const { + return CImg(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); + } + + //! Estimate displacement field between two images. + /** + \param source Reference image. + \param smoothness Smoothness of estimated displacement field. + \param precision Precision required for algorithm convergence. + \param nb_scales Number of scales used to estimate the displacement field. + \param iteration_max Maximum number of iterations allowed for one scale. + \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)). + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). + **/ + CImg& displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). + move_to(*this); + } + + //! Estimate displacement field between two images \newinstance. + CImg get_displacement(const CImg& source, + const float smoothness=0.1f, const float precision=5.f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) const { + if (is_empty() || !source) return +*this; + if (!is_sameXYZC(source)) + throw CImgArgumentException(_cimg_instance + "displacement(): Instance and source image (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + source._width,source._height,source._depth,source._spectrum,source._data); + if (precision<0) + throw CImgArgumentException(_cimg_instance + "displacement(): Invalid specified precision %g " + "(should be >=0)", + cimg_instance, + precision); + + const bool is_3d = source._depth>1; + const unsigned int constraint = is_3d?3:2; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum0?nb_scales: + (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1); + + const float _precision = (float)std::pow(10.,-(double)precision); + float sm, sM = source.max_min(sm), tm, tM = max_min(tm); + const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm); + + CImg U, V; + floatT bound = 0; + for (int scale = (int)_nb_scales - 1; scale>=0; --scale) { + const float factor = (float)std::pow(1.5,(double)scale); + const unsigned int + _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1, + _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1, + _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1; + if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // Skip too small scales + const CImg + I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta, + I2 = (get_resize(I1,2)-=tm)/=tdelta; + if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V); + if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); + else { + if (guide) + guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U); + else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); + } + + float dt = 2, energy = cimg::type::max(); + const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); + cimg_abort_init; + + for (unsigned int iteration = 0; iteration=0) // Isotropic regularization + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } + if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints + U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor; + U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor; + U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor; + } + } else { // Anisotropic regularization + const float nsmoothness = -smoothness; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints + U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor; + U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor; + U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor; + } + } + } + } else { // 2D version + if (smoothness>=0) // Isotropic regularization + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; + if (U(x,y,1)>y) U(x,y,1) = (float)y; + bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; + bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; + } else { + if (U(x,y,0)<-x) U(x,y,0) = -(float)x; + if (U(x,y,1)<-y) U(x,y,1) = -(float)y; + bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; + bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } + if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints + U(_x,_y,0) = V(_x,_y,0)/factor; + U(_x,_y,1) = V(_x,_y,1)/factor; + } + } else { // Anisotropic regularization + const float nsmoothness = -smoothness; + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; + if (U(x,y,1)>y) U(x,y,1) = (float)y; + bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; + bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; + } else { + if (U(x,y,0)<-x) U(x,y,0) = -(float)x; + if (U(x,y,1)<-y) U(x,y,1) = -(float)y; + bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; + bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints + U(_x,_y,0) = V(_x,_y,0)/factor; + U(_x,_y,1) = V(_x,_y,1)/factor; + } + } + } + } + const float d_energy = (_energy - energy)/(sw*sh*sd); + if (d_energy<=0 && -d_energy<_precision) break; + if (d_energy>0) dt*=0.5f; + energy = _energy; + } + } + return U; + } + + //! Compute correspondence map between two images, using a patch-matching algorithm. + /** + \param patch_image The image containing the reference patches to match with the instance image. + \param patch_width Width of the patch used for matching. + \param patch_height Height of the patch used for matching. + \param patch_depth Depth of the patch used for matching. + \param nb_iterations Number of patch-match iterations. + \param nb_randoms Number of randomization attempts (per pixel). + \param patch_penalization Penalization factor in score related patch occurrences. + if negative, also tells that identity result is not avoided. + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). + \param[out] matching_score Returned as the image of matching scores. + **/ + template + CImg& matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + CImg &matching_score) { + return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. + template + CImg get_matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + CImg &matching_score) const { + return _matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization, + guide,true,matching_score); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg& matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5, + const float patch_penalization=0, + const CImg &guide=CImg::const_empty()) { + return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg get_matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5, + const float patch_penalization=0, + const CImg &guide=CImg::const_empty()) const { + CImg matching_score; + return _matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score); + } + + template + CImg _matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + const bool is_matching_score, + CImg &matching_score) const { + if (is_empty()) return CImg::const_empty(); + if (patch_image._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) " + "have different spectrums.", + cimg_instance, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + if (patch_width>_width || patch_height>_height || patch_depth>_depth) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the instance image.", + cimg_instance,patch_width,patch_height,patch_depth); + if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the patch image image (%u,%u,%u,%u,%p).", + cimg_instance,patch_width,patch_height,patch_depth, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + const unsigned int + _constraint = patch_image._depth>1?3:2, + constraint = guide._spectrum>_constraint?_constraint:0; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint)) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " + "considering instance and patch image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + + CImg a_map(_width,_height,_depth,patch_image._depth>1?3:2); + CImg is_updated(_width,_height,_depth,1,3); + CImg score(_width,_height,_depth); + CImg occ; + const float _patch_penalization = cimg::abs(patch_penalization); + const bool allow_identity = patch_penalization>=0; + if (_patch_penalization!=0) occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0); + const int + psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1, + psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1, + psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1; + + // Interleave image buffers to speed up patch comparison (cache-friendly). + CImg in_this = get_permute_axes("cxyz"); + in_this._width = _width*_spectrum; + in_this._height = _height; + in_this._depth = _depth; + in_this._spectrum = 1; + CImg in_patch = patch_image.get_permute_axes("cxyz"); + in_patch._width = patch_image._width*patch_image._spectrum; + in_patch._height = patch_image._height; + in_patch._depth = patch_image._depth; + in_patch._spectrum = 1; + + if (_depth>1 || patch_image._depth>1) { // 3D version + + // Initialize correspondence map. + if (guide) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64)) + cimg_forXYZ(*this,x,y,z) { // User-defined initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for cimg_openmp_collapse(2)) + cimg_forXYZ(*this,x,y,z) { // Random initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } + cimg::srand(rng); + } + + // Start iteration loop. + cimg_abort_init; + for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && + iter0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor + u = a_map(x - 1,y,z,0); + v = a_map(x - 1,y,z,1); + w = a_map(x - 1,y,z,2); + if (u>=cx1 - 1 && u=cy1 && v=cz1 && w0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor + u = a_map(x,y - 1,z,0); + v = a_map(x,y - 1,z,1); + w = a_map(x,y - 1,z,2); + if (u>=cx1 && u=cy1 - 1 && v=cz1 && w0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor + u = a_map(x,y,z - 1,0); + v = a_map(x,y,z - 1,1); + w = a_map(x,y,z - 1,2); + if (u>=cx1 && u=cy1 && v=cz1 - 1 && w=cx1 + 1 && u=cy1 && v=cz1 && w=cx1 && u=cy1 + 1 && v=cz1 && w=cx1 && u=cy1 && v=cz1 + 1 && w::inf()); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_forXY(*this,x,y) { // Random initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } + cimg::srand(rng); + } + + // Start iteration loop. + cimg_abort_init; + for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && + iter0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor + u = a_map(x - 1,y,0); + v = a_map(x - 1,y,1); + if (u>=cx1 - 1 && u=cy1 && v0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor + u = a_map(x,y - 1,0); + v = a_map(x,y - 1,1); + if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v& img1, const CImg& img2, const CImg& occ, + const unsigned int psizew, const unsigned int psizeh, + const unsigned int psized, const unsigned int psizec, + const int x1, const int y1, const int z1, + const int x2, const int y2, const int z2, + const int xc, const int yc, const int zc, + const float patch_penalization, + const bool allow_identity, + const float max_score) { // 3D version + if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2,(float)z1-z2)::inf(); + const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2); + const unsigned int psizewc = psizew*psizec; + const ulongT + offx1 = (ulongT)img1._width - psizewc, + offx2 = (ulongT)img2._width - psizewc, + offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width, + offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width; + float ssd = 0; + for (unsigned int k = 0; kmax_score) return max_score; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + + patch_penalization*psizewc*psizeh*psized*occ(xc,yc,zc)/100); + } + + static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& occ, + const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec, + const int x1, const int y1, + const int x2, const int y2, + const int xc, const int yc, + const float patch_penalization, + const bool allow_identity, + const float max_score) { // 2D version + if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2)::inf(); + const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2); + const unsigned int psizewc = psizew*psizec; + const ulongT + offx1 = (ulongT)img1._width - psizewc, + offx2 = (ulongT)img2._width - psizewc; + float ssd = 0; + for (unsigned int j = 0; jmax_score) return max_score; + p1+=offx1; p2+=offx2; + } + return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + + patch_penalization*psizewc*psizeh*occ(xc,yc)/100); + } + + //! Compute Euclidean distance function to a specified value. + /** + \param value Reference value. + \param metric Type of metric. Can be { 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }. + \note + The distance transform implementation has been submitted by A. Meijster, and implements + the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink, + "A general algorithm for computing distance transforms in linear time.", + In: Mathematical Morphology and its Applications to Image and Signal Processing, + J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.' + The submitted code has then been modified to fit CImg coding style and constraints. + **/ + CImg& distance(const T& value, const unsigned int metric=2) { + if (is_empty()) return *this; + if (cimg::type::string()!=pixel_type()) // For datatype < int + return CImg(*this,false).distance((Tint)value,metric). + cut((Tint)cimg::type::min(),(Tint)cimg::type::max()).move_to(*this); + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning) + if (!is_value) return fill(cimg::type::max()); + switch (metric) { + case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev + case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan + case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean + default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean + } + return *this; + } + + //! Compute distance to a specified value \newinstance. + CImg get_distance(const T& value, const unsigned int metric=2) const { + return CImg(*this,false).distance((Tfloat)value,metric); + } + + static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) { + return (u*u - i*i + g[u] - g[i])/(2*(u - i)); + } + + static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) { + return (x - i)*(x - i) + g[i]; + } + + static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) { + return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2); + } + + static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) { + return (x=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; } + if (q<0) { q = 0; s[0] = u; } + else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }} + } + for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan + } + + CImg& _distance_core(longT (*const sep)(const longT, const longT, const longT *const), + longT (*const f)(const longT, const longT, const longT *const)) { + // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why. +#define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9) + + const ulongT wh = (ulongT)_width*_height; +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) +#endif + cimg_forC(*this,c) { + CImg g(_width), dt(_width), s(_width), t(_width); + CImg img = get_shared_channel(c); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forYZ(*this,y,z) { // Over X-direction + cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh); + _distance_scan(_width,g,sep,f,s,t,dt); + cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x]; + } + if (_height>1) { + g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forXZ(*this,x,z) { // Over Y-direction + cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh); + _distance_scan(_height,g,sep,f,s,t,dt); + cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y]; + } + } + if (_depth>1) { + g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forXY(*this,x,y) { // Over Z-direction + cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh); + _distance_scan(_depth,g,sep,f,s,t,dt); + cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z]; + } + } + } + return *this; + } + + //! Compute chamfer distance to a specified value, with a custom metric. + /** + \param value Reference value. + \param metric_mask Metric mask. + \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé. + **/ + template + CImg& distance(const T& value, const CImg& metric_mask) { + if (is_empty()) return *this; + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; + if (!is_value) return fill(cimg::type::max()); + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg img = get_shared_channel(c); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024)) + cimg_forXYZ(metric_mask,dx,dy,dz) { + const t weight = metric_mask(dx,dy,dz); + if (weight) { + for (int z = dz, nz = 0; z=0; --z,--nz) { // Backward scan + for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) { + for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) { + const T dd = img(nx,ny,nz,0,wh) + weight; + if (dd + CImg get_distance(const T& value, const CImg& metric_mask) const { + return CImg(*this,false).distance(value,metric_mask); + } + + //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm). + /** + \param value Reference value. + \param metric Field of distance potentials. + \param is_high_connectivity Tells if the algorithm uses low or high connectivity. + \param[out] return_path An image containing the nodes of the minimal path. + **/ + template + CImg& distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) { + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); + } + + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance. + template + CImg::type> + get_distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) const { + if (is_empty()) return return_path.assign(); + if (!is_sameXYZ(metric)) + throw CImgArgumentException(_cimg_instance + "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) " + "have incompatible dimensions.", + cimg_instance, + metric._width,metric._height,metric._depth,metric._spectrum); + typedef typename cimg::superset::type td; // Type used for computing cumulative distances + CImg result(_width,_height,_depth,_spectrum), Q; + CImg is_queued(_width,_height,_depth,1); + if (return_path) return_path.assign(_width,_height,_depth,_spectrum); + + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + CImg path = return_path?return_path.get_shared_channel(c):CImg(); + unsigned int sizeQ = 0; + + // Detect initial seeds. + is_queued.fill(0); + cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) { + Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z); + res(x,y,z) = 0; + if (path) path(x,y,z) = (to)0; + } + + // Start distance propagation. + while (sizeQ) { + + // Get and remove point with minimal potential from the queue. + const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); + const td P = (td)-Q(0,0); + Q._priority_queue_remove(sizeQ); + + // Update neighbors. + td npot = 0; + if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) { + res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2; + } + if (x + 1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) { + res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8; + } + if (y + 1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) { + res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32; + } + if (z + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) { + res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) { + res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9; + } + if (x - 1>=0 && y + 1=0) { // Diagonal neighbors on slice z - 1 + if (x - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) { + res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) { + res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40; + } + if (y + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)), + x - 1,y - 1,z - 1)) { + res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)), + x + 1,y - 1,z - 1)) { + res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41; + } + if (x - 1>=0 && y + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) { + res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) { + res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24; + } + if (y + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)), + x - 1,y - 1,z + 1)) { + res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)), + x + 1,y - 1,z + 1)) { + res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25; + } + if (x - 1>=0 && y + 1 + CImg& distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) { + return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this); + } + + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. + template + CImg get_distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) const { + CImg return_path; + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path); + } + + //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). + /** + \param value Reference value. + \param metric Field of distance potentials. + **/ + template + CImg& distance_eikonal(const T& value, const CImg& metric) { + return get_distance_eikonal(value,metric).move_to(*this); + } + + //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). + template + CImg get_distance_eikonal(const T& value, const CImg& metric) const { + if (is_empty()) return *this; + if (!is_sameXYZ(metric)) + throw CImgArgumentException(_cimg_instance + "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have " + "incompatible dimensions.", + cimg_instance, + metric._width,metric._height,metric._depth,metric._spectrum); + CImg result(_width,_height,_depth,_spectrum,cimg::type::max()), Q; + CImg state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen + + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state)) + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + unsigned int sizeQ = 0; + state.fill(-1); + + // Detect initial seeds. + Tfloat *ptr1 = res._data; char *ptr2 = state._data; + cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; } + + // Initialize seeds neighbors. + ptr2 = state._data; + cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) { + if (x - 1>=0 && state(x - 1,y,z)==-1) { + const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z); + } + if (x + 1=0 && state(x,y - 1,z)==-1) { + const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z); + } + if (y + 1=0 && state(x,y,z - 1)==-1) { + const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1); + } + if (z + 1=0) { + if (x - 1>=0 && state(x - 1,y,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); + if (dist=0 && state(x,y - 1,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + if (dist=0 && state(x,y,z - 1)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); + if (dist& res, const Tfloat P, + const int x=0, const int y=0, const int z=0) const { + const Tfloat M = (Tfloat)cimg::type::max(); + T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 11) { // 3D + T + T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1=0?res(x,y,z - 1):M,z + 1T2) cimg::swap(T1,T2); + if (T2>T3) cimg::swap(T2,T3); + if (T1>T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T31) { // 2D + T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T2 + void _eik_priority_queue_insert(CImg& state, unsigned int& siz, const t value, + const unsigned int x, const unsigned int y, const unsigned int z) { + if (state(x,y,z)>0) return; + state(x,y,z) = 0; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3)); + } + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE. + /** + \param nb_iterations Number of PDE iterations. + \param band_size Size of the narrow band. + \param time_step Time step of the PDE iterations. + **/ + CImg& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { + if (is_empty()) return *this; + CImg velocity(*this,false); + for (unsigned int iteration = 0; iteration1) { // 3D + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)0?(Incc - Iccc):(Iccc - Ipcc), + iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc), + iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp), + ng = 1e-5f + cimg::hypot(gx,gy,gz), + ngx = gx/ng, + ngy = gy/ng, + ngz = gz/ng, + veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } else { // 2D version + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)0?(Inc - Icc):(Icc - Ipc), + iy = gy*sgn>0?(Icn - Icc):(Icc - Icp), + ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)), + ngx = gx/ng, + ngy = gy/ng, + veloc = sgn*(ngx*ix + ngy*iy - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } + if (veloc_max>0) *this+=(velocity*=time_step/veloc_max); + } + return *this; + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance. + CImg get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, + const float time_step=0.5f) const { + return CImg(*this,false).distance_eikonal(nb_iterations,band_size,time_step); + } + + //! Compute Haar multiscale wavelet transform. + /** + \param axis Axis considered for the transform. + \param invert Set inverse of direct transform. + \param nb_scales Number of scales used for the transform. + **/ + CImg& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(axis,invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { + if (is_empty() || !nb_scales) return +*this; + CImg res; + const Tfloat sqrt2 = std::sqrt(2.f); + if (nb_scales==1) { + switch (cimg::lowercase(axis)) { // Single scale transform + case 'x' : { + const unsigned int w = _width/2; + if (w) { + if ((w%2) && w!=1) + throw CImgInstanceException(_cimg_instance + "haar(): Sub-image width %u is not even.", + cimg_instance, + w); + + res.assign(_width,_height,_depth,_spectrum); + if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X + for (unsigned int x = 0, xw = w, x2 = 0; x& haar(const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const bool invert=false, const unsigned int nb_scales=1) const { + CImg res; + if (nb_scales==1) { // Single scale transform + if (_width>1) get_haar('x',invert,1).move_to(res); + if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); } + if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); } + if (res) return res; + } else { // Multi-scale transform + if (invert) { // Inverse transform + res.assign(*this,false); + if (_width>1) { + if (_height>1) { + if (_depth>1) { + unsigned int w = _width, h = _height, d = _depth; + for (unsigned int s = 1; w && h && d && s1) { + unsigned int w = _width, d = _depth; + for (unsigned int s = 1; w && d && s1) { + if (_depth>1) { + unsigned int h = _height, d = _depth; + for (unsigned int s = 1; h && d && s1) { + unsigned int d = _depth; + for (unsigned int s = 1; d && s1) { + if (_height>1) { + if (_depth>1) + for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s1) { + if (_depth>1) + for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s1) for (unsigned int s = 1, d = _depth/2; d && s get_FFT(const char axis, const bool is_inverse=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],axis,is_inverse); + return res; + } + + //! Compute n-D Fast Fourier Transform. + /* + \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + CImgList get_FFT(const bool is_inverse=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],is_inverse); + return res; + } + + //! Compute 1D Fast Fourier Transform, along a specified axis. + /** + \param[in,out] real Real part of the pixel values. + \param[in,out] imag Imaginary part of the pixel values. + \param axis Axis along which the FFT is computed. + \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + static void FFT(CImg& real, CImg& imag, const char axis, const bool is_inverse=false, + const unsigned int nb_threads=0) { + if (!real) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.", + pixel_type()); + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " + "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + const char _axis = cimg::lowercase(axis); + if (_axis!='x' && _axis!='y' && _axis!='z') + throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts " + "(%u,%u,%u,%u) " + "(should be { x | y | z }).", + pixel_type(),axis, + real._width,real._height,real._depth,real._spectrum); + cimg::unused(nb_threads); +#ifdef cimg_use_fftw3 + cimg::mutex(12); +#ifndef cimg_use_fftw3_singlethread + fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus()); +#endif + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) + throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " + "for computing FFT of image (%u,%u,%u,%u) along the X-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth), + real._width,real._height,real._depth,real._spectrum); + double *const ptrf = (double*)data_in; + fftw_plan data_plan = + _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(), + data_in,0,1,real.width(), + data_in,0,1,real.width(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(), + data_in,0,1,real.height(), + data_in,0,1,real.height(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(), + data_in,0,1,real.depth(), + data_in,0,1,real.depth(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + CImg realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c); + switch (_axis) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + break; + case 'y' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + break; + default : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + } + + fftw_execute(data_plan); + + const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0; + switch (_axis) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + break; + case 'y' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + break; + default : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + } + } + + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#ifndef cimg_use_fftw3_singlethread + fftw_cleanup_threads(); +#endif + cimg::mutex(12,0); +#else + switch (_axis) { + case 'x' : { // Fourier along X, using built-in functions + const unsigned int N = real._width, N2 = N>>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the X-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forYZC(real,y,z,c) { + cimg::swap(real(i,y,z,c),real(j,y,z,c)); + cimg::swap(imag(i,y,z,c),imag(j,y,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = delta>>1; + for (unsigned int i = 0; i>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the Y-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXZC(real,x,z,c) { + cimg::swap(real(x,i,z,c),real(x,j,z,c)); + cimg::swap(imag(x,i,z,c),imag(x,j,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the Z-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXYC(real,x,y,c) { + cimg::swap(real(x,y,i,c),real(x,y,j,c)); + cimg::swap(imag(x,y,i,c),imag(x,y,j,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i& real, CImg& imag, const bool is_inverse=false, + const unsigned int nb_threads=0) { + if (!real) + throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.", + pixel_type()); + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " + "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + cimg::unused(nb_threads); +#ifdef cimg_use_fftw3 + cimg::mutex(12); +#ifndef cimg_use_fftw3_singlethread + fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus()); +#endif + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) + throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " + "for computing FFT of image (%u,%u,%u,%u).", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width* + real._height*real._depth*real._spectrum), + real._width,real._height,real._depth,real._spectrum); + double *const ptrf = (double*)data_in; + fftw_plan data_plan = + real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + fftw_plan_dft_1d(real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + CImg realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; } + fftw_execute(data_plan); + if (is_inverse) { + const double a = 1.0/(real.width()*real.height()*real.depth()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); } + } else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; } + } + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#ifndef cimg_use_fftw3_singlethread + fftw_cleanup_threads(); +#endif + cimg::mutex(12,0); +#else + if (real._depth>1) FFT(real,imag,'z',is_inverse); + if (real._height>1) FFT(real,imag,'y',is_inverse); + if (real._width>1) FFT(real,imag,'x',is_inverse); +#endif + } + + //@} + //------------------------------------- + // + //! \name 3D Objects Management + //@{ + //------------------------------------- + + //! Rotate 3D object's vertices. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or second quaternion coordinate. + \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). + **/ + CImg& rotate_object3d(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) { + return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this); + } + + CImg get_rotate_object3d(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) const { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "rotate_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + return CImg::rotation_matrix(x,y,z,w,is_quaternion)**this; + } + + //! Shift 3D object's vertices. + /** + \param tx X-coordinate of the 3D displacement vector. + \param ty Y-coordinate of the 3D displacement vector. + \param tz Z-coordinate of the 3D displacement vector. + **/ + CImg& shift_object3d(const float tx, const float ty=0, const float tz=0) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz; + return *this; + } + + //! Shift 3D object's vertices \newinstance. + CImg get_shift_object3d(const float tx, const float ty=0, const float tz=0) const { + return CImg(*this,false).shift_object3d(tx,ty,tz); + } + + //! Shift 3D object's vertices, so that it becomes centered. + /** + \note The object center is computed as its barycenter. + **/ + CImg& shift_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; + return *this; + } + + //! Shift 3D object's vertices, so that it becomes centered \newinstance. + CImg get_shift_object3d() const { + return CImg(*this,false).shift_object3d(); + } + + //! Resize 3D object. + /** + \param sx Width of the 3D object's bounding box. + \param sy Height of the 3D object's bounding box. + \param sz Depth of the 3D object's bounding box. + **/ + CImg& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + if (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } + if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } + if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } + return *this; + } + + //! Resize 3D object \newinstance. + CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { + return CImg(*this,false).resize_object3d(sx,sy,sz); + } + + //! Resize 3D object to unit size. + CImg resize_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz); + if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; } + return *this; + } + + //! Resize 3D object to unit size \newinstance. + CImg get_resize_object3d() const { + return CImg(*this,false).resize_object3d(); + } + + //! Merge two 3D objects together. + /** + \param[in,out] primitives Primitives data of the current 3D object. + \param obj_vertices Vertices data of the additional 3D object. + \param obj_primitives Primitives data of the additional 3D object. + **/ + template + CImg& append_object3d(CImgList& primitives, const CImg& obj_vertices, + const CImgList& obj_primitives) { + if (!obj_vertices || !obj_primitives) return *this; + if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a " + "set of 3D vertices.", + cimg_instance, + obj_vertices._width,obj_vertices._height, + obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data); + + if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); } + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + const unsigned int P = _width; + append(obj_vertices,'x'); + const unsigned int N = primitives._width; + primitives.insert(obj_primitives); + for (unsigned int i = N; i &p = primitives[i]; + switch (p.size()) { + case 1 : p[0]+=P; break; // Point + case 5 : p[0]+=P; p[1]+=P; break; // Sphere + case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment + case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle + case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle + } + } + return *this; + } + + //! Texturize primitives of a 3D object. + /** + \param[in,out] primitives Primitives data of the 3D object. + \param[in,out] colors Colors data of the 3D object. + \param texture Texture image to map to 3D object. + \param coords Texture-mapping coordinates. + **/ + template + const CImg& texturize_object3d(CImgList& primitives, CImgList& colors, + const CImg& texture, const CImg& coords=CImg::const_empty()) const { + if (is_empty()) return *this; + if (_height!=3) + throw CImgInstanceException(_cimg_instance + "texturize_object3d(): image instance is not a set of 3D points.", + cimg_instance); + if (coords && (coords._width!=_width || coords._height!=2)) + throw CImgArgumentException(_cimg_instance + "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).", + cimg_instance, + coords._width,coords._height,coords._depth,coords._spectrum,coords._data); + CImg _coords; + if (!coords) { // If no texture coordinates specified, do a default XY-projection + _coords.assign(_width,2); + float + xmin, xmax = (float)get_shared_row(0).max_min(xmin), + ymin, ymax = (float)get_shared_row(1).max_min(ymin), + dx = xmax>xmin?xmax-xmin:1, + dy = ymax>ymin?ymax-ymin:1; + cimg_forX(*this,p) { + _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx); + _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy); + } + } else _coords = coords; + + int texture_ind = -1; + cimglist_for(primitives,l) { + CImg &p = primitives[l]; + const unsigned int siz = p.size(); + switch (siz) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)p[0]; + const int x0 = _coords(i0,0), y0 = _coords(i0,1); + texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, + y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]); + } break; + case 2 : case 6 : { // Line + const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,x0,y0,x1,y1).move_to(p); + } break; + case 3 : case 9 : { // Triangle + const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1), + x2 = _coords(i2,0), y2 = _coords(i2,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p); + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1), + x2 = _coords(i2,0), y2 = _coords(i2,1), + x3 = _coords(i3,0), y3 = _coords(i3,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p); + } break; + } + } + return *this; + } + + //! Generate a 3D elevation of the image instance. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param[out] colors The returned list of the 3D object colors. + \param elevation The input elevation map. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + CImgList colors3d; + const CImg points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2); + CImg().display_object3d("Elevation3d",points3d,faces3d,colors3d); + \endcode + \image html ref_elevation3d.jpg + **/ + template + CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& elevation) const { + if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1) + throw CImgArgumentException(_cimg_instance + "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + elevation._width,elevation._height,elevation._depth, + elevation._spectrum,elevation._data); + if (is_empty()) return *this; + float m, M = (float)max_min(m); + if (M==m) ++M; + colors.assign(); + const unsigned int size_x1 = _width - 1, size_y1 = _height - 1; + for (unsigned int y = 0; y1?((*this)(x,y,1) - m)*255/(M-m):r), + b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r); + CImg::vector((tc)r,(tc)g,(tc)b).move_to(colors); + } + const typename CImg::_functor2d_int func(elevation); + return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height); + } + + //! Generate the 3D projection planes of the image instance. + /** + \param[out] primitives Primitives data of the returned 3D object. + \param[out] colors Colors data of the returned 3D object. + \param x0 X-coordinate of the projection point. + \param y0 Y-coordinate of the projection point. + \param z0 Z-coordinate of the projection point. + \param normalize_colors Tells if the created textures have normalized colors. + **/ + template + CImg get_projections3d(CImgList& primitives, CImgList& colors, + const unsigned int x0, const unsigned int y0, const unsigned int z0, + const bool normalize_colors=false) const { + float m = 0, M = 0, delta = 1; + if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); } + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + CImg img_xy, img_xz, img_yz; + if (normalize_colors) { + ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy); + ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1). + move_to(img_xz); + ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1). + move_to(img_yz); + } else { + get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy); + get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz); + get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz); + } + CImg points(12,3,1,1, + 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0, + 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0, + _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1); + primitives.assign(); + CImg::vector(0,1,2,3,0,0,img_xy._width - 1,0,img_xy._width - 1,img_xy._height - 1,0,img_xy._height - 1). + move_to(primitives); + CImg::vector(4,5,6,7,0,0,img_xz._width - 1,0,img_xz._width - 1,img_xz._height - 1,0,img_xz._height - 1). + move_to(primitives); + CImg::vector(8,9,10,11,0,0,img_yz._width - 1,0,img_yz._width - 1,img_yz._height - 1,0,img_yz._height - 1). + move_to(primitives); + colors.assign(); + img_xy.move_to(colors); + img_xz.move_to(colors); + img_yz.move_to(colors); + return points; + } + + //! Generate a isoline of the image instance as a 3D object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3D object colors. + \param size_x The number of subdivisions along the X-axis. + \param size_y The number of subdisivions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + const CImg points3d = img.get_isoline3d(faces3d,100); + CImg().display_object3d("Isoline3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isoline3d.jpg + **/ + template + CImg get_isoline3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a scalar image.", + cimg_instance); + if (_depth>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a 2D image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) { + const _functor2d_int func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height()); + } else { + const _functor2d_float func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y); + } + return vertices; + } + + //! Compute isolines of a function, as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Elevation functor. Must have operator()(x,y) defined. + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static CImg isoline3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + CImgList vertices; + primitives.assign(); + typename CImg::_functor_isoline3d add_vertex(vertices); + typename CImg::_functor_isoline3d add_segment(primitives); + isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y); + return vertices>'x'; + } + + //! Compute isolines of a function, as a 3D object. + /** + \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex. + \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x, const int size_y) { + static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, + 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, + { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, + { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, + { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; + const unsigned int + _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nxm1 = nx - 1, + nym1 = ny - 1; + + if (!nxm1 || !nym1) return; + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1; + CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); + CImg values1(nx), values2(nx); + float X = x0, Y = y0, nX = X + dx, nY = Y + dy; + int nb_vertices = 0; + + // Fill first line with values + cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; } + + // Run the marching squares algorithm + for (unsigned int yi = 0, nyi = 1; yi + static CImg isoline3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y); + } + + template + static int _isoline3d_index(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int nx) { + switch (edge) { + case 0 : return (int)indices1(x,0); + case 1 : return (int)indices1(nx,1); + case 2 : return (int)indices2(x,0); + case 3 : return (int)indices1(x,1); + } + return 0; + } + + //! Generate an isosurface of the image instance as a 3D object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3D object colors. + \param size_x Number of subdivisions along the X-axis. + \param size_y Number of subdisivions along the Y-axis. + \param size_z Number of subdisivions along the Z-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img = CImg("reference.jpg").resize(-100,-100,20); + CImgList faces3d; + const CImg points3d = img.get_isosurface3d(faces3d,100); + CImg().display_object3d("Isosurface3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isosurface3d.jpg + **/ + template + CImg get_isosurface3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100, const int size_z=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isosurface3d(): Instance is not a scalar image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) { + const _functor3d_int func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, + width(),height(),depth()); + } else { + const _functor3d_float func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, + size_x,size_y,size_z); + } + return vertices; + } + + //! Compute isosurface of a function, as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static CImg isosurface3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x=32, const int size_y=32, const int size_z=32) { + CImgList vertices; + primitives.assign(); + typename CImg::_functor_isosurface3d add_vertex(vertices); + typename CImg::_functor_isosurface3d add_triangle(primitives); + isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z); + return vertices>'x'; + } + + //! Compute isosurface of a function, as a 3D object. + /** + \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex. + \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x, const int size_y, const int size_z) { + static const unsigned int edges[256] = { + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 + }; + + static const int triangles[256][16] = { + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, + { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, + { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, + { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, + { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, + { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, + { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, + { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, + { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, + { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, + { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, + { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, + { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, + { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, + { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, + { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, + { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, + { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, + { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, + { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, + { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, + { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, + { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, + { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, + { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, + { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, + { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, + { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, + { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, + { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, + { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, + { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, + { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, + { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, + { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, + { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, + { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, + { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, + { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, + { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, + { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, + { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, + { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, + { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, + { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, + { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, + { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } + }; + + const unsigned int + _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nz = _nz?_nz:1, + nxm1 = nx - 1, + nym1 = ny - 1, + nzm1 = nz - 1; + if (!nxm1 || !nym1 || !nzm1) return; + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1; + CImg indices1(nx,ny,1,3,-1), indices2(indices1); + CImg values1(nx,ny), values2(nx,ny); + float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; + int nb_vertices = 0; + + // Fill the first plane with function values + Y = y0; + cimg_forY(values1,y) { + X = x0; + cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; } + Y+=dy; + } + + // Run Marching Cubes algorithm + Z = z0; nZ = Z + dz; + for (unsigned int zi = 0; zi + static CImg isosurface3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int dx=32, const int dy=32, const int dz=32) { + const _functor3d_expr func(expression); + return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz); + } + + template + static int _isosurface3d_index(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int y, + const unsigned int nx, const unsigned int ny) { + switch (edge) { + case 0 : return indices1(x,y,0); + case 1 : return indices1(nx,y,1); + case 2 : return indices1(x,ny,0); + case 3 : return indices1(x,y,1); + case 4 : return indices2(x,y,0); + case 5 : return indices2(nx,y,1); + case 6 : return indices2(x,ny,0); + case 7 : return indices2(x,y,1); + case 8 : return indices1(x,y,2); + case 9 : return indices1(nx,y,2); + case 10 : return indices1(nx,ny,2); + case 11 : return indices1(x,ny,2); + } + return 0; + } + + // Define functors for accessing image values (used in previous functions). + struct _functor2d_int { + const CImg& ref; + _functor2d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref((int)x,(int)y); + } + }; + + struct _functor2d_float { + const CImg& ref; + _functor2d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref._linear_atXY(x,y); + } + }; + + struct _functor2d_expr { + _cimg_math_parser *mp; + ~_functor2d_expr() { mp->end(); delete mp; } + _functor2d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } + float operator()(const float x, const float y) const { + return (float)(*mp)(x,y,0,0); + } + }; + + struct _functor3d_int { + const CImg& ref; + _functor3d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref((int)x,(int)y,(int)z); + } + }; + + struct _functor3d_float { + const CImg& ref; + _functor3d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref._linear_atXYZ(x,y,z); + } + }; + + struct _functor3d_expr { + _cimg_math_parser *mp; + ~_functor3d_expr() { mp->end(); delete mp; } + _functor3d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } + float operator()(const float x, const float y, const float z) const { + return (float)(*mp)(x,y,z,0); + } + }; + + struct _functor4d_int { + const CImg& ref; + _functor4d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref((int)x,(int)y,(int)z,c); + } + }; + + struct _functor_isoline3d { + CImgList& list; + _functor_isoline3d(CImgList& _list):list(_list) {} + template + void operator()(const t x, const t y, const t z) { CImg::vector((T)x,(T)y,(T)z).move_to(list); } + template + void operator()(const t i, const t j) { CImg::vector((T)i,(T)j).move_to(list); } + }; + + struct _functor_isosurface3d { + CImgList& list; + _functor_isosurface3d(CImgList& _list):list(_list) {} + template + void operator()(const t x, const t y, const t z) { CImg::vector((T)x,(T)y,(T)z).move_to(list); } + }; + + //! Compute 3D elevation of a function as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + **/ + template + static CImg elevation3d(CImgList& primitives, const tfunc& func, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const float + nx0 = x0=0?size_x:(nx1-nx0)*-size_x/100), + nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1, + _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), + nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1; + if (nsize_x<2 || nsize_y<2) + throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).", + pixel_type(), + nsize_x,nsize_y); + + CImg vertices(nsize_x*nsize_y,3); + floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2); + for (unsigned int y = 0; y + static CImg elevation3d(CImgList& primitives, const char *const expression, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y); + } + + //! Generate a 3D box object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the box (dimension along the X-axis). + \param size_y The height of the box (dimension along the Y-axis). + \param size_z The depth of the box (dimension along the Z-axis). + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::box3d(faces3d,10,20,30); + CImg().display_object3d("Box3d",points3d,faces3d); + \endcode + \image html ref_box3d.jpg + **/ + template + static CImg box3d(CImgList& primitives, + const float size_x=200, const float size_y=100, const float size_z=100) { + primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); + return CImg(8,3,1,1, + 0.,size_x,size_x, 0., 0.,size_x,size_x, 0., + 0., 0.,size_y,size_y, 0., 0.,size_y,size_y, + 0., 0., 0., 0.,size_z,size_z,size_z,size_z); + } + + //! Generate a 3D cone. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cone basis. + \param size_z The cone's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cone3d(faces3d,50); + CImg().display_object3d("Cone3d",points3d,faces3d); + \endcode + \image html ref_cone3d.jpg + **/ + template + static CImg cone3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,size_z, + 0.,0.,0.); + for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices); + } + const unsigned int nbr = vertices._width - 2; + for (unsigned int p = 0; p::vector(1,next,curr).move_to(primitives); + CImg::vector(0,curr,next).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D cylinder. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cylinder basis. + \param size_z The cylinder's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cylinder3d(faces3d,50); + CImg().display_object3d("Cylinder3d",points3d,faces3d); + \endcode + \image html ref_cylinder3d.jpg + **/ + template + static CImg cylinder3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,0., + 0.,0.,size_z); + for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices); + } + const unsigned int nbr = (vertices._width - 2)/2; + for (unsigned int p = 0; p::vector(0,next,curr).move_to(primitives); + CImg::vector(1,curr + 1,next + 1).move_to(primitives); + CImg::vector(curr,next,next + 1,curr + 1).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D torus. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius1 The large radius. + \param radius2 The small radius. + \param subdivisions1 The number of angular subdivisions for the large radius. + \param subdivisions2 The number of angular subdivisions for the small radius. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::torus3d(faces3d,20,4); + CImg().display_object3d("Torus3d",points3d,faces3d); + \endcode + \image html ref_torus3d.jpg + **/ + template + static CImg torus3d(CImgList& primitives, + const float radius1=100, const float radius2=30, + const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { + primitives.assign(); + if (!subdivisions1 || !subdivisions2) return CImg(); + CImgList vertices; + for (unsigned int v = 0; v::vector(x,y,z).move_to(vertices); + } + } + for (unsigned int vv = 0; vv::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives); + } + } + return vertices>'x'; + } + + //! Generate a 3D XY-plane. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the plane (dimension along the X-axis). + \param size_y The height of the plane (dimensions along the Y-axis). + \param subdivisions_x The number of planar subdivisions along the X-axis. + \param subdivisions_y The number of planar subdivisions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::plane3d(faces3d,100,50); + CImg().display_object3d("Plane3d",points3d,faces3d); + \endcode + \image html ref_plane3d.jpg + **/ + template + static CImg plane3d(CImgList& primitives, + const float size_x=100, const float size_y=100, + const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) { + primitives.assign(); + if (!subdivisions_x || !subdivisions_y) return CImg(); + CImgList vertices; + const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1; + const float fx = (float)size_x/w, fy = (float)size_y/h; + for (unsigned int y = 0; y::vector(fx*x,fy*y,0).move_to(vertices); + for (unsigned int y = 0; y::vector(off1,off4,off3,off2).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D sphere. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the sphere (dimension along the X-axis). + \param subdivisions The number of recursive subdivisions from an initial icosahedron. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::sphere3d(faces3d,100,4); + CImg().display_object3d("Sphere3d",points3d,faces3d); + \endcode + \image html ref_sphere3d.jpg + **/ + template + static CImg sphere3d(CImgList& primitives, + const float radius=50, const unsigned int subdivisions=3) { + + // Create initial icosahedron + primitives.assign(); + const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a; + CImgList vertices(12,1,3,1,1, b,a,0., -b,a,0., -b,-a,0., b,-a,0., a,0.,b, a,0.,-b, + -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a); + primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6, + 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3, + 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2); + // edge - length/2 + float he = (float)a; + + // Recurse subdivisions + for (unsigned int i = 0; i::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; } + if (i1<0) { CImg::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; } + if (i2<0) { CImg::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; } + primitives.remove(0); + CImg::vector(p0,i0,i1).move_to(primitives); + CImg::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives); + CImg::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives); + CImg::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives); + } + } + return (vertices>'x')*=radius; + } + + //! Generate a 3D ellipsoid. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param tensor The tensor which gives the shape and size of the ellipsoid. + \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg tensor = CImg::diagonal(10,7,3), + points3d = CImg::ellipsoid3d(faces3d,tensor,4); + CImg().display_object3d("Ellipsoid3d",points3d,faces3d); + \endcode + \image html ref_ellipsoid3d.jpg + **/ + template + static CImg ellipsoid3d(CImgList& primitives, + const CImg& tensor, const unsigned int subdivisions=3) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImg S, V; + tensor.symmetric_eigen(S,V); + const float orient = + (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) + + (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) + + (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2); + if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); } + const float l0 = S[0], l1 = S[1], l2 = S[2]; + CImg vertices = sphere3d(primitives,1.,subdivisions); + vertices.get_shared_row(0)*=l0; + vertices.get_shared_row(1)*=l1; + vertices.get_shared_row(2)*=l2; + return V*vertices; + } + + //! Convert 3D object into a CImg3d representation. + /** + \param primitives Primitives data of the 3D object. + \param colors Colors data of the 3D object. + \param opacities Opacities data of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + **/ + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + CImg& object3dtoCImg3d(const bool full_check=true) { + return get_object3dtoCImg3d(full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \newinstance. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true) const { + CImg error_message(1024); + if (!is_object3d(primitives,colors,opacities,full_check,error_message)) + throw CImgInstanceException(_cimg_instance + "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,_width,primitives._width,error_message.data()); + CImg res(1,_size_object3dtoCImg3d(primitives,colors,opacities)); + float *ptrd = res._data; + + // Put magick number. + *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f; + *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f; + + // Put number of vertices and primitives. + *(ptrd++) = cimg::uint2float(_width); + *(ptrd++) = cimg::uint2float(primitives._width); + + // Put vertex data. + if (is_empty() || !primitives) return res; + const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2); + cimg_forX(*this,p) { + *(ptrd++) = (float)*(ptrx++); + *(ptrd++) = (float)*(ptry++); + *(ptrd++) = (float)*(ptrz++); + } + + // Put primitive data. + cimglist_for(primitives,p) { + *(ptrd++) = (float)primitives[p].size(); + const tp *ptrp = primitives[p]._data; + cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++)); + } + + // Put color/texture data. + const unsigned int csiz = std::min(colors._width,primitives._width); + for (int c = 0; c<(int)csiz; ++c) { + const CImg& color = colors[c]; + const tc *ptrc = color._data; + if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; } + else { + *(ptrd++) = -128.f; + int shared_ind = -1; + if (color.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImgList& opacities, float *ptrd) const { + cimglist_for(opacities,o) { + const CImg& opacity = opacities[o]; + const to *ptro = opacity._data; + if (opacity.size()==1) *(ptrd++) = (float)*ptro; + else { + *(ptrd++) = -128.f; + int shared_ind = -1; + if (opacity.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImg& opacities, float *ptrd) const { + const to *ptro = opacities._data; + cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++); + return ptrd; + } + + template + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImgList& opacities) const { + unsigned int siz = 8U + 3*_width; + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { + if (colors[c].is_shared()) siz+=4; + else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; } + } + if (colors._width + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImg& opacities) const { + unsigned int siz = 8U + 3*_width; + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { + const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; + } + if (colors._width + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const bool full_check=true) const { + CImgList opacities; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) const { + CImgList colors, opacities; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert 3D object into a CImg3d representation \overloading. + CImg get_object3dtoCImg3d(const bool full_check=true) const { + CImgList opacities, colors; + CImgList primitives(width(),1,1,1,1); + cimglist_for(primitives,p) primitives(p,0) = p; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert CImg3d representation into a 3D object. + /** + \param[out] primitives Primitives data of the 3D object. + \param[out] colors Colors data of the 3D object. + \param[out] opacities Opacities data of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + **/ + template + CImg& CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) { + return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this); + } + + //! Convert CImg3d representation into a 3D object \newinstance. + template + CImg get_CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) const { + CImg error_message(1024); + if (!is_CImg3d(full_check,error_message)) + throw CImgInstanceException(_cimg_instance + "CImg3dtoobject3d(): image instance is not a CImg3d (%s).", + cimg_instance,error_message.data()); + const T *ptrs = _data + 6; + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + const CImg points = CImg(ptrs,3,nb_points,1,1,true).get_transpose(); + ptrs+=3*nb_points; + primitives.assign(nb_primitives); + cimglist_for(primitives,p) { + const unsigned int nb_inds = (unsigned int)*(ptrs++); + primitives[p].assign(1,nb_inds); + tp *ptrp = primitives[p]._data; + for (unsigned int i = 0; i::max(),(T)cimg::type::max()); \ + const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \ + const ulongT _sc_whd = (ulongT)_width*_height*_depth; \ + cimg::unused(_sc_maxval); + +#define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \ + _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval) + + // [internal] The following _draw_scanline() routines are *non user-friendly functions*, + // used only for internal purpose. + // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid. + template + CImg& _draw_scanline(const int x0, const int x1, const int y, + const tc *const color, const float opacity, + const float brightness, + const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) { + const int nx0 = x0>0?x0:0, nx1 = x1=0) { + const tc *col = color; + const ulongT off = whd - dx - 1; + T *ptrd = data(nx0,y); + if (opacity>=1) { // ** Opaque drawing ** + if (brightness==1) { // Brightness==1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)*(col++); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)*(col++); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } else if (brightness<1) { // Brightness<1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } else { // Brightness>1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } + } else { // ** Transparent drawing ** + if (brightness==1) { // Brightness==1 + cimg_forC(*this,c) { + const Tfloat val = *(col++)*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else if (brightness<=1) { // Brightness<1 + cimg_forC(*this,c) { + const Tfloat val = *(col++)*brightness*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else { // Brightness>1 + cimg_forC(*this,c) { + const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } + } + } + return *this; + } + + //! Draw a 3D point. + /** + \param x0 X-coordinate of the point. + \param y0 Y-coordinate of the point. + \param z0 Z-coordinate of the point. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_point(50,50,color); + \endcode + **/ + template + CImg& draw_point(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_point(): Specified color is (null).", + cimg_instance); + if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } + } + return *this; + } + + //! Draw a 2D point \simplification. + template + CImg& draw_point(const int x0, const int y0, + const tc *const color, const float opacity=1) { + return draw_point(x0,y0,0,color,opacity); + } + + // Draw a points cloud. + /** + \param points Image of vertices coordinates. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_point(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity); + } break; + default : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity); + } + } + return *this; + } + + //! Draw a 2D line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + \note + - Line routine uses Bresenham's algorithm. + - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,color); + \endcode + **/ + template + CImg& draw_line(int x0, int y0, + int x1, int y1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !opacity || !pattern || + std::min(y0,y1)>=height() || std::max(y0,y1)<0 || + std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1); + dx01*=-1; dy01*=-1; + } + + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + const int + step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + T *const ptrd = is_horizontal?data(y,x):data(x,y); + cimg_forC(*this,c) { + const T val = color[c]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a 2D line, with z-buffering. + /** + \param zbuffer Zbuffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + **/ + template + CImg& draw_line(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_line(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float diz01 = iz1 - iz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1); + dx01*=-1; dy01*=-1; diz01*=-1; + } + + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float iz = iz0 + diz01*yy0/dy01; + tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y); + + if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) { + *ptrz = (tz)iz; + T *const ptrd = is_horizontal?data(y,x):data(x,y); + cimg_forC(*this,c) { + const T val = color[c]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + \note + - Line routine uses the well known Bresenham's algorithm. + \par Example: + \code + CImg img(100,100,1,3,0), texture("texture256x256.ppm"); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,texture,0,0,255,255); + \endcode + **/ + template + CImg& draw_line(int x0, int y0, + int x1, int y1, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + + if (is_empty() || !opacity || !pattern) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + int + dtx01 = tx1 - tx0, dty01 = ty1 - ty0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1); + dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy01ty = dy01*cimg::sign(dty01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01, + tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01, + ty = ty0 + (dty01*yy0 + hdy01ty)/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line, with perspective correction. + /** + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(int x0, int y0, const float z0, + int x1, int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float + diz01 = iz1 - iz0, + txz0 = tx0*iz0, txz1 = tx1*iz1, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, + dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1); + dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float + iz = iz0 + diz01*yy0/dy01, + txz = txz0 + dtxz01*yy0/dy01, + tyz = tyz0 + dtyz01*yy0/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line, with perspective correction and z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float + diz01 = iz1 - iz0, + txz0 = tx0*iz0, txz1 = tx1*iz1, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, + dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1); + dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float + iz = iz0 + diz01*yy0/dy01, + txz = txz0 + dtxz01*yy0/dy01, + tyz = tyz0 + dtyz01*yy0/dy01; + tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y); + + if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) { + *ptrz = (tz)iz; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a set of consecutive lines. + /** + \param points Coordinates of vertices, stored as a list of vectors. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If set to true, init hatch motif. + \note + - This function uses several call to the single CImg::draw_line() procedure, + depending on the vectors size in \p points. + **/ + template + CImg& draw_line(const CImg& points, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + default : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i + CImg& draw_arrow(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1, + const float angle=30, const float length=-10, + const unsigned int pattern=~0U) { + if (is_empty()) return *this; + const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v, + deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f, + l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; + if (sq>0) { + const float + cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg), + cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg); + const int + xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl), + xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr), + xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2; + draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); + } else draw_point(x0,y0,color,opacity); + return *this; + } + + //! Draw a 2D spline. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + \note + - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points + and corresponding velocity vectors. + - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the + average number of pixels in each drawn segment. + - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), + (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point + and (\p xa,\p ya), (\p xb,\p yb) are two + \e control points. + The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from + the control points as + \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,255,255 }; + img.draw_spline(30,30,0,100,90,40,0,-100,color); + \endcode + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const tc *const color, const float opacity=1, + const float precision=0.25, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Specified color is (null).", + cimg_instance); + if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); + int ox = x0, oy = y0; + for (float t = 0; t<1; t+=_precision) { + const float t2 = t*t, t3 = t2*t; + const int + nx = (int)(ax*t3 + bx*t2 + u0*t + x0), + ny = (int)(ay*t3 + by*t2 + v0*t + y0); + draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch); + ninit_hatch = false; + ox = nx; oy = ny; + } + return draw_line(ox,oy,x1,y1,color,opacity,pattern,false); + } + + //! Draw a textured 2D spline. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param texture Texture image defining line pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch if \c true, reinit hatch motif. + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const CImg& texture, + const int tx0, const int ty0, const int tx1, const int ty1, + const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_empty()) return *this; + if (is_overlapped(texture)) + return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch); + if (x0==x1 && y0==y1) + return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, + y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(), + opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); + int ox = x0, oy = y0, otx = tx0, oty = ty0; + for (float t1 = 0; t1<1; t1+=_precision) { + const float t2 = t1*t1, t3 = t2*t1; + const int + nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0), + ny = (int)(ay*t3 + by*t2 + v0*t1 + y0), + ntx = tx0 + (int)((tx1 - tx0)*t1), + nty = ty0 + (int)((ty1 - ty0)*t1); + draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch); + ninit_hatch = false; + ox = nx; oy = ny; otx = ntx; oty = nty; + } + return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false); + } + + //! Draw a set of consecutive splines. + /** + \param points Vertices data. + \param tangents Tangents data. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param is_closed_set Tells if the drawn spline set is closed. + \param precision Precision of the drawing. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + **/ + template + CImg& draw_spline(const CImg& points, const CImg& tangents, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + default : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); + int ox = x0, oy = y0; + float ou = u0, ov = v0; + for (unsigned int i = 1; i + CImg& draw_spline(const CImg& points, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + CImg tangents; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + tangents.assign(points._width,points._height); + cimg_forX(points,p) { + const unsigned int + p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0), + p1 = is_closed_set?(p + 1)%points.width():(p + 1 + CImg& _draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, const float opacity, + const float brightness) { + if (y0>y1) cimg::swap(x0,x1,y0,y1); + if (y0>y2) cimg::swap(x0,x2,y0,y2); + if (y1>y2) cimg::swap(x1,x2,y1,y2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM); + cimg_draw_scanline(xm,xM,y,color,opacity,cbs); + } + return *this; + } + + //! Draw a filled 2D triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); + return *this; + } + + //! Draw a outlined 2D triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + draw_line(x0,y0,x1,y1,color,opacity,pattern,true). + draw_line(x1,y1,x2,y2,color,opacity,pattern,false). + draw_line(x2,y2,x0,y0,color,opacity,pattern,false); + return *this; + } + + //! Draw a filled 2D triangle, with z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param z0 Z-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param z1 Z-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param z2 Z-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param brightness Brightness factor. + **/ + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; + + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param bs0 Brightness factor of the first vertex (in [0,2]). + \param bs1 brightness factor of the second vertex (in [0,2]). + \param bs2 brightness factor of the third vertex (in [0,2]). + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, + float bs0, + float bs1, + float bs2, + float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a color-interpolated 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex. + \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex. + \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc1 *const color1, + const tc2 *const color2, + const tc3 *const color3, + const float opacity=1) { + const unsigned char one = 1; + cimg_forC(*this,c) + get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity); + return *this; + } + + //! Draw a textured 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param opacity Drawing opacity. + \param brightness Brightness factor of the drawing (in [0,2]). + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2; + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM; + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a 2D textured triangle, with perspective correction. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured 2D triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a Phong-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + + const ulongT lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,lxm,lxM,lym,lyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dlxmM = lxM - lxm, dlymM = lyM - lym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a Phong-shaded 2D triangle, with z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; + + const ulongT lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dlxmM = lxM - lxm, dlymM = lyM - lym; + const float dizmM = izM - izm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const int + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const tc col = color[c]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param bs0 Brightness factor of the first vertex. + \param bs1 Brightness factor of the second vertex. + \param bs2 Brightness factor of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + bs0,bs1,bs2,opacity); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2; + const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym; + const float dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + bs0,bs1,bs2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym, + dlxmM = lxM - lxm, dlymM = lyM - lym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM, + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle, with perspective correction. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2, + lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2, + dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1, + dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float + dizmM = izM - izm, + dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, + dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + lxz = lxzm + dlxzmM*xxm/dxmM, + lyz = lyzm + dlyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz), + lx = (int)cimg::round(lxz/iz), + ly = (int)cimg::round(lyz/iz); + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2, + lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2, + dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1, + dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float + dizmM = izM - izm, + dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, + dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + lxz = lxzm + dlxzmM*xxm/dxmM, + lyz = lyzm + dlyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz), + lx = (int)cimg::round(lxz/iz), + ly = (int)cimg::round(lyz/iz); + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a filled 4D rectangle. + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param z0 Z-coordinate of the upper-left rectangle corner. + \param c0 C-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param z1 Z-coordinate of the lower-right rectangle corner. + \param c1 C-coordinate of the lower-right rectangle corner. + \param val Scalar value used to fill the rectangle area. + \param opacity Drawing opacity. + **/ + CImg& draw_rectangle(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const T val, const float opacity=1) { + if (is_empty()) return *this; + const int + nx0 = x0=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0), + ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0), + lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0), + lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0); + const ulongT + offX = (ulongT)_width - lx, + offY = (ulongT)_width*(_height - ly), + offZ = (ulongT)_width*_height*(_depth - lz); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0); + if (lx>0 && ly>0 && lz>0 && lc>0) + for (int v = 0; v=1) { + if (sizeof(T)!=1) { for (int x = 0; x + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_rectangle(): Specified color is (null).", + cimg_instance); + cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity); + return *this; + } + + //! Draw a filled 2D rectangle. + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1) { + return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity); + } + + //! Draw a outlined 2D rectangle \overloading. + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); + if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); + const int + nx0 = x0 + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + if (points.height()!=2) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum); + if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity); + if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)), + cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity); + if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)), + cimg::uiround(points(1,0)),cimg::uiround(points(1,1)), + cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity); + cimg_init_scanline(opacity); + int + xmin = 0, ymin = 0, + xmax = points.get_shared_row(0).max_min(xmin), + ymax = points.get_shared_row(1).max_min(ymin); + if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; + if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity); + + ymin = std::max(0,ymin); + ymax = std::min(height() - 1,ymax); + CImg Xs(points._width,ymax - ymin + 1); + CImg count(Xs._height,1,1,1,0); + unsigned int n = 0, nn = 1; + bool go_on = true; + + while (go_on) { + unsigned int an = (nn + 1)%points._width; + const int + x0 = cimg::uiround(points(n,0)), + y0 = cimg::uiround(points(n,1)); + if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; } + const int + x1 = cimg::uiround(points(nn,0)), + y1 = cimg::uiround(points(nn,1)); + unsigned int tn = an; + while (points(tn,1)==y1) (tn+=1)%=points._width; + + if (y0!=y1) { + const int + y2 = cimg::uiround(points(tn,1)), + x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1, + step = cimg::sign(y01), + tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2, + tend = tmax - (step==cimg::sign(y12)); + unsigned int y = (unsigned int)y0 - ymin; + for (int t = 0; t<=tend; ++t, y+=step) + if (yn; + n = nn; + nn = an; + } + + cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512)) + cimg_forY(Xs,y) { + const CImg Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort(); + int px = width(); + for (unsigned int k = 0; k + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity, const unsigned int pattern) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity); + if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1), + (int)points(1,0),(int)points(1,1),color,opacity,pattern); + bool ninit_hatch = true; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum); + default : { + CImg npoints(points._width,2); + int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); + unsigned int nb_points = 1; + for (unsigned int p = 1; p + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity=1) { + return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true); + } + + //! Draw a filled 2D ellipse \overloading. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param tensor Diffusion tensor describing the ellipse. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity=1) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity); + } + + //! Draw an outlined 2D ellipse. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param r1 First radius of the ellipse. + \param r2 Second radius of the ellipse. + \param angle Angle of the first radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity, const unsigned int pattern) { + if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false); + return *this; + } + + //! Draw an outlined 2D ellipse \overloading. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param tensor Diffusion tensor describing the ellipse. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity, + const unsigned int pattern) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity,pattern); + } + + template + CImg& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle, + const tc *const color, const float opacity, + const unsigned int pattern, const bool is_filled) { + if (is_empty() || (!is_filled && !pattern)) return *this; + const float radiusM = std::max(radius1,radius2); + if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_ellipse(): Specified color is (null).", + cimg_instance); + const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2); + if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity); + if (iradius1==iradius2) { + if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity); + else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern); + } + const float ang = (float)(angle*cimg::PI/180); + + if (!is_filled) { // Outlined + const float ca = std::cos(ang), sa = std::sin(ang); + CImg points((unsigned int)cimg::round(6*radiusM),2); + cimg_forX(points,k) { + const float + _ang = (float)(2*cimg::PI*k/points._width), + X = (float)(radius1*std::cos(_ang)), + Y = (float)(radius2*std::sin(_ang)); + points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa)); + points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca)); + } + draw_polygon(points,color,opacity,pattern); + } else { // Filled + cimg_init_scanline(opacity); + const float + ca = std::cos(ang), + sa = -std::sin(ang), + ca2 = ca*ca, + sa2 = sa*sa, + casa = ca*sa, + i1 = 1/cimg::sqr(radius1), + i2 = 1/cimg::sqr(radius2), + t1 = i1*ca2 + i2*sa2, + t2 = (i2 - i1)*casa, + t3 = i2*ca2 + i1*sa2, + t12 = t1*2; + const int + _ymin = (int)std::floor(y0 - radiusM), + _ymax = (int)std::ceil(y0 + radiusM), + ymin = _ymin<0?0:_ymin, + ymax = _ymax>=height()?height() - 1:_ymax; + for (int y = ymin; y<=ymax; ++y) { + const float + Y = y - y0 + 0.5f, + B = 2*t2*Y, + C = t3*Y*Y - 1, + D = B*B - 4*t1*C; + if (D>=0) { + const float sD = std::sqrt(D); + const int + xmin = (int)(x0 + cimg::round((-B - sD)/t12)), + xmax = (int)(x0 + cimg::round((-B + sD)/t12)); + cimg_draw_scanline(xmin,xmax,y,color,opacity,1); + } + } + } + return *this; + } + + //! Draw a filled 2D circle. + /** + \param x0 X-coordinate of the circle center. + \param y0 Y-coordinate of the circle center. + \param radius Circle radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - Circle version of the Bresenham's algorithm is used. + **/ + template + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (!radius) return draw_point(x0,y0,color,opacity); + cimg_init_scanline(opacity); + if (y0>=0 && y0=0) { + const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y; + if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern); + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; + if (!radius) return draw_point(x0,y0,color,opacity); + + draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity). + draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity); + if (radius==1) return *this; + for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x=0) { f+=(ddFy+=2); --y; } + ++x; ++(f+=(ddFx+=2)); + if (x!=y + 1) { + const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x, + x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y; + draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). + draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); + if (x!=y) + draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). + draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); + } + } + return *this; + } + + //! Draw an image. + /** + \param sprite Sprite image. + \param x0 X-coordinate of the sprite position. + \param y0 Y-coordinate of the sprite position. + \param z0 Z-coordinate of the sprite position. + \param c0 C-coordinate of the sprite position. + \param opacity Drawing opacity. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) + return assign(sprite,false); + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c=1) for (int x = 0; x& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) + return assign(sprite,false); + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + const ulongT slx = lx*sizeof(T); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c=1) std::memcpy(ptrd,ptrs,slx); + else for (int x = 0; x + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,z0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const CImg& sprite, const float opacity=1) { + return draw_image(0,sprite,opacity); + } + + //! Draw a masked image. + /** + \param sprite Sprite image. + \param mask Mask image. + \param x0 X-coordinate of the sprite position in the image instance. + \param y0 Y-coordinate of the sprite position in the image instance. + \param z0 Z-coordinate of the sprite position in the image instance. + \param c0 C-coordinate of the sprite position in the image instance. + \param mask_max_value Maximum pixel value of the mask image \c mask. + \param opacity Drawing opacity. + \note + - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. + - Dimensions along x,y and z of \p sprite and \p mask must be the same. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + if (is_empty() || !sprite || !mask) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value); + if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value); + if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth) + throw CImgArgumentException(_cimg_instance + "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data, + mask._width,mask._height,mask._depth,mask._spectrum,mask._data); + + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + const ulongT msize = mask.size(); + + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw an image. + template + CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a text string. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color. + \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color. + \param opacity Drawing opacity. + \param font Font used for drawing text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \note A transparent background is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \note A transparent foreground is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Array of spectrum() values of type \c T, + defining the foreground color (0 means 'transparent'). + \param background_color Array of spectrum() values of type \c T, + defining the background color (0 means 'transparent'). + \param opacity Drawing opacity. + \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise). + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + const CImgList& font = CImgList::font(font_height,true); + _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); + return *this; + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int background_color=0, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + cimg::unused(background_color); + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data); + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data); + } + + template + CImg& _draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, + const bool is_native_font) { + if (!text) return *this; + if (!font) + throw CImgArgumentException(_cimg_instance + "draw_text(): Empty specified font.", + cimg_instance); + + const unsigned int text_length = (unsigned int)std::strlen(text); + const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4; + unsigned char o_ch, ch = 0; + int x, y, w; + CImg left_paddings(text_length,1,1,1,0); + const CImg empty = CImg::empty(); + + if (is_empty() || is_native_font) { + // Pre-compute necessary size of the image as well as left paddings of each character. + x = y = w = 0; + o_ch = 0; + for (unsigned int i = 0; iw) w = x; x = 0; break; + case '\t' : x+=4*font[(int)' ']._width; break; + case ' ' : x+=font[(int)' ']._width; break; + default : if (ch'9')) || o_ch==';' || o_ch==':' || o_ch=='!') + left_padding = 4*padding_x; + else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') && + ((ch>='0' && ch<='9') || + (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') || + (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) || + o_ch=='.' || o_ch=='\'' || ch=='\'') + left_padding = padding_x; + else if ((o_ch<'0' || o_ch>'9') && ch!='-') { + const CImg &mask = ch + 256U' ' && o_ch>' ' && mask._height>13) { + const CImg &o_mask = o_ch + 256U13) { + const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0; + left_padding = -10; + cimg_forY(mask,k) { + const int + lpad = o_mask(w1,k)>=8?0: + o_mask._width<=2 || o_mask(w2,k)>=8?-1: + o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3, + rpad = mask(0,k)>=8?0: + mask._width<=2 || mask(1,k)>=8?-1: + mask._width<=3 || mask(2,k)>=8?-2:-3; + left_padding = std::max(left_padding,lpad + rpad); + } + } + } + } + left_paddings[i] = left_padding; + } + x+=left_padding + font[ch]._width + padding_x; + o_ch = ch; + } + } + } + if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; } + if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0); + } + + // Draw font characters on image. + x = x0; y = y0; + for (unsigned int i = 0; i letter = font[ch]; + if (letter) { + const CImg &mask = ch + 256Uletter._spectrum) + letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false); + const unsigned int cmin = std::min(_spectrum,letter._spectrum); + if (foreground_color) + for (unsigned int c = 0; c& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) { + CImg tmp(2048); + std::va_list ap; + va_start(ap,is_down); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + CImg a_label, a_labelmask; + const unsigned char a_labelcolor = 255; + unsigned int ofs = font_size, fs = ofs; + do { // Determine best font size + a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data); + if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) { + font_size = fs; break; + } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) { + ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f)); + } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) { + ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f)); + } else { font_size = fs; break; } + } while (true); + a_label.normalize(0,255); + a_label+=(255 - a_label.get_dilate(3)).normalize(0,80); + a_label.resize(-100,-100,1,3,1); + return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f); + } + + //! Draw a 2D vector field. + /** + \param flow Image of 2D vectors used as input data. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const t2 *const color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + return draw_quiver(flow,CImg(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern); + } + + //! Draw a 2D vector field, using a field of colors. + /** + \param flow Image of 2D vectors used as input data. + \param color Image of spectrum()-D vectors corresponding to the color of each arrow. + \param opacity Opacity of the drawing. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const CImg& color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + if (is_empty()) return *this; + if (!flow || flow._spectrum!=2) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).", + cimg_instance, + flow._width,flow._height,flow._depth,flow._spectrum,flow._data); + if (sampling<=0) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid sampling value %g " + "(should be >0)", + cimg_instance, + sampling); + const bool colorfield = (color._width==flow._width && color._height==flow._height && + color._depth==1 && color._spectrum==_spectrum); + if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern); + float vmax,fact; + if (factor<=0) { + float m, M = (float)flow.get_norm(2).max_min(m); + vmax = (float)std::max(cimg::abs(m),cimg::abs(M)); + if (!vmax) vmax = 1; + fact = -factor; + } else { fact = factor; vmax = 1; } + + for (unsigned int y = sampling/2; y<_height; y+=sampling) + for (unsigned int x = sampling/2; x<_width; x+=sampling) { + const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height; + float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax; + if (is_arrow) { + const int xx = (int)(x + u), yy = (int)(y + v); + if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern); + else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern); + } else { + if (colorfield) + draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), + color.get_vector_at(X,Y)._data,opacity,pattern); + else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), + color._data,opacity,pattern); + } + } + return *this; + } + + //! Draw a labeled horizontal axis. + /** + \param values_x Values along the horizontal axis. + \param y Y-coordinate of the horizontal axis in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const CImg& values_x, const int y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true, const float round_x=0) { + if (is_empty()) return *this; + const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height; + const int siz = (int)values_x.size() - 1; + CImg txt(32); + CImg a_label; + if (siz<=0) { // Degenerated case + draw_line(0,y,_width - 1,y,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x); + a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _xt = (width() - a_label.width())/2, + xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt; + draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case + if (values_x[0]=width() - 2?width() - 3 - a_label.width():_xt; + draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw a labeled vertical axis. + /** + \param x X-coordinate of the vertical axis in the image instance. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const int x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true, const float round_y=0) { + if (is_empty()) return *this; + int siz = (int)values_y.size() - 1; + CImg txt(32); + CImg a_label; + if (siz<=0) { // Degenerated case + draw_line(x,0,x,_height - 1,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y); + a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _yt = (height() - a_label.height())/2, + yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt, + _xt = x - 2 - a_label.width(), + xt = _xt>=0?_xt:x + 3; + draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case + if (values_y[0]=height()?height() - 1 - a_label.height():_yt, + _xt = x - 2 - a_label.width(), + xt = _xt>=0?_xt:x + 3; + draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes. + /** + \param values_x Values along the X-axis. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for the X-axis. + \param pattern_y Drawing pattern for the Y-axis. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axes(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13, const bool allow_zero=true, + const float round_x=0, const float round_y=0) { + if (is_empty()) return *this; + const CImg nvalues_x(values_x._data,values_x.size(),1,1,1,true); + const int sizx = (int)values_x.size() - 1, wm1 = width() - 1; + if (sizx>=0) { + float ox = (float)*nvalues_x; + for (unsigned int x = sizx?1U:0U; x<_width; ++x) { + const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1); + if (nx*ox<=0) { + draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y); + break; + } + ox = nx; + } + } + const CImg nvalues_y(values_y._data,values_y.size(),1,1,1,true); + const int sizy = (int)values_y.size() - 1, hm1 = height() - 1; + if (sizy>0) { + float oy = (float)nvalues_y[0]; + for (unsigned int y = sizy?1U:0U; y<_height; ++y) { + const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1); + if (ny*oy<=0) { + draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x); + break; + } + oy = ny; + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes \overloading. + template + CImg& draw_axes(const float x0, const float x1, const float y0, const float y1, + const tc *const color, const float opacity=1, + const int subdivisionx=-60, const int subdivisiony=-60, + const float precisionx=0, const float precisiony=0, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13) { + if (is_empty()) return *this; + const bool allow_zero = (x0*x1>0) || (y0*y1>0); + const float + dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0), + px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx, + py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony; + if (x0!=x1 && y0!=y1) + draw_axes(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1), + CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1), + color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py); + else if (x0==x1 && y0!=y1) + draw_axis((int)x0,CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1), + color,opacity,pattern_y,font_height,py); + else if (x0!=x1 && y0==y1) + draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0, + color,opacity,pattern_x,font_height,px); + return *this; + } + + //! Draw 2D grid. + /** + \param values_x X-coordinates of the vertical lines. + \param values_y Y-coordinates of the horizontal lines. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for vertical lines. + \param pattern_y Drawing pattern for horizontal lines. + **/ + template + CImg& draw_grid(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + if (values_x) cimg_foroff(values_x,x) { + const int xi = (int)values_x[x]; + if (xi>=0 && xi=0 && yi + CImg& draw_grid(const float delta_x, const float delta_y, + const float offsetx, const float offsety, + const bool invertx, const bool inverty, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + CImg seqx, seqy; + if (delta_x!=0) { + const float dx = delta_x>0?delta_x:_width*-delta_x/100; + const unsigned int nx = (unsigned int)(_width/dx); + seqx = CImg::sequence(1 + nx,0,(unsigned int)(dx*nx)); + if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width); + if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x); + } + if (delta_y!=0) { + const float dy = delta_y>0?delta_y:_height*-delta_y/100; + const unsigned int ny = (unsigned int)(_height/dy); + seqy = CImg::sequence(1 + ny,0,(unsigned int)(dy*ny)); + if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height); + if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y); + } + return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y); + } + + //! Draw 1D graph. + /** + \param data Image containing the graph values I = f(x). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + + \param plot_type Define the type of the plot: + - 0 = No plot. + - 1 = Plot using segments. + - 2 = Plot using cubic splines. + - 3 = Plot with bars. + \param vertex_type Define the type of points: + - 0 = No points. + - 1 = Point. + - 2 = Straight cross. + - 3 = Diagonal cross. + - 4 = Filled circle. + - 5 = Outlined circle. + - 6 = Square. + - 7 = Diamond. + \param ymin Lower bound of the y-range. + \param ymax Upper bound of the y-range. + \param pattern Drawing pattern. + \note + - if \c ymin==ymax==0, the y-range is computed automatically from the input samples. + **/ + template + CImg& draw_graph(const CImg& data, + const tc *const color, const float opacity=1, + const unsigned int plot_type=1, const int vertex_type=1, + const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { + if (is_empty() || _height<=1) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_graph(): Specified color is (null).", + cimg_instance); + + // Create shaded colors for displaying bar plots. + CImg color1, color2; + if (plot_type==3) { + color1.assign(_spectrum); color2.assign(_spectrum); + cimg_forC(*this,c) { + color1[c] = (tc)std::min((float)cimg::type::max(),(float)color[c]*1.2f); + color2[c] = (tc)(color[c]*0.4f); + } + } + + // Compute min/max and normalization factors. + const ulongT + siz = data.size(), + _siz1 = siz - (plot_type!=3), + siz1 = _siz1?_siz1:1; + const unsigned int + _width1 = _width - (plot_type!=3), + width1 = _width1?_width1:1; + double m = ymin, M = ymax; + if (ymin==ymax) m = (double)data.max_min(M); + if (m==M) { --m; ++M; } + const float ca = (float)(M-m)/(_height - 1); + bool init_hatch = true; + + // Draw graph edges + switch (plot_type%4) { + case 1 : { // Segments + int oX = 0, oY = (int)cimg::round((data[0] - m)/ca); + if (siz==1) { + const int Y = (int)cimg::round((*data - m)/ca); + draw_line(0,Y,width() - 1,Y,color,opacity,pattern); + } else { + const float fx = (float)_width/siz1; + for (ulongT off = 1; off ndata(data._data,siz,1,1,1,true); + int oY = (int)cimg::round((data[0] - m)/ca); + cimg_forX(*this,x) { + const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca); + if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch); + init_hatch = false; + oY = Y; + } + } break; + case 3 : { // Bars + const int Y0 = (int)cimg::round(-m/ca); + const float fx = (float)_width/siz1; + int oX = 0; + cimg_foroff(data,off) { + const int + X = (int)cimg::round((off + 1)*fx) - 1, + Y = (int)cimg::round((data[off] - m)/ca); + draw_rectangle(oX,Y0,X,Y,color,opacity). + draw_line(oX,Y,oX,Y0,color2.data(),opacity). + draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity). + draw_line(X,Y,X,Y0,color1.data(),opacity). + draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity); + oX = X + 1; + } + } break; + default : break; // No edges + } + + // Draw graph points + const unsigned int wb2 = plot_type==3?_width1/(2*siz):0; + const float fx = (float)_width1/siz1; + switch (vertex_type%8) { + case 1 : { // Point + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_point(X,Y,color,opacity); + } + } break; + case 2 : { // Straight Cross + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity); + } + } break; + case 3 : { // Diagonal Cross + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity); + } + } break; + case 4 : { // Filled Circle + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity); + } + } break; + case 5 : { // Outlined circle + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity,~0U); + } + } break; + case 6 : { // Square + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U); + } + } break; + case 7 : { // Diamond + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X,Y - 4,X + 4,Y,color,opacity). + draw_line(X + 4,Y,X,Y + 4,color,opacity). + draw_line(X,Y + 4,X - 4,Y,color,opacity). + draw_line(X - 4,Y,X,Y - 4,color,opacity); + } + } break; + default : break; // No points + } + return *this; + } + + bool _draw_fill(const int x, const int y, const int z, + const CImg& ref, const float tolerance2) const { + const T *ptr1 = data(x,y,z), *ptr2 = ref._data; + const ulongT off = _width*_height*_depth; + float diff = 0; + cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; } + return diff<=tolerance2; + } + + //! Draw filled 3D region with the flood fill algorithm. + /** + \param x0 X-coordinate of the starting point of the region to fill. + \param y0 Y-coordinate of the starting point of the region to fill. + \param z0 Z-coordinate of the starting point of the region to fill. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param[out] region Image that will contain the mask of the filled region mask, as an output. + \param tolerance Tolerance concerning neighborhood values. + \param opacity Opacity of the drawing. + \param is_high_connectivity Tells if 8-connexity must be used. + \return \c region is initialized with the binary mask of the filled region. + **/ + template + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity, + CImg ®ion, + const float tolerance = 0, + const bool is_high_connectivity = false) { +#define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \ + stack[N] = x; stack(N,1) = y; stack(N++,2) = z +#define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2) +#define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2) + + if (!containsXYZC(x0,y0,z0,0)) return *this; + const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f); + const float tolerance2 = cimg::sqr(tolerance); + const CImg ref = get_vector_at(x0,y0,z0); + CImg stack(256,1,1,3); + CImg _region(_width,_height,_depth,1,0); + unsigned int N = 0; + int x, y, z; + + _draw_fill_push(x0,y0,z0); + while (N>0) { + _draw_fill_pop(x,y,z); + if (!_region(x,y,z)) { + const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1; + int xl = x, xr = x; + + // Using these booleans reduces the number of pushes drastically. + bool is_yp = false, is_yn = false, is_zp = false, is_zn = false; + for (int step = -1; step<2; step+=2) { + while (x>=0 && x=0 && _draw_fill_is_inside(x,yp,z)) { + if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; } + } else is_yp = false; + if (yn1) { + if (zp>=0 && _draw_fill_is_inside(x,y,zp)) { + if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; } + } else is_zp = false; + if (zn=0 && !is_yp) { + if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) { + _draw_fill_push(xp,yp,z); if (step<0) is_yp = true; + } + if (xn0) is_yp = true; + } + } + if (yn=0 && _draw_fill_is_inside(xp,yn,z)) { + _draw_fill_push(xp,yn,z); if (step<0) is_yn = true; + } + if (xn0) is_yn = true; + } + } + if (depth()>1) { + if (zp>=0 && !is_zp) { + if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) { + _draw_fill_push(xp,y,zp); if (step<0) is_zp = true; + } + if (xn0) is_zp = true; + } + + if (yp>=0 && !is_yp) { + if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); } + if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); } + if (xn=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); } + if (xn=0 && _draw_fill_is_inside(xp,y,zn)) { + _draw_fill_push(xp,y,zn); if (step<0) is_zn = true; + } + if (xn0) is_zn = true; + } + + if (yp>=0 && !is_yp) { + if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); } + if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); } + if (xn=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); } + if (xn + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1, + const float tolerance=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity); + } + + //! Draw filled 2D region with the flood fill algorithm \simplification. + template + CImg& draw_fill(const int x0, const int y0, + const tc *const color, const float opacity=1, + const float tolerance=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity); + } + + //! Draw a random plasma texture. + /** + \param alpha Alpha-parameter. + \param beta Beta-parameter. + \param scale Scale-parameter. + \note Use the mid-point algorithm to render. + **/ + CImg& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) { + if (is_empty()) return *this; + const int w = width(), h = height(); + const Tfloat m = (Tfloat)cimg::type::min(), M = (Tfloat)cimg::type::max(); + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + cimg_forZC(*this,z,c) { + CImg ref = get_shared_slice(z,c); + for (int delta = 1<1; delta>>=1) { + const int delta2 = delta>>1; + const float r = alpha*delta + beta; + + // Square step. + for (int y0 = 0; y0M?M:val); + } + + // Diamond steps. + for (int y = -delta2; yM?M:val); + } + for (int y0 = 0; y0M?M:val); + } + for (int y = -delta2; yM?M:val); + } + } + } + cimg::srand(rng); + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2D fractal. + /** + \param x0 X-coordinate of the upper-left pixel. + \param y0 Y-coordinate of the upper-left pixel. + \param x1 X-coordinate of the lower-right pixel. + \param y1 Y-coordinate of the lower-right pixel. + \param colormap Colormap. + \param opacity Drawing opacity. + \param z0r Real part of the upper-left fractal vertex. + \param z0i Imaginary part of the upper-left fractal vertex. + \param z1r Real part of the lower-right fractal vertex. + \param z1i Imaginary part of the lower-right fractal vertex. + \param iteration_max Maximum number of iterations for each estimated point. + \param is_normalized_iteration Tells if iterations are normalized. + \param is_julia_set Tells if the Mandelbrot or Julia set is rendered. + \param param_r Real part of the Julia set parameter. + \param param_i Imaginary part of the Julia set parameter. + \note Fractal rendering is done by the Escape Time Algorithm. + **/ + template + CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, + const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + if (is_empty()) return *this; + CImg palette; + if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true); + if (palette && palette._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.); + const int + _x0 = cimg::cut(x0,0,width() - 1), + _y0 = cimg::cut(y0,0,height() - 1), + _x1 = cimg::cut(x1,0,width() - 1), + _y1 = cimg::cut(y1,0,height() - 1); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048)) + for (int q = _y0; q<=_y1; ++q) + for (int p = _x0; p<=_x1; ++p) { + unsigned int iteration = 0; + const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height; + double zr, zi, cr, ci; + if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; } + else { zr = param_r; zi = param_i; cr = x; ci = y; } + for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) { + const double temp = zr*zr - zi*zi + cr; + zi = 2*zr*zi + ci; + zr = temp; + } + if (iteration>iteration_max) { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity); + } + } else if (is_normalized_iteration) { + const float + normz = (float)cimg::abs(zr*zr + zi*zi), + niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2); + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c); + else cimg_forC(*this,c) + (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } else { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } + } + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading. + template + CImg& draw_mandelbrot(const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity, + z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i); + } + + //! Draw a 1D gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param sigma Standard variation of the gaussian distribution. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float sigma, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + cimg_forX(*this,x) { + const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2); + T *ptrd = data(x,0,0,0); + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 2D gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param yc Y-coordinate of the gaussian center. + \param tensor Covariance matrix (must be 2x2). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + typedef typename CImg::Tfloat tfloat; + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + float dy = -yc; + cimg_forY(*this,y) { + float dx = -xc; + cimg_forX(*this,x) { + const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); + T *ptrd = data(x,y,0,0); + if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + ++dx; + } + ++dy; + } + return *this; + } + + //! Draw a 2D gaussian function \overloading. + template + CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity=1) { + const double + a = r1*ru*ru + r2*rv*rv, + b = (r1-r2)*ru*rv, + c = r1*rv*rv + r2*ru*ru; + const CImg tensor(2,2,1,1, a,b,b,c); + return draw_gaussian(xc,yc,tensor,color,opacity); + } + + //! Draw a 2D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); + } + + //! Draw a 3D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + typedef typename CImg::Tfloat tfloat; + if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + cimg_forXYZ(*this,x,y,z) { + const float + dx = (x - xc), dy = (y - yc), dz = (z - zc), + val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); + T *ptrd = data(x,y,z,0); + if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 3D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); + } + + //! Draw a 3D object. + /** + \param x0 X-coordinate of the 3D object position + \param y0 Y-coordinate of the 3D object position + \param z0 Z-coordinate of the 3D object position + \param vertices Image Nx3 describing 3D point coordinates + \param primitives List of P primitives + \param colors List of P color (or textures) + \param opacities Image or list of P opacities + \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) + \param is_double_sided Tells if object faces have two sides or are oriented. + \param focale length of the focale (0 for parallel projection) + \param lightx X-coordinate of the light + \param lighty Y-coordinate of the light + \param lightz Z-coordinate of the light + \param specular_lightness Amount of specular light. + \param specular_shininess Shininess of the object + \param g_opacity Global opacity of the object. + **/ + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } +#endif + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } +#endif + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,zbuffer); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,zbuffer); + } +#endif + + template + static float __draw_object3d(const CImgList& opacities, const unsigned int n_primitive, CImg& opacity) { + if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; } + if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); } + opacity.assign(opacities[n_primitive],true); + return 1.f; + } + + template + static float __draw_object3d(const CImg& opacities, const unsigned int n_primitive, CImg& opacity) { + opacity.assign(); + return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive]; + } + + template + static float ___draw_object3d(const CImgList& opacities, const unsigned int n_primitive) { + return n_primitive + static float ___draw_object3d(const CImg& opacities, const unsigned int n_primitive) { + return n_primitive + CImg& _draw_object3d(void *const pboard, CImg& zbuffer, + const float X, const float Y, const float Z, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, const float sprite_scale) { + typedef typename cimg::superset2::type tpfloat; + typedef typename to::value_type _to; + if (is_empty() || !vertices || !primitives) return *this; + CImg error_message(1024); + if (!vertices.is_object3d(primitives,colors,opacities,false,error_message)) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); +#ifndef cimg_use_board + if (pboard) return *this; +#endif + if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety + + const float + nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)), + nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess), + nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1), + nsl2 = 1 - 2*nsl1*nspec, + nsl3 = nspec2 - nsl1 - nsl2; + + // Create light texture for phong-like rendering. + CImg light_texture; + if (render_type==5) { + if (colors._width>primitives._width) { + static CImg default_light_texture; + static const tc *lptr = 0; + static tc ref_values[64] = { 0 }; + const CImg& img = colors.back(); + bool is_same_texture = (lptr==img._data); + if (is_same_texture) + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) { + is_same_texture = false; break; + } + if (!is_same_texture || default_light_texture._spectrum<_spectrum) { + (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum); + lptr = colors.back().data(); + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum); + } + light_texture.assign(default_light_texture,true); + } else { + static CImg default_light_texture; + static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0; + if (!default_light_texture || + lightx!=olightx || lighty!=olighty || lightz!=olightz || + specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) { + default_light_texture.assign(512,512); + const float + dlx = lightx - X, + dly = lighty - Y, + dlz = lightz - Z, + nl = cimg::hypot(dlx,dly,dlz), + nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl), + nly = (default_light_texture._height - 1)/2*(1 + dly/nl), + white[] = { 1 }; + default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white); + cimg_forXY(default_light_texture,x,y) { + const float factor = default_light_texture(x,y); + if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3); + } + default_light_texture.resize(-100,-100,1,_spectrum); + olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess; + } + light_texture.assign(default_light_texture,true); + } + } + + // Compute 3D to 2D projection. + CImg projections(vertices._width,2); + tpfloat parallzmin = cimg::type::max(); + const float absfocale = focale?cimg::abs(focale):0; + if (absfocale) { + cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) + cimg_forX(projections,l) { // Perspective projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + const tpfloat projectedz = z + Z + absfocale; + projections(l,1) = Y + absfocale*y/projectedz; + projections(l,0) = X + absfocale*x/projectedz; + } + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) + cimg_forX(projections,l) { // Parallel projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + if (z visibles(primitives._width,1,1,1,~0U); + CImg zrange(primitives._width); + const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); + bool is_forward = zbuffer?true:false; + + cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096)) + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + switch (primitive.size()) { + case 1 : { // Point + CImg<_to> _opacity; + __draw_object3d(opacities,l,_opacity); + if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false; + const unsigned int i0 = (unsigned int)primitive(0); + const tpfloat z0 = Z + vertices(i0,2); + if (z0>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = z0; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)), + Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)), + Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)), + _zc = Z + Zc, + zc = _zc + _focale, + xc = X + Xc*(absfocale?absfocale/zc:1), + yc = Y + Yc*(absfocale?absfocale/zc:1), + radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0), + vertices(i1,1) - vertices(i0,1), + vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1), + xm = xc - radius, + ym = yc - radius, + xM = xc + radius, + yM = yc + radius; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = _zc; + } + is_forward = false; + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2); + tpfloat xm, xM, ym, yM; + if (x0=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1)/2; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (y0yM) yM = y2; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { + const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0); + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2)/3; + } + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = (unsigned int)primitive(3); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2), + x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (x3xM) xM = x3; + if (y0yM) yM = y2; + if (y3yM) yM = y3; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) { + const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0); + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2 + z3)/4; + } + } + } break; + default : + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid primitive[%u] with size %u " + "(should have size 1,2,3,4,5,6,9 or 12).", + cimg_instance, + l,primitive.size()); + } + } + + // Force transparent primitives to be drawn last when zbuffer is activated + // (and if object contains no spheres or sprites). + if (is_forward) + cimglist_for(primitives,l) + if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l); + + // Sort only visibles primitives. + unsigned int *p_visibles = visibles._data; + tpfloat *p_zrange = zrange._data; + const tpfloat *ptrz = p_zrange; + cimg_for(visibles,ptr,unsigned int) { + if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; } + ++ptrz; + } + const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data); + if (!nb_visibles) { + if (render_type==5) cimg::mutex(10,0); + return *this; + } + CImg permutations; + CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); + + // Compute light properties + CImg lightprops; + switch (render_type) { + case 3 : { // Flat Shading + lightprops.assign(nb_visibles); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const CImg& primitive = primitives(visibles(permutations(l))); + const unsigned int psize = (unsigned int)primitive.size(); + if (psize==3 || psize==4 || psize==9 || psize==12) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nx = dy1*dz2 - dz1*dy2, + ny = dz1*dx2 - dx1*dz2, + nz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + cimg::hypot(nx,ny,nz), + lx = X + (x0 + x1 + x2)/3 - lightx, + ly = Y + (y0 + y1 + y2)/3 - lighty, + lz = Z + (z0 + z1 + z2)/3 - lightz, + nl = 1e-5f + cimg::hypot(lx,ly,lz), + factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } else lightprops[l] = 1; + } + } break; + + case 4 : // Gouraud Shading + case 5 : { // Phong-Shading + CImg vertices_normals(vertices._width,6,1,1,0); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + for (int l = 0; l<(int)nb_visibles; ++l) { + const CImg& primitive = primitives[visibles(l)]; + const unsigned int psize = (unsigned int)primitive.size(); + const bool + triangle_flag = (psize==3) || (psize==9), + quadrangle_flag = (psize==4) || (psize==12); + if (triangle_flag || quadrangle_flag) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = quadrangle_flag?(unsigned int)primitive(3):0; + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nnx = dy1*dz2 - dz1*dy2, + nny = dz1*dx2 - dx1*dz2, + nnz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + cimg::hypot(nnx,nny,nnz), + nx = nnx/norm, + ny = nny/norm, + nz = nnz/norm; + unsigned int ix = 0, iy = 1, iz = 2; + if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; } + vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz; + vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz; + vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz; + if (quadrangle_flag) { + vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz; + } + } + } + + if (is_double_sided) cimg_forX(vertices_normals,p) { + const float + nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2), + nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5), + n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1; + if (n1>n0) { + vertices_normals(p,0) = -nx1; + vertices_normals(p,1) = -ny1; + vertices_normals(p,2) = -nz1; + } + } + + if (render_type==4) { + lightprops.assign(vertices._width); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = 1e-5f + cimg::hypot(nx,ny,nz), + lx = X + vertices(l,0) - lightx, + ly = Y + vertices(l,1) - lighty, + lz = Z + vertices(l,2) - lightz, + nl = 1e-5f + cimg::hypot(lx,ly,lz), + factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } + } else { + const unsigned int + lw2 = light_texture._width/2 - 1, + lh2 = light_texture._height/2 - 1; + lightprops.assign(vertices._width,2); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = 1e-5f + cimg::hypot(nx,ny,nz), + nnx = nx/norm, + nny = ny/norm; + lightprops(l,0) = lw2*(1 + nnx); + lightprops(l,1) = lh2*(1 + nny); + } + } + } break; + } + + // Draw visible primitives + const CImg default_color(1,_spectrum,1,1,(tc)200); + CImg<_to> _opacity; + + for (unsigned int l = 0; l& primitive = primitives[n_primitive]; + const CImg + &__color = n_primitive(), + _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)? + __color.get_resize(-100,-100,-100,_spectrum,0):CImg(), + &color = _color?_color:(__color?__color:default_color); + const tc *const pcolor = color._data; + float opacity = __draw_object3d(opacities,n_primitive,_opacity); + if (_opacity.is_empty()) opacity*=g_opacity; + +#ifdef cimg_use_board + LibBoard::Board &board = *(LibBoard::Board*)pboard; +#endif + + switch (primitive.size()) { + case 1 : { // Colored point or sprite + const unsigned int n0 = (unsigned int)primitive[0]; + const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)); + + if (_opacity.is_empty()) { // Scalar opacity + + if (color.size()==_spectrum) { // Colored point + draw_point(x0,y0,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height()-(float)y0); + } +#endif + } else { // Sprite + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(color._width*factor), + _sh = (unsigned int)(color._height*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && + (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + draw_image(nx0,ny0,sprite,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); + } +#endif + } + } + } else { // Opacity mask + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor), + _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && + (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + const CImg<_to> + _nopacity = (sw!=_opacity._width || sh!=_opacity._height)? + _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(), + &nopacity = _nopacity?_nopacity:_opacity; + draw_image(nx0,ny0,sprite,nopacity,g_opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); + } +#endif + } + } + } break; + case 2 : { // Colored line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity); + else draw_line(x0,y0,x1,y1,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1); + } +#endif + } else { + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + } +#endif + } + } break; + case 5 : { // Colored sphere + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + is_wireframe = (unsigned int)primitive[2], + is_radius = (unsigned int)primitive[3]; + float Xc,Yc,Zc,radius; + if (is_radius) { + Xc = (float)vertices(n0,0); + Yc = (float)vertices(n0,1); + Zc = (float)vertices(n0,2); + radius = cimg::hypot(vertices(n1,0) - vertices(n0,0), + vertices(n1,1) - vertices(n0,1), + vertices(n1,2) - vertices(n0,2)); + } else { + Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)); + Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)); + Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)); + radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0), + vertices(n1,1) - vertices(n0,1), + vertices(n1,2) - vertices(n0,2)); + } + const float + zc = Z + Zc + _focale, + af = absfocale?absfocale/zc:1, + xc = X + Xc*af, + yc = Y + Yc*af; + radius*=af; + + switch (render_type) { + case 0 : + draw_point((int)xc,(int)yc,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot(xc,height() - yc); + } +#endif + break; + case 1 : + draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.setFillColor(LibBoard::Color::Null); + board.drawCircle(xc,height() - yc,radius); + } +#endif + break; + default : + if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); + else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + if (!is_wireframe) board.fillCircle(xc,height() - yc,radius); + else { + board.setFillColor(LibBoard::Color::Null); + board.drawCircle(xc,height() - yc,radius); + } + } +#endif + break; + } + } break; + case 6 : { // Textured line + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for line primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + tx0 = (int)primitive[2], ty0 = (int)primitive[3], + tx1 = (int)primitive[4], ty1 = (int)primitive[5], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + } +#endif + } else { + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + } +#endif + } + } break; + case 3 : { // Colored triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity). + draw_line(x1,y1,x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 3 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)); + else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), + (float)x1,height() - (float)y1,lightprops(n1), + (float)x2,height() - (float)y2,lightprops(n2)); + } +#endif + break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), + (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), + (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), + (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + } +#endif + } break; + } + } break; + case 4 : { // Colored quadrangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)), + x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)), + xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4; + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale, + zc = (z0 + z1 + z2 + z3)/4; + + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity). + draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + board.drawDot((float)x3,height() - (float)y3); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity). + draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); + board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity); + else + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l)); + else + _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)). + _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp),(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3), + lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4; + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity). + draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity). + draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity). + draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity); + else + draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity). + draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity). + draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity). + draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x1,height() - (float)y1,lightprop1, + (float)x2,height() - (float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x2,height() - (float)y2,lightprop2, + (float)x3,height() - (float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)), + lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)), + lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4; + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). + draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). + draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). + draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); + else + draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). + draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). + draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). + draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x2,height() - (float)y2,l2, + (float)x3,height() - (float)y3,l3); + } +#endif + } break; + } + } break; + case 9 : { // Textured triangle + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for triangle primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + tx0 = (int)primitive[3], ty0 = (int)primitive[4], + tx1 = (int)primitive[5], ty1 = (int)primitive[6], + tx2 = (int)primitive[7], ty2 = (int)primitive[8], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, + ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), + (float)x1,height() - (float)y1,lightprops(n1), + (float)x2,height() - (float)y2,lightprops(n2)); + } +#endif + break; + case 5 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), + opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), + opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), + (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), + (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), + (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + } +#endif + break; + } + } break; + case 12 : { // Textured quadrangle + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for quadrangle primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + tx0 = (int)primitive[4], ty0 = (int)primitive[5], + tx1 = (int)primitive[6], ty1 = (int)primitive[7], + tx2 = (int)primitive[8], ty2 = (int)primitive[9], + tx3 = (int)primitive[10], ty3 = (int)primitive[11], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)), + x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale; + + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, + ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity). + draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3, + ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + board.drawDot((float)x3,height() - (float)y3); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); + board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x1,height() - (float)y1,lightprop1, + (float)x2,height() - (float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0, + (float)x2,height() - (float)y2,lightprop2, + (float)x3,height() - (float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)), + lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + board.fillGouraudTriangle((float)x0,height() -(float)y0,l0, + (float)x2,height() - (float)y2,l2, + (float)x3,height() - (float)y3,l3); + } +#endif + } break; + } + } break; + } + } + if (render_type==5) cimg::mutex(10,0); + return *this; + } + + //@} + //--------------------------- + // + //! \name Data Input + //@{ + //--------------------------- + + //! Launch simple interface to select a shape from an image. + /** + \param disp Display window to use. + \param feature_type Type of feature to select. Can be { 0=point | 1=line | 2=rectangle | 3=ellipse }. + \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images. + \param exit_on_anykey Exit function when any key is pressed. + **/ + CImg& select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); + } + + //! Simple interface to select a shape from an image \overloading. + CImg& select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + CImgDisplay disp; + return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); + } + + CImg _select(CImgDisplay &disp, const char *const title, + const unsigned int feature_type, unsigned int *const XYZ, + const int origX, const int origY, const int origZ, + const bool exit_on_anykey, + const bool reset_view3d, + const bool force_display_z_coord, + const bool is_deep_selection_default) const { + if (is_empty()) return CImg(1,feature_type==0?3:6,1,1,-1); + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + } else { + if (title) disp.set_title("%s",title); + disp.move_inside_screen(); + } + + CImg thumb; + if (width()>disp.screen_width() || height()>disp.screen_height()) + get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb); + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).set_wheel().show_mouse(); + + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + int area = 0, area_started = 0, area_clicked = 0, phase = 0, + X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), + Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), + Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth), + X1 =-1, Y1 = -1, Z1 = -1, + X3d = -1, Y3d = -1, + oX3d = X3d, oY3d = -1, + omx = -1, omy = -1; + float X = -1, Y = -1, Z = -1; + unsigned int key = 0, font_size = 32; + + bool is_deep_selection = is_deep_selection_default, + shape_selected = false, text_down = false, visible_cursor = true; + static CImg pose3d; + static bool is_view3d = false, is_axes = true; + if (reset_view3d) { pose3d.assign(); is_view3d = false; } + CImg points3d, opacities3d, sel_opacities3d; + CImgList primitives3d, sel_primitives3d; + CImgList colors3d, sel_colors3d; + CImg visu, visu0, view3d; + CImg text(1024); *text = 0; + + while (!key && !disp.is_closed() && !shape_selected) { + + // Handle mouse motion and selection + int + mx = disp.mouse_x(), + my = disp.mouse_y(); + + const float + mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height(); + + area = 0; + if (mX>=0 && mY>=0 && mX=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); } + if (mY>=0 && mX>=width() && mY=width() && mY>=height()) area = 4; + if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0; + + CImg filename(32); + + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break; + case cimg::keyPAGEDOWN : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break; + case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp); + visu0.save(filename); + (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + } + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp); + save(filename); + (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + } + + switch (area) { + + case 0 : // When mouse is out of image range + mx = my = -1; X = Y = Z = -1; + break; + + case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections + const unsigned int but = disp.button(); + const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4); + + if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step) + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; + } + if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes) + switch (area_started) { + case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break; + case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break; + case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break; + } + } + if (b2 && area_clicked==area) { // When moving through the image/volume + if (phase) { + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; + } else { + if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign(); + X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; + } + } + if (b3) { // Reset selection + X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0; + visu0.assign(); + } + if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel) + if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && + !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) { + switch (area) { + case 1 : + if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); + visu0.assign(); break; + case 2 : + if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel()); + visu0.assign(); break; + case 3 : + if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel()); + visu0.assign(); break; + } + disp.set_wheel(); + } else key = ~0U; + } + + if ((phase==0 && b1) || + (phase==1 && !b1) || + (phase==2 && b1)) switch (phase) { // Detect change of phase + case 0 : + if (area==area_clicked) { + X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase; + } break; + case 1 : + if (area==area_started) { + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; + if (_depth>1) { + if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default; + if (is_deep_selection) ++phase; + } + } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } + break; + case 2 : ++phase; break; + } + } break; + + case 4 : // When mouse is over the 3D view + if (is_view3d && points3d) { + X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0)); + Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0)); + if (oX3d<0) { oX3d = X3d; oY3d = Y3d; } + // Left + right buttons: reset. + if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } + else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate + const float + R = 0.45f*std::min(view3d._width,view3d._height), + R2 = R*R, + u0 = (float)(oX3d - view3d.width()/2), + v0 = (float)(oY3d - view3d.height()/2), + u1 = (float)(X3d - view3d.width()/2), + v1 = (float)(Y3d - view3d.height()/2), + n0 = cimg::hypot(u0,v0), + n1 = cimg::hypot(u1,v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), + u = nv0*nw1 - nw0*nv1, + v = nw0*nu1 - nu0*nw1, + w = nv0*nu1 - nu0*nv1, + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + pose3d.draw_image(CImg::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2)); + view3d.assign(); + } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom + pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign(); + } + if (disp.wheel()) { // Wheel: zoom + pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel(); + } + if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift + pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign(); + } + oX3d = X3d; oY3d = Y3d; + } + mx = my = -1; X = Y = Z = -1; + break; + } + + if (phase) { + if (!feature_type) shape_selected = phase?true:false; + else { + if (_depth>1) shape_selected = (phase==3)?true:false; + else shape_selected = (phase==2)?true:false; + } + } + + if (X0<0) X0 = 0; + if (X0>=width()) X0 = width() - 1; + if (Y0<0) Y0 = 0; + if (Y0>=height()) Y0 = height() - 1; + if (Z0<0) Z0 = 0; + if (Z0>=depth()) Z0 = depth() - 1; + if (X1<1) X1 = 0; + if (X1>=width()) X1 = width() - 1; + if (Y1<0) Y1 = 0; + if (Y1>=height()) Y1 = height() - 1; + if (Z1<0) Z1 = 0; + if (Z1>=depth()) Z1 = depth() - 1; + + // Draw visualization image on the display + if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { + + if (!visu0) { // Create image of projected planes + if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + visu0.resize(disp); + view3d.assign(); + points3d.assign(); + } + + if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images + const unsigned int + _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1), + _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1), + x3d = _x3d>=visu0._width?visu0._width - 1:_x3d, + y3d = _y3d>=visu0._height?visu0._height - 1:_y3d; + CImg(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3). + move_to(view3d); + if (!points3d) { + get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d); + points3d.append(CImg(8,3,1,1, + 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0, + 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1, + 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x'); + CImg::vector(12,13).move_to(primitives3d); CImg::vector(13,14).move_to(primitives3d); + CImg::vector(14,15).move_to(primitives3d); CImg::vector(15,12).move_to(primitives3d); + CImg::vector(16,17).move_to(primitives3d); CImg::vector(17,18).move_to(primitives3d); + CImg::vector(18,19).move_to(primitives3d); CImg::vector(19,16).move_to(primitives3d); + CImg::vector(12,16).move_to(primitives3d); CImg::vector(13,17).move_to(primitives3d); + CImg::vector(14,18).move_to(primitives3d); CImg::vector(15,19).move_to(primitives3d); + colors3d.insert(12,CImg::vector(255,255,255)); + opacities3d.assign(primitives3d.width(),1,1,1,0.5f); + if (!phase) { + opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f; + sel_primitives3d.assign(); + sel_colors3d.assign(); + sel_opacities3d.assign(); + } else { + if (feature_type==2) { + points3d.append(CImg(8,3,1,1, + X0,X1,X1,X0,X0,X1,X1,X0, + Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1, + Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x'); + sel_primitives3d.assign(); + CImg::vector(20,21).move_to(sel_primitives3d); + CImg::vector(21,22).move_to(sel_primitives3d); + CImg::vector(22,23).move_to(sel_primitives3d); + CImg::vector(23,20).move_to(sel_primitives3d); + CImg::vector(24,25).move_to(sel_primitives3d); + CImg::vector(25,26).move_to(sel_primitives3d); + CImg::vector(26,27).move_to(sel_primitives3d); + CImg::vector(27,24).move_to(sel_primitives3d); + CImg::vector(20,24).move_to(sel_primitives3d); + CImg::vector(21,25).move_to(sel_primitives3d); + CImg::vector(22,26).move_to(sel_primitives3d); + CImg::vector(23,27).move_to(sel_primitives3d); + } else { + points3d.append(CImg(2,3,1,1, + X0,X1, + Y0,Y1, + Z0,Z1),'x'); + sel_primitives3d.assign(CImg::vector(20,21)); + } + sel_colors3d.assign(sel_primitives3d._width,CImg::vector(255,255,255)); + sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f); + } + points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d(); + points3d*=0.75f*std::min(view3d._width,view3d._height); + } + + if (!pose3d) CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d); + CImg zbuffer3d(view3d._width,view3d._height,1,1,0); + const CImg rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d; + if (sel_primitives3d) + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d, + 2,true,500,0,0,0,0,0,1,zbuffer3d); + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,primitives3d,colors3d,opacities3d, + 2,true,500,0,0,0,0,0,1,zbuffer3d); + visu0.draw_image(x3d,y3d,view3d); + } + visu = visu0; + + if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} + else { + if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }} + else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} + const int d = (depth()>1)?depth():0; + int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z; + if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; } + int + w = disp.width(), W = width() + d, + h = disp.height(), H = height() + d, + _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX), + _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY), + _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1), + _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1), + _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()), + _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()), + _zxn = (int)((_vZ + width() + 1.f)*w/W - 1), + zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1), + _zyn = (int)((_vZ + height() + 1.f)*h/H - 1), + zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1), + _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()), + _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()), + xc = (xp + xn)/2, + yc = (yp + yn)/2, + zxc = (zxp + zxn)/2, + zyc = (zyp + zyn)/2, + xf = (int)(X*w/W), + yf = (int)(Y*h/H), + zxf = (int)((Z + width())*w/W), + zyf = (int)((Z + height())*h/H); + + if (is_axes) { // Draw axes + visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00). + draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF). + draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00). + draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF); + if (_depth>1) + visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00). + draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF). + draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00). + draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF); + } + + // Draw box cursor. + if (xn - xp>=4 && yn - yp>=4) + visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). + draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); + if (_depth>1) { + if (yn - yp>=4 && zxn - zxp>=4) + visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). + draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); + if (xn - xp>=4 && zyn - zyp>=4) + visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). + draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); + } + + // Draw selection. + if (phase && (phase!=1 || area_started==area)) { + const int + _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0), + _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0), + _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1), + _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1), + _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()), + _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()), + _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1), + zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1), + _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1), + zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1), + xc0 = (xp0 + xn0)/2, + yc0 = (yp0 + yn0)/2, + zxc0 = (zxp0 + zxn0)/2, + zyc0 = (zyp0 + zyn0)/2; + + switch (feature_type) { + case 1 : { // Vector + visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333). + draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC); + if (d) { + visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333). + draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC). + draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333). + draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC); + } + } break; + case 2 : { // Box + visu.draw_rectangle(X0=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false; + if (!feature_type || !phase) { + if (X>=0 && Y>=0 && Z>=0 && X1 || force_display_z_coord) + cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z); + else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y); + CImg values = get_vector_at((int)X,(int)Y,(int)Z); + const bool is_large_spectrum = values._height>8; + if (is_large_spectrum) + values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0); + char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; + for (unsigned int c = 0; c::format_s(), + cimg::type::format(values[c])); + ctext += std::strlen(ctext); + if (c==3 && is_large_spectrum) { + cimg_snprintf(ctext,24," ..."); + ctext += std::strlen(ctext); + } + *(ctext++) = ' '; *ctext = 0; + } + std::strcpy(text._data + std::strlen(text),"] "); + } + } else switch (feature_type) { + case 1 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length); + else if (_width!=1 && _height!=1) + cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length, + cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1)); + else + cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length); + } break; + case 2 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width, + " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ", + origX + (X01 || force_display_z_coord) + cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1, + 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1)); + else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ", + origX + X0,origY + Y0,origX + X1,origY + Y1, + 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1)); + } + if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data); + } + + disp.display(visu); + } + if (!shape_selected) disp.wait(); + if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } + omx = mx; omy = my; + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + + // Return result. + CImg res(1,feature_type==0?3:6,1,1,-1); + if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } + if (shape_selected) { + if (feature_type==2) { + if (is_deep_selection) switch (area_started) { + case 1 : Z0 = 0; Z1 = _depth - 1; break; + case 2 : Y0 = 0; Y1 = _height - 1; break; + case 3 : X0 = 0; X1 = _width - 1; break; + } + if (X0>X1) cimg::swap(X0,X1); + if (Y0>Y1) cimg::swap(Y0,Y1); + if (Z0>Z1) cimg::swap(Z0,Z1); + } + if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; + switch (feature_type) { + case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; + case 3 : + res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); + res[0] = X0; res[1] = Y0; res[2] = Z0; + break; + default : res[0] = X0; res[1] = Y0; res[2] = Z0; + } + } + if (!exit_on_anykey || !(disp.button()&4)) disp.set_button(); + if (!visible_cursor) disp.show_mouse(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + if (key!=~0U) disp.set_key(key); + return res; + } + + // Return a visualizable uchar8 image for display routines. + CImg _get_select(const CImgDisplay& disp, const int normalization, + const int x, const int y, const int z) const { + if (is_empty()) return CImg(1,1,1,1,0); + const CImg crop = get_shared_channels(0,std::min(2,spectrum() - 1)); + CImg img2d; + if (_depth>1) { + const int mdisp = std::min(disp.screen_width(),disp.screen_height()); + if (depth()>mdisp) { + crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d); + img2d.projections2d(x,y,z*img2d._depth/_depth); + } else crop.get_projections2d(x,y,z).move_to(img2d); + } else CImg(crop,false).move_to(img2d); + + // Check for inf and NaN values. + if (cimg::type::is_float() && normalization) { + bool is_inf = false, is_nan = false; + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) { is_inf = true; break; } + else if (cimg::type::is_nan(*ptr)) { is_nan = true; break; } + if (is_inf || is_nan) { + Tint m0 = (Tint)cimg::type::max(), M0 = (Tint)cimg::type::min(); + if (!normalization) { m0 = 0; M0 = 255; } + else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; } + else + cimg_for(img2d,ptr,Tuchar) + if (!cimg::type::is_inf(*ptr) && !cimg::type::is_nan(*ptr)) { + if (*ptr<(Tuchar)m0) m0 = *ptr; + if (*ptr>(Tuchar)M0) M0 = *ptr; + } + const T + val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0), + val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0); + if (is_nan) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values + if (is_inf) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values + } + } + + switch (normalization) { + case 1 : img2d.normalize((ucharT)0,(ucharT)255); break; + case 2 : { + const float m = disp._min, M = disp._max; + (img2d-=m)*=255.f/(M - m>0?M - m:1); + } break; + case 3 : + if (cimg::type::is_float()) img2d.normalize((ucharT)0,(ucharT)255); + else { + const float + m = (float)cimg::type::min(), + M = (float)cimg::type::max(); + (img2d-=m)*=255.f/(M - m>0?M - m:1); + } break; + } + if (img2d.spectrum()==2) img2d.channels(0,2); + return img2d; + } + + //! Select sub-graph in a graph. + CImg get_select_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "select_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). + set_title("CImg<%s>",pixel_type()); + const ulongT siz = (ulongT)_width*_height*_depth; + const unsigned int old_normalization = disp.normalization(); + disp.show().set_button().set_wheel()._normalization = 0; + + double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; + if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; } + if (nymin==nymax) { --nymin; ++nymax; } + if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; } + + static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; + static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; + + CImg colormap(3,_spectrum); + if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; } + else { + colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10; + if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; } + if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; } + if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10; } + if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10; colormap(2,4) = 220; } + if (_spectrum>5) { colormap(0,5) = 10; colormap(1,5) = 220; colormap(2,5) = 220; } + if (_spectrum>6) { + cimg_uint64 rng = 10; + cimg_for_inY(colormap,6,colormap.height()-1,k) { + colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + } + } + } + + CImg visu0, visu, graph, text, axes; + int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; + const unsigned int one = plot_type==3?0U:1U; + unsigned int okey = 0, obutton = 0, font_size = 32; + CImg message(1024); + CImg_3x3(I,unsigned char); + + for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) { + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + const unsigned int key = disp.key(), button = disp.button(); + + // Generate graph representation. + if (!visu0) { + visu0.assign(disp.width(),disp.height(),1,3,220); + const int gdimx = disp.width() - 32, gdimy = disp.height() - 32; + if (gdimx>0 && gdimy>0) { + graph.assign(gdimx,gdimy,1,3,255); + if (siz<32) { + if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0, + false,true,black,0.2f,0x33333333,0x33333333); + } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); + cimg_forC(*this,c) + graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f, + plot_type,vertex_type,nymax,nymin); + + axes.assign(gdimx,gdimy,1,1,0); + const float + dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin), + px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.), + py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.); + const CImg + seqx = dx<=0?CImg::vector(nxmin): + CImg::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz), + seqy = CImg::sequence(1 + gdimy/60,nymax,nymin); + + const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0); + axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py); + if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px); + if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px); + if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py); + if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py); + + cimg_for3x3(axes,x,y,0,0,I,unsigned char) + if (Icc) { + if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0; + else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3); + } + else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) + cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3); + + visu0.draw_image(16,16,graph); + visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2). + draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white); + } else graph.assign(); + text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3); + visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text); + text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3); + visu0.draw_image(1,(visu0.height() - text.height())/2,~text); + visu.assign(); + } + + // Generate and display current view. + if (!visu) { + visu.assign(visu0); + if (graph && x0>=0 && x1>=0) { + const int + nx0 = x0<=x1?x0:x1, + nx1 = x0<=x1?x1:x0, + ny0 = y0<=y1?y0:y1, + ny1 = y0<=y1?y1:y0, + sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)), + sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)), + sy0 = 16 + ny0, + sy1 = 16 + ny1; + if (y0>=0 && y1>=0) + visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU); + else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f). + draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU). + draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU); + } + if (mouse_x>=16 && mouse_y>=16 && mouse_x=7) + cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx, + (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), + (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3), + (double)(*this)(x,0,0,_spectrum - 1)); + else { + cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx); + cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); + cimg_sprintf(message._data + std::strlen(message),")"); + } + if (x0>=0 && x1>=0) { + const unsigned int + nx0 = (unsigned int)(x0<=x1?x0:x1), + nx1 = (unsigned int)(x0<=x1?x1:x0), + ny0 = (unsigned int)(y0<=y1?y0:y1), + ny1 = (unsigned int)(y0<=y1?y1:y0); + const double + cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), + cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), + cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32), + cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32); + if (y0>=0 && y1>=0) + cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + x0,cx0,cy0,x1 + one,cx1,cy1); + else + cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", + x0,cx0,x1 + one,cx1); + } + text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); + visu.draw_image((visu.width() - text.width())/2,1,~text); + } + visu.display(disp); + } + + // Test keys. + CImg filename(32); + switch (okey = key) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + CImgDisplay::screen_height()/2,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp); + screen.save(filename); + (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp); + save(filename); + (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + } + + // Handle mouse motion and mouse buttons. + if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) { + visu.assign(); + if (disp.mouse_x()>=0 && disp.mouse_y()>=0) { + const int + mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32), + cx = cimg::cut(mx,0,(int)(siz - 1 - one)), + my = mouse_y - 16, + cy = cimg::cut(my,0,disp.height() - 32); + if (button&1) { + if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; } + } + else if (button&2) { + if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; } + } + else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; } + } else if (!button && obutton) selected = true; + obutton = button; omouse_x = mouse_x; omouse_y = mouse_y; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (visu && visu0) disp.wait(); + if (!exit_on_anykey && okey && okey!=cimg::keyESC && + (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + okey = 0; + } + } + + disp._normalization = old_normalization; + if (x1>=0 && x1(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1); + } + + //! Load image from a file. + /** + \param filename Filename, as a C-string. + \note The extension of \c filename defines the file format. If no filename + extension is provided, CImg::get_load() will try to load the file as a .cimg or .cimgz file. + **/ + CImg& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load(): Specified filename is (null).", + cimg_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg filename_local(256); + load(cimg::load_network(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + bool is_loaded = true; + try { +#ifdef cimg_load_plugin + cimg_load_plugin(filename); +#endif +#ifdef cimg_load_plugin1 + cimg_load_plugin1(filename); +#endif +#ifdef cimg_load_plugin2 + cimg_load_plugin2(filename); +#endif +#ifdef cimg_load_plugin3 + cimg_load_plugin3(filename); +#endif +#ifdef cimg_load_plugin4 + cimg_load_plugin4(filename); +#endif +#ifdef cimg_load_plugin5 + cimg_load_plugin5(filename); +#endif +#ifdef cimg_load_plugin6 + cimg_load_plugin6(filename); +#endif +#ifdef cimg_load_plugin7 + cimg_load_plugin7(filename); +#endif +#ifdef cimg_load_plugin8 + cimg_load_plugin8(filename); +#endif + // Text formats + if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); + else if (!cimg::strcasecmp(ext,"csv") || + !cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) load_dlm(filename); + else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename); + + // 2D binary formats + else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); + else if (!cimg::strcasecmp(ext,"png")) load_png(filename); + else if (!cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"pnm") || + !cimg::strcasecmp(ext,"pbm") || + !cimg::strcasecmp(ext,"pnk")) load_pnm(filename); + else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename); + else if (!cimg::strcasecmp(ext,"cr2") || + !cimg::strcasecmp(ext,"crw") || + !cimg::strcasecmp(ext,"dcr") || + !cimg::strcasecmp(ext,"mrw") || + !cimg::strcasecmp(ext,"nef") || + !cimg::strcasecmp(ext,"orf") || + !cimg::strcasecmp(ext,"pix") || + !cimg::strcasecmp(ext,"ptx") || + !cimg::strcasecmp(ext,"raf") || + !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"heic") || + !cimg::strcasecmp(ext,"avif")) load_heif(filename); + + // 3D binary formats + else if (!cimg::strcasecmp(ext,"dcm") || + !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) load_analyze(filename); + else if (!cimg::strcasecmp(ext,"par") || + !cimg::strcasecmp(ext,"rec")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename); + else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) return load_cimg(filename); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + + // Image sequences + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + + // If nothing loaded, try to guess file format from magic number in file. + if (!is_loaded) { + std::FILE *file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load(): Failed to open file '%s'.", + cimg_instance, + filename); + } + + const char *const f_type = cimg::ftype(file,filename); + cimg::fclose(file); + is_loaded = true; + try { + if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename); + else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename); + else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(f_type,"png")) load_png(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + } + + // If nothing loaded, try to load file with other means. + if (!is_loaded) { + try { + load_other(filename); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load image from a file \newinstance. + static CImg get_load(const char *const filename) { + return CImg().load(filename); + } + + //! Load image from an ascii file. + /** + \param filename Filename, as a C -string. + **/ + CImg& load_ascii(const char *const filename) { + return _load_ascii(0,filename); + } + + //! Load image from an ascii file \inplace. + static CImg get_load_ascii(const char *const filename) { + return CImg().load_ascii(filename); + } + + //! Load image from an ascii file \overloading. + CImg& load_ascii(std::FILE *const file) { + return _load_ascii(file,0); + } + + //! Loadimage from an ascii file \newinstance. + static CImg get_load_ascii(std::FILE *const file) { + return CImg().load_ascii(file); + } + + CImg& _load_ascii(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_ascii(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg line(256); *line = 0; + int err = std::fscanf(nfile,"%255[^\n]",line._data); + unsigned int dx = 0, dy = 1, dz = 1, dc = 1; + cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); + err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]"); + if (!dx || !dy || !dz || !dc) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_ascii(): Invalid ascii header in file '%s', image dimensions are set " + "to (%u,%u,%u,%u).", + cimg_instance, + filename?filename:"(FILE*)",dx,dy,dz,dc); + } + assign(dx,dy,dz,dc); + const ulongT siz = size(); + ulongT off = 0; + double val; + T *ptr = _data; + for (err = 1, off = 0; off& load_dlm(const char *const filename) { + return _load_dlm(0,filename); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(const char *const filename) { + return CImg().load_dlm(filename); + } + + //! Load image from a DLM file \overloading. + CImg& load_dlm(std::FILE *const file) { + return _load_dlm(file,0); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(std::FILE *const file) { + return CImg().load_dlm(file); + } + + CImg& _load_dlm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_dlm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + CImg delimiter(256), tmp(256); *delimiter = *tmp = 0; + unsigned int cdx = 0, dx = 0, dy = 0; + int err = 0; + double val; + assign(256,256,1,1,(T)0); + while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) { + if (err>0) (*this)(cdx++,dy) = (T)val; + if (cdx>=_width) resize(3*_width/2,_height,1,1,0); + char c = 0; + if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') { + dx = std::max(cdx,dx); + if (++dy>=_height) resize(_width,3*_height/2,1,1,0); + cdx = 0; + } + } + if (cdx && err==1) { dx = cdx; ++dy; } + if (!dx || !dy) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_dlm(): Invalid DLM file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + resize(dx,dy,1,1,0); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a BMP file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_bmp(const char *const filename) { + return _load_bmp(0,filename); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(const char *const filename) { + return CImg().load_bmp(filename); + } + + //! Load image from a BMP file \overloading. + CImg& load_bmp(std::FILE *const file) { + return _load_bmp(file,0); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(std::FILE *const file) { + return CImg().load_bmp(file); + } + + CImg& _load_bmp(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_bmp(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg header(54); + cimg::fread(header._data,54,nfile); + if (*header!='B' || header[1]!='M') { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid BMP file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read header and pixel buffer + int + file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), + offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), + header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24), + dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), + dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), + compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), + nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), + bpp = header[0x1C] + (header[0x1D]<<8); + + if (!file_size || file_size==offset) { + cimg::fseek(nfile,0,SEEK_END); + file_size = (int)cimg::ftell(nfile); + cimg::fseek(nfile,54,SEEK_SET); + } + if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); + + const int + dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)), + align_bytes = (4 - dx_bytes%4)%4; + const ulongT + cimg_iobuffer = (ulongT)24*1024*1024, + buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes); + + CImg colormap; + if (bpp<16) { if (!nb_colors) nb_colors = 1<0) cimg::fseek(nfile,xoffset,SEEK_CUR); + + CImg buffer; + if (buf_size=2) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + unsigned char mask = 0x80, val = 0; + cimg_forX(*this,x) { + if (mask==0x80) val = *(ptrs++); + const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask); + } + ptrs+=align_bytes; + } + } break; + case 4 : { // 16 colors + if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + unsigned char mask = 0xF0, val = 0; + cimg_forX(*this,x) { + if (mask==0xF0) val = *(ptrs++); + const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); + const unsigned char *col = (unsigned char*)(colormap._data + color); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask,4); + } + ptrs+=align_bytes; + } + } break; + case 8 : { // 256 colors + if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + } + ptrs+=align_bytes; + } + } break; + case 16 : { // 16 bits colors (RGB565) + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); + const unsigned short col = (unsigned short)c2<<8 | c1; + (*this)(x,y,2) = (T)((col&0x1F)<<3); + (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3); + (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3); + } + ptrs+=align_bytes; + } + } break; + case 24 : { // 24 bits colors + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + } + ptrs+=align_bytes; + } + } break; + case 32 : { // 32 bits colors + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + ++ptrs; + } + ptrs+=align_bytes; + } + } break; + } + if (dy<0) mirror('y'); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a JPEG file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_jpeg(const char *const filename) { + return _load_jpeg(0,filename); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(const char *const filename) { + return CImg().load_jpeg(filename); + } + + //! Load image from a JPEG file \overloading. + CImg& load_jpeg(std::FILE *const file) { + return _load_jpeg(file,0); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(std::FILE *const file) { + return CImg().load_jpeg(file); + } + + // Custom error handler for libjpeg. +#ifdef cimg_use_jpeg + struct _cimg_error_mgr { + struct jpeg_error_mgr original; + jmp_buf setjmp_buffer; + char message[JMSG_LENGTH_MAX]; + }; + + typedef struct _cimg_error_mgr *_cimg_error_ptr; + + METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) { + _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point + (*cinfo->err->format_message)(cinfo,c_err->message); + jpeg_destroy(cinfo); // Clean memory and temp files + longjmp(c_err->setjmp_buffer,1); + } +#endif + + CImg& _load_jpeg(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_jpeg(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_jpeg + if (file) + throw CImgIOException(_cimg_instance + "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.", + cimg_instance); + else return load_other(filename); +#else + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + struct jpeg_decompress_struct cinfo; + struct _cimg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr.original); + jerr.original.error_exit = _cimg_jpeg_error_exit; + if (setjmp(jerr.setjmp_buffer)) { // JPEG error + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_jpeg(): Error message returned by libjpeg: %s.", + cimg_instance,jerr.message); + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo,nfile); + jpeg_read_header(&cinfo,TRUE); + jpeg_start_decompress(&cinfo); + + if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { + if (!file) { + cimg::fclose(nfile); + return load_other(filename); + } else + throw CImgIOException(_cimg_instance + "load_jpeg(): Failed to load JPEG data from file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + } + CImg buffer(cinfo.output_width*cinfo.output_components); + JSAMPROW row_pointer[1]; + try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); } + catch (...) { if (!file) cimg::fclose(nfile); throw; } + T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, + *ptr_a = _data + 3UL*_width*_height; + while (cinfo.output_scanline + // This is experimental code, not much tested, use with care. + CImg& load_magick(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_magick(): Specified filename is (null).", + cimg_instance); + +#ifdef cimg_use_magick + Magick::Image image(filename); + const unsigned int W = image.size().width(), H = image.size().height(); + switch (image.type()) { + case Magick::PaletteMatteType : + case Magick::TrueColorMatteType : + case Magick::ColorSeparationType : { + assign(W,H,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + case Magick::PaletteType : + case Magick::TrueColorType : { + assign(W,H,1,3); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + ++pixels; + } + } break; + case Magick::GrayscaleMatteType : { + assign(W,H,1,2); + T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + default : { + assign(W,H,1,1); + T *ptr_r = data(0,0,0,0); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + ++pixels; + } + } + } + return *this; +#else + throw CImgIOException(_cimg_instance + "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + } + + //! Load image from a file, using Magick++ library \newinstance. + static CImg get_load_magick(const char *const filename) { + return CImg().load_magick(filename); + } + + //! Load image from a PNG file. + /** + \param filename Filename, as a C-string. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + **/ + CImg& load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return _load_png(0,filename,bits_per_value); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return CImg().load_png(filename,bits_per_value); + } + + //! Load image from a PNG file \overloading. + CImg& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return _load_png(file,0,bits_per_value); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return CImg().load_png(file,bits_per_value); + } + + // (Note: Most of this function has been written by Eric Fausett) + CImg& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_png(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_png + cimg::unused(bits_per_value); + if (file) + throw CImgIOException(_cimg_instance + "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.", + cimg_instance); + + else return load_other(filename); +#else + // Open file and check for PNG validity +#if defined __GNUC__ + const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); +#else + const char *nfilename = filename; + std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb"); +#endif + unsigned char pngCheck[8] = { 0 }; + cimg::fread(pngCheck,8,(std::FILE*)nfile); + if (png_sig_cmp(pngCheck,0,8)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Invalid PNG file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Setup PNG structures for read + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn); + if (!png_ptr) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'end_info' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Error handling callback for png file reading + if (setjmp(png_jmpbuf(png_ptr))) { + if (!file) cimg::fclose((std::FILE*)nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Encountered unknown fatal error in libpng for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + png_set_sig_bytes(png_ptr, 8); + + // Get PNG Header Info up to data block + png_read_info(png_ptr,info_ptr); + png_uint_32 W, H; + int bit_depth, color_type, interlace_type; + bool is_gray = false; + png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0); + if (bits_per_value) *bits_per_value = (unsigned int)bit_depth; + + // Transforms to unify image data + if (color_type==PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + color_type = PNG_COLOR_TYPE_RGB; + bit_depth = 8; + } + if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + is_gray = true; + bit_depth = 8; + } + if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + color_type |= PNG_COLOR_MASK_ALPHA; + } + if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + color_type |= PNG_COLOR_MASK_COLOR; + is_gray = true; + } + if (color_type==PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER); + + png_read_update_info(png_ptr,info_ptr); + if (bit_depth!=8 && bit_depth!=16) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Invalid bit depth %u in file '%s'.", + cimg_instance, + bit_depth,nfilename?nfilename:"(FILE*)"); + } + const int byte_depth = bit_depth>>3; + + // Allocate memory for image reading + png_bytep *const imgData = new png_bytep[H]; + for (unsigned int row = 0; row& load_pnm(const char *const filename) { + return _load_pnm(0,filename); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(const char *const filename) { + return CImg().load_pnm(filename); + } + + //! Load image from a PNM file \overloading. + CImg& load_pnm(std::FILE *const file) { + return _load_pnm(file,0); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(std::FILE *const file) { + return CImg().load_pnm(file); + } + + CImg& _load_pnm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pnm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned int ppm_type, W, H, D = 1, colormax = 255; + CImg item(16384,1,1,1,0); + int err, rval, gval, bval; + const longT cimg_iobuffer = (longT)24*1024*1024; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item," P%u",&ppm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (ppm_type!=1 && ppm_type!=4) { + if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item,"%u",&colormax)!=1) + cimg::warn(_cimg_instance + "load_pnm(): COLORMAX field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else { colormax = D; D = 1; } + } + std::fgetc(nfile); + + if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension + const cimg_int64 siz = cimg::fsize(filename); + if (W*H*D>siz) + throw CImgIOException(_cimg_instance + "load_pnm(): Specified image dimensions in file '%s' exceed file size.", + cimg_instance, + filename); + } + + switch (ppm_type) { + case 1 : { // 2D B&W ascii + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; } + } break; + case 2 : { // 2D grey ascii + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; } + } break; + case 3 : { // 2D color ascii + assign(W,H,1,3); + T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forXY(*this,x,y) { + if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { + *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; + } else break; + } + } break; + case 4 : { // 2D b&w binary (support 3D PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + unsigned int w = 0, h = 0, d = 0; + for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + unsigned char mask = 0, val = 0; + for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) { + if (!mask) { if (off--) val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?0:255); + if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }} + } + } + } break; + case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension) + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } else { // 16 bits + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } + } break; + case 6 : { // 2D color binary + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { // 16 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } + } break; + case 8 : { // 2D/3D grey binary with int32 integers (PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const int *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + case 9 : { // 2D/3D grey binary with float values (PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const float *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + default : + assign(); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM type 'P%d' found, but type is not supported.", + cimg_instance, + filename?filename:"(FILE*)",ppm_type); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PFM file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pfm(const char *const filename) { + return _load_pfm(0,filename); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(const char *const filename) { + return CImg().load_pfm(filename); + } + + //! Load image from a PFM file \overloading. + CImg& load_pfm(std::FILE *const file) { + return _load_pfm(file,0); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(std::FILE *const file) { + return CImg().load_pfm(file); + } + + CImg& _load_pfm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pfm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char pfm_type; + CImg item(16384,1,1,1,0); + int W = 0, H = 0, err = 0; + double scale = 0; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item," P%c",&pfm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): PFM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else if (W<=0 || H<=0) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.", + cimg_instance,W,H, + filename?filename:"(FILE*)"); + } + if (err==2) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item,"%lf",&scale)!=1) + cimg::warn(_cimg_instance + "load_pfm(): SCALE field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + std::fgetc(nfile); + const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness(); + if (is_color) { + assign(W,H,1,3,(T)0); + CImg buf(3*W); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forY(*this,y) { + cimg::fread(buf._data,3*W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,3*W); + const float *ptrs = buf._data; + cimg_forX(*this,x) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { + assign(W,H,1,1,(T)0); + CImg buf(W); + T *ptrd = data(0,0,0,0); + cimg_forY(*this,y) { + cimg::fread(buf._data,W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,W); + const float *ptrs = buf._data; + cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return mirror('y'); // Most of the .pfm files are flipped along the y-axis + } + + //! Load image from a RGB file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(0,filename,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(filename,dimw,dimh); + } + + //! Load image from a RGB file \overloading. + CImg& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(file,0,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(file,dimw,dimh); + } + + CImg& _load_rgb(std::FILE *const file, const char *const filename, + const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgb(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const longT cimg_iobuffer = (longT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = raw._width/3UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a RGBA file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(0,filename,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(filename,dimw,dimh); + } + + //! Load image from a RGBA file \overloading. + CImg& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(file,0,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(file,dimw,dimh); + } + + CImg& _load_rgba(std::FILE *const file, const char *const filename, + const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgba(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const longT cimg_iobuffer = (longT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,4); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2), + *ptr_a = data(0,0,0,3); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = raw._width/4UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a TIFF file. + /** + \param filename Filename, as a C-string. + \param first_frame First frame to read (for multi-pages tiff). + \param last_frame Last frame to read (for multi-pages tiff). + \param step_frame Step value of frame reading. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + \param[out] voxel_size Voxel size, as stored in the filename. + \param[out] description Description, as stored in the filename. + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tiff is not defined at compile time the + function uses CImg& load_other(const char*). + **/ + CImg& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Specified filename is (null).", + cimg_instance); + + const unsigned int + nfirst_frame = first_frame1) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.", + cimg_instance, + filename); + return load_other(filename); +#else +#if cimg_verbosity<3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn(_cimg_instance + "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).", + cimg_instance, + filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + TIFFSetDirectory(tif,0); + CImg frame; + for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { + frame._load_tiff(tif,l,bits_per_value,voxel_size,description); + if (l==nfirst_frame) + assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum); + if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) + resize(std::max(frame._width,_width), + std::max(frame._height,_height),-100, + std::max(frame._spectrum,_spectrum),0); + draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame); + } + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "load_tiff(): Failed to open file '%s'.", + cimg_instance, + filename); + return *this; +#endif + } + + //! Load image from a TIFF file \newinstance. + static CImg get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); + } + + // (Original contribution by Jerome Boulanger). +#ifdef cimg_use_tiff + template + void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, + const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int row = 0; row + void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, + const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int vv = 0; vv + void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (row = 0; rowny?ny - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0; rr + void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (unsigned int vv = 0; vvny?ny - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0;rr& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value, + float *const voxel_size, CImg *const description) { + if (!TIFFSetDirectory(tif,directory)) return assign(); + uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; + uint16 sampleformat = 1; + uint32 nx = 1, ny = 1; + const char *const filename = TIFFFileName(tif); + const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); + TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); + TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); + TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); + TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); + TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + if (bits_per_value) *bits_per_value = (unsigned int)bitspersample; + if (voxel_size) { + const char *s_description = 0; + float vx = 0, vy = 0, vz = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) { + const char *s_desc = std::strstr(s_description,"VX="); + if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format + voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz; + } + s_desc = std::strstr(s_description,"spacing="); + if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format + voxel_size[2] = vz; + } + } + TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size); + TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1); + voxel_size[0] = 1.f/voxel_size[0]; + voxel_size[1] = 1.f/voxel_size[1]; + } + if (description) { + const char *s_description = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) + CImg::string(s_description).move_to(*description); + } + const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; + assign(nx,ny,1,spectrum); + + if ((photo>=3 && sampleformat==1 && + (bitspersample==4 || bitspersample==8) && + (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) || + (bitspersample==1 && samplesperpixel==1)) { + // Special case for unsigned color images. + uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32)); + if (!raster) { + _TIFFfree(raster); TIFFClose(tif); + throw CImgException(_cimg_instance + "load_tiff(): Failed to allocate memory (%s) for file '%s'.", + cimg_instance, + cimg::strbuffersize(nx*ny*sizeof(uint32)),filename); + } + TIFFReadRGBAImage(tif,nx,ny,raster,0); + switch (spectrum) { + case 1 : + cimg_forXY(*this,x,y) + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); + break; + case 3 : + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); + } + break; + case 4 : + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]); + } + break; + } + _TIFFfree(raster); + } else { // Other cases + uint16 config; + TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); + if (TIFFIsTiled(tif)) { + uint32 tw = 1, th = 1; + TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); + TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + } + } else { + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + } + } + } + return *this; + } +#endif + + //! Load image from a MINC2 file. + /** + \param filename Filename, as a C-string. + **/ + // (Original code by Haz-Edine Assemlal). + CImg& load_minc2(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_minc2(): Specified filename is (null).", + cimg_instance); +#ifndef cimg_use_minc2 + return load_other(filename); +#else + minc::minc_1_reader rdr; + rdr.open(filename); + assign(rdr.ndim(1)?rdr.ndim(1):1, + rdr.ndim(2)?rdr.ndim(2):1, + rdr.ndim(3)?rdr.ndim(3):1, + rdr.ndim(4)?rdr.ndim(4):1); + if (pixel_type()==cimg::type::string()) + rdr.setup_read_byte(); + else if (pixel_type()==cimg::type::string()) + rdr.setup_read_int(); + else if (pixel_type()==cimg::type::string()) + rdr.setup_read_double(); + else + rdr.setup_read_float(); + minc::load_standard_volume(rdr,this->_data); + return *this; +#endif + } + + //! Load image from a MINC2 file \newinstance. + static CImg get_load_minc2(const char *const filename) { + return CImg().load_analyze(filename); + } + + //! Load image from an ANALYZE7.5/NIFTI file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_analyze(const char *const filename, float *const voxel_size=0) { + return _load_analyze(0,filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(const char *const filename, float *const voxel_size=0) { + return CImg().load_analyze(filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \overloading. + CImg& load_analyze(std::FILE *const file, float *const voxel_size=0) { + return _load_analyze(file,0,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(std::FILE *const file, float *const voxel_size=0) { + return CImg().load_analyze(file,voxel_size); + } + + CImg& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_analyze(): Specified filename is (null).", + cimg_instance); + + std::FILE *nfile_header = 0, *nfile = 0; + if (!file) { + CImg body(1024); + const char *const ext = cimg::split_filename(filename,body); + if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file + nfile_header = cimg::fopen(filename,"rb"); + cimg_sprintf(body._data + std::strlen(body),".img"); + nfile = cimg::fopen(body,"rb"); + } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file + nfile = cimg::fopen(filename,"rb"); + cimg_sprintf(body._data + std::strlen(body),".hdr"); + nfile_header = cimg::fopen(body,"rb"); + } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file + } else nfile_header = nfile = file; // File is a Niftii file + if (!nfile || !nfile_header) + throw CImgIOException(_cimg_instance + "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + // Read header. + bool endian = false; + unsigned int header_size; + cimg::fread(&header_size,1,nfile_header); + if (!header_size) + throw CImgIOException(_cimg_instance + "load_analyze(): Invalid zero-size header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } + + unsigned char *const header = new unsigned char[header_size]; + cimg::fread(header + 4,header_size - 4,nfile_header); + if (!file && nfile_header!=nfile) cimg::fclose(nfile_header); + if (endian) { + cimg::invert_endianness((short*)(header + 40),5); + cimg::invert_endianness((short*)(header + 70),1); + cimg::invert_endianness((short*)(header + 72),1); + cimg::invert_endianness((float*)(header + 76),4); + cimg::invert_endianness((float*)(header + 108),1); + cimg::invert_endianness((float*)(header + 112),1); + } + + if (nfile_header==nfile) { + const unsigned int vox_offset = (unsigned int)*(float*)(header + 108); + std::fseek(nfile,vox_offset,SEEK_SET); + } + + unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1; + if (!dim[0]) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with zero dimensions.", + cimg_instance, + filename?filename:"(FILE*)"); + + if (dim[0]>4) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.", + cimg_instance, + filename?filename:"(FILE*)",dim[0]); + + if (dim[0]>=1) dimx = dim[1]; + if (dim[0]>=2) dimy = dim[2]; + if (dim[0]>=3) dimz = dim[3]; + if (dim[0]>=4) dimv = dim[4]; + float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1; + const unsigned short datatype = *(unsigned short*)(header + 70); + if (voxel_size) { + const float *vsize = (float*)(header + 76); + voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3]; + } + delete[] header; + + // Read pixel data. + assign(dimx,dimy,dimz,dimv); + const size_t pdim = (size_t)dimx*dimy*dimz*dimv; + switch (datatype) { + case 2 : { + unsigned char *const buffer = new unsigned char[pdim]; + cimg::fread(buffer,pdim,nfile); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 4 : { + short *const buffer = new short[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 8 : { + int *const buffer = new int[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 16 : { + float *const buffer = new float[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 64 : { + double *const buffer = new double[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_analyze(): Unable to load datatype %d in file '%s'", + cimg_instance, + datatype,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a .cimg[z] file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(const char *const filename, const char axis='z', const float align=0) { + return CImg().load_cimg(filename,axis,align); + } + + //! Load image from a .cimg[z] file \overloading. + CImg& load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + return CImg().load_cimg(file,axis,align); + } + + //! Load sub-images of a .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Starting frame. + \param n1 Ending frame (~0U for max). + \param x0 X-coordinate of the starting sub-image vertex. + \param y0 Y-coordinate of the starting sub-image vertex. + \param z0 Z-coordinate of the starting sub-image vertex. + \param c0 C-coordinate of the starting sub-image vertex. + \param x1 X-coordinate of the ending sub-image vertex (~0U for max). + \param y1 Y-coordinate of the ending sub-image vertex (~0U for max). + \param z1 Z-coordinate of the ending sub-image vertex (~0U for max). + \param c1 C-coordinate of the ending sub-image vertex (~0U for max). + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load sub-images of a .cimg file \overloading. + CImg& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load image from an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_inr(const char *const filename, float *const voxel_size=0) { + return _load_inr(0,filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(const char *const filename, float *const voxel_size=0) { + return CImg().load_inr(filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \overloading. + CImg& load_inr(std::FILE *const file, float *const voxel_size=0) { + return _load_inr(file,0,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(std::FILE *const file, float *voxel_size=0) { + return CImg().load_inr(file,voxel_size); + } + + static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { + CImg item(1024), tmp1(64), tmp2(64); + *item = *tmp1 = *tmp2 = 0; + out[0] = std::fscanf(file,"%63s",item._data); + out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; + if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) + throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.", + pixel_type()); + + while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) { + cimg_sscanf(item," XDIM%*[^0-9]%d",out); + cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1); + cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2); + cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3); + cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6); + if (voxel_size) { + cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size); + cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); + cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); + } + if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { + case 0 : break; + case 2 : + out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; + std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough + case 1 : + if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; + if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; + if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; + if (out[4]>=0) break; // fallthrough + default : + throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", + pixel_type(), + tmp2._data); + } + } + if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) + throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.", + pixel_type(), + out[0],out[1],out[2],out[3]); + if (out[4]<0 || out[5]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.", + pixel_type()); + if (out[6]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.", + pixel_type()); + if (out[7]<0) + throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.", + pixel_type()); + } + + CImg& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) { +#define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ + if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ + Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \ + cimg_forYZ(*this,y,z) { \ + cimg::fread(val,fopt[0]*fopt[3],nfile); \ + if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \ + xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \ + } \ + delete[] val; \ + loaded = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_inr(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + int fopt[8], endian = cimg::endianness()?1:0; + bool loaded = false; + if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1; + _load_inr_header(nfile,fopt,voxel_size); + assign(fopt[0],fopt[1],fopt[2],fopt[3]); + _cimg_load_inr_case(0,0,8,unsigned char); + _cimg_load_inr_case(0,1,8,char); + _cimg_load_inr_case(0,0,16,unsigned short); + _cimg_load_inr_case(0,1,16,short); + _cimg_load_inr_case(0,0,32,unsigned int); + _cimg_load_inr_case(0,1,32,int); + _cimg_load_inr_case(1,0,32,float); + _cimg_load_inr_case(1,1,32,float); + _cimg_load_inr_case(1,0,64,double); + _cimg_load_inr_case(1,1,64,double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_inr(): Unknown pixel type defined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a EXR file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_exr(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_exr(): Specified filename is (null).", + cimg_instance); +#if defined(cimg_use_openexr) + Imf::RgbaInputFile file(filename); + Imath::Box2i dw = file.dataWindow(); + const int + inwidth = dw.max.x - dw.min.x + 1, + inheight = dw.max.y - dw.min.y + 1; + Imf::Array2D pixels; + pixels.resizeErase(inheight,inwidth); + file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth); + file.readPixels(dw.min.y, dw.max.y); + assign(inwidth,inheight,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + cimg_forXY(*this,x,y) { + *(ptr_r++) = (T)pixels[y][x].r; + *(ptr_g++) = (T)pixels[y][x].g; + *(ptr_b++) = (T)pixels[y][x].b; + *(ptr_a++) = (T)pixels[y][x].a; + } + return *this; +#elif defined(cimg_use_tinyexr) + float *res; + const char *err = 0; + int width = 0, height = 0; + const int ret = LoadEXR(&res,&width,&height,filename,&err); + if (ret) throw CImgIOException(_cimg_instance + "load_exr(): Unable to load EXR file '%s'.", + cimg_instance,filename); + CImg(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this); + std::free(res); + return *this; +#else + return load_other(filename); +#endif + } + + //! Load image from a EXR file \newinstance. + static CImg get_load_exr(const char *const filename) { + return CImg().load_exr(filename); + } + + //! Load image from a PANDORE-5 file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pandore(const char *const filename) { + return _load_pandore(0,filename); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(const char *const filename) { + return CImg().load_pandore(filename); + } + + //! Load image from a PANDORE-5 file \overloading. + CImg& load_pandore(std::FILE *const file) { + return _load_pandore(file,0); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(std::FILE *const file) { + return CImg().load_pandore(file); + } + + CImg& _load_pandore(std::FILE *const file, const char *const filename) { +#define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ + cimg::fread(dims,nbdim,nfile); \ + if (endian) cimg::invert_endianness(dims,nbdim); \ + assign(nwidth,nheight,ndepth,ndim); \ + const size_t siz = size(); \ + stype *buffer = new stype[siz]; \ + cimg::fread(buffer,siz,nfile); \ + if (endian) cimg::invert_endianness(buffer,siz); \ + T *ptrd = _data; \ + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ + buffer-=siz; \ + delete[] buffer + +#define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \ + if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \ + else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \ + else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \ + else throw CImgIOException(_cimg_instance \ + "load_pandore(): Unknown pixel datatype in file '%s'.", \ + cimg_instance, \ + filename?filename:"(FILE*)"); } + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pandore(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg header(32); + cimg::fread(header._data,12,nfile); + if (cimg::strncasecmp("PANDORE",header,7)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): PANDORE header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + unsigned int imageid, dims[8] = { 0 }; + int ptbuf[4] = { 0 }; + cimg::fread(&imageid,1,nfile); + const bool endian = imageid>255; + if (endian) cimg::invert_endianness(imageid); + cimg::fread(header._data,20,nfile); + + switch (imageid) { + case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break; + case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break; + case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break; + case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break; + case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break; + case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break; + case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break; + case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break; + case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break; + case 11 : { // Region 1D + cimg::fread(dims,3,nfile); + if (endian) cimg::invert_endianness(dims,3); + assign(dims[1],1,1,1); + const unsigned siz = size(); + if (dims[2]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[2]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 12 : { // Region 2D + cimg::fread(dims,4,nfile); + if (endian) cimg::invert_endianness(dims,4); + assign(dims[2],dims[1],1,1); + const size_t siz = size(); + if (dims[3]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[3]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 13 : { // Region 3D + cimg::fread(dims,5,nfile); + if (endian) cimg::invert_endianness(dims,5); + assign(dims[3],dims[2],dims[1],1); + const size_t siz = size(); + if (dims[4]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[4]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break; + case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break; + case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break; + case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break; + case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; + case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; + case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break; + case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break; + case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break; + case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; + case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break; + case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break; + case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break; + case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break; + case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); + break; + case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break; + case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); + break; + case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; + case 34 : { // Points 1D + cimg::fread(ptbuf,1,nfile); + if (endian) cimg::invert_endianness(ptbuf,1); + assign(1); (*this)(0) = (T)ptbuf[0]; + } break; + case 35 : { // Points 2D + cimg::fread(ptbuf,2,nfile); + if (endian) cimg::invert_endianness(ptbuf,2); + assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; + } break; + case 36 : { // Points 3D + cimg::fread(ptbuf,3,nfile); + if (endian) cimg::invert_endianness(ptbuf,3); + assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): Unable to load data with ID_type %u in file '%s'.", + cimg_instance, + imageid,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PAR-REC (Philips) file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_parrec(const char *const filename, const char axis='c', const float align=0) { + CImgList list; + list.load_parrec(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a PAR-REC (Philips) file \newinstance. + static CImg get_load_parrec(const char *const filename, const char axis='c', const float align=0) { + return CImg().load_parrec(filename,axis,align); + } + + //! Load image from a raw binary file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the image buffer. + \param size_y Height of the image buffer. + \param size_z Depth of the image buffer. + \param size_c Spectrum of the image buffer. + \param is_multiplexed Tells if the image values are multiplexed along the C-axis. + \param invert_endianness Tells if the endianness of the image buffer must be inverted. + \param offset Starting offset of the read in the specified file. + **/ + CImg& load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return CImg().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \overloading. + CImg& load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return CImg().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + CImg& _load_raw(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const bool is_multiplexed, const bool invert_endianness, + const ulongT offset) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename is (null).", + cimg_instance); + if (cimg::is_directory(filename)) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename '%s' is a directory.", + cimg_instance,filename); + const bool is_bool = pixel_type()==cimg::type::string(); + ulongT siz = (ulongT)size_x*size_y*size_z*size_c; + unsigned int + _size_x = size_x, + _size_y = size_y, + _size_z = size_z, + _size_c = size_c; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + if (!siz) { // Retrieve file size + const longT fpos = cimg::ftell(nfile); + if (fpos<0) throw CImgArgumentException(_cimg_instance + "load_raw(): Cannot determine size of input file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + cimg::fseek(nfile,0,SEEK_END); + siz = (ulongT)cimg::ftell(nfile); + if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; } + else _size_y = (unsigned int)(siz*8); + _size_x = _size_z = _size_c = 1; + cimg::fseek(nfile,fpos,SEEK_SET); + } + cimg::fseek(nfile,(longT)offset,SEEK_SET); + assign(_size_x,_size_y,_size_z,_size_c,0); + + if (is_bool) { // Boolean data (bitwise) + unsigned char *const buf = new unsigned char[siz]; + cimg::fread(buf,siz,nfile); + _uchar2bool(buf,siz,is_multiplexed); + delete[] buf; + } else { // Non-boolean data + if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed + cimg::fread(_data,siz,nfile); + if (invert_endianness) cimg::invert_endianness(_data,siz); + } else if (siz) { // Multiplexed + CImg buf(1,1,1,_size_c); + cimg_forXYZ(*this,x,y,z) { + cimg::fread(buf._data,_size_c,nfile); + if (invert_endianness) cimg::invert_endianness(buf._data,_size_c); + set_vector_at(buf,x,y,z); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image sequence from a YUV file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the frames. + \param size_y Height of the frames. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param yuv2rgb Tells if the YUV to RGB transform must be applied. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + **/ + CImg& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load image sequence from a YUV file \overloading. + CImg& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load 3D object from a .OFF file. + /** + \param[out] primitives Primitives data of the 3D object. + \param[out] colors Colors data of the 3D object. + \param filename Filename, as a C-string. + **/ + template + CImg& load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return _load_off(primitives,colors,0,filename); + } + + //! Load 3D object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return CImg().load_off(primitives,colors,filename); + } + + //! Load 3D object from a .OFF file \overloading. + template + CImg& load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return _load_off(primitives,colors,file,0); + } + + //! Load 3D object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return CImg().load_off(primitives,colors,file); + } + + template + CImg& _load_off(CImgList& primitives, CImgList& colors, + std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_off(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; + CImg line(256); *line = 0; + int err; + + // Skip comments, and read magic string OFF + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): OFF header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Invalid number of vertices or primitives specified in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read points data + assign(nb_points,3); + float X = 0, Y = 0, Z = 0; + cimg_forX(*this,l) { + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Failed to read vertex %u/%u in file '%s'.", + cimg_instance, + l + 1,nb_points,filename?filename:"(FILE*)"); + } + (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; + } + + // Read primitive data + primitives.assign(); + colors.assign(); + bool stop_flag = false; + while (!stop_flag) { + float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; + unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; + *line = 0; + if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true; + else { + ++nb_read; + switch (prim) { + case 1 : { + if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 2 : { + if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 3 : { + if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 4 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 5 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 6 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 7 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i4,i3,i1).move_to(primitives); + CImg::vector(i0,i6,i5,i4).move_to(primitives); + CImg::vector(i3,i2,i1).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + case 8 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line._data))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + CImg::vector(i0,i7,i6,i5).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + default : + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.", + cimg_instance, + nb_read,nb_primitives,prim,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } + } + } + if (!file) cimg::fclose(nfile); + if (primitives._width!=nb_primitives) + cimg::warn(_cimg_instance + "load_off(): Only %u/%u primitives read from file '%s'.", + cimg_instance, + primitives._width,nb_primitives,filename?filename:"(FILE*)"); + return *this; + } + + //! Load image sequence from a video file, using OpenCV library. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param axis Alignment axis. + \param align Appending alignment. + **/ + CImg& load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, + const char axis='z', const float align=0) { + return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this); + } + + //! Load image sequence from a video file, using OpenCV library \newinstance. + static CImg get_load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, + const char axis='z', const float align=0) { + return CImgList().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align); + } + + //! Load image sequence using custom's external tool 'custom'. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_custom_external(const char *const filename, const char axis='z', const float align=0) { + return get_load_custom_external(filename,axis,align).move_to(*this); + } + + //! Load image sequence using custom's external tool 'custom' \newinstance. + static CImg get_load_custom_external(const char *const filename, const char axis='z', const float align=0) { + return CImgList().load_custom_external(filename).get_append(axis,align); + } + + //! Load gif file, using Imagemagick or GraphicsMagicks's external tools. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return get_load_gif_external(filename,axis,align).move_to(*this); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance. + static CImg get_load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return CImgList().load_gif_external(filename).get_append(axis,align); + } + + //! Load image from a HEIC file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_heif(const char *const filename) { + return _load_heif(filename); + } + + //! Load image from a HEIC file \newinstance. + static CImg get_load_heif(const char *const filename) { + return CImg().load_heif(filename); + } + + CImg& _load_heif(const char *const filename) { +#ifndef cimg_use_heif + return load_other(filename); +#else + try { + heif::Context ctx; + ctx.read_from_file(filename); + + heif::ImageHandle handle = ctx.get_primary_image_handle(); + const heif::Image image = + handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA: + heif_chroma_interleaved_RGB); + const int + W = image.get_width(heif_channel_interleaved), + H = image.get_height(heif_channel_interleaved), + S = handle.has_alpha_channel()?4:3; + assign(W,H,1,S); + + int stride; + const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride); + T *ptr_r = _data, *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = S>3?data(0,0,0,3):0; + cimg_forY(*this,y) { + const unsigned char *ptrs = buffer + y*stride; + if (ptr_a) cimg_forX(*this,x) { // RGBA + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + else cimg_forX(*this,x) { // RGB + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } catch (const heif::Error& e) { + throw CImgInstanceException(_cimg_instance + "load_heif(): Unable to decode image: %s", + cimg_instance, + e.get_message().c_str()); + } catch (...) { + throw; + } + return *this; +#endif + } + + //! Load image using GraphicsMagick's external tool 'gm'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_graphicsmagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + if (!cimg::system("which gm")) { + cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-", + cimg::graphicsmagick_path(), + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' " + "with external command 'gm'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"", + cimg::graphicsmagick_path(), + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::graphicsmagick_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", + cimg_instance, + filename); + + } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else + load_pnm(filename_tmp); +#endif + std::remove(filename_tmp); + return *this; + } + + //! Load image using GraphicsMagick's external tool 'gm' \newinstance. + static CImg get_load_graphicsmagick_external(const char *const filename) { + return CImg().load_graphicsmagick_external(filename); + } + + //! Load gzipped image file, using external tool 'gunzip'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimg_instance + "load_gzip_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + const char + *const ext = cimg::split_filename(filename,body), + *const ext2 = cimg::split_filename(body,0); + + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load gzipped image file, using external tool 'gunzip' \newinstance. + static CImg get_load_gzip_external(const char *const filename) { + return CImg().load_gzip_external(filename); + } + + //! Load image using ImageMagick's external tool 'convert'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_imagemagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_imagemagick_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + if (!cimg::system("which convert")) { + cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::imagemagick_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + + } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else + load_pnm(filename_tmp); +#endif + std::remove(filename_tmp); + return *this; + } + + //! Load image using ImageMagick's external tool 'convert' \newinstance. + static CImg get_load_imagemagick_external(const char *const filename) { + return CImg().load_imagemagick_external(filename); + } + + //! Load image from a DICOM file, using Medcon's external tool 'medcon'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_medcon_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_medcon_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + cimg::fclose(cimg::fopen(filename,"r")); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::medcon_path()); + cimg::split_filename(filename_tmp,body); + + cimg_snprintf(command,command._width,"%s.hdr",body._data); + file = cimg::std_fopen(command,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"m000-%s.hdr",body._data); + file = cimg::std_fopen(command,"rb"); + if (!file) { + throw CImgIOException(_cimg_instance + "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + load_analyze(command); + std::remove(command); + cimg::split_filename(command,body); + cimg_snprintf(command,command._width,"%s.img",body._data); + std::remove(command); + return *this; + } + + //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance. + static CImg get_load_medcon_external(const char *const filename) { + return CImg().load_medcon_external(filename); + } + + //! Load image from a .pdf file. + /** + \param filename Filename, as a C-string. + \param resolution Image resolution. + **/ + CImg& load_pdf_external(const char *const filename, const unsigned int resolution=400) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_pdf_external(): Specified filename is (null).", + cimg_instance); + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"", + resolution,s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_pdf_external(): Failed to load file '%s' with external command 'gs'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"", + CImg::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data()); + cimg::system(command,"gs"); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_pdf_external(): Failed to load file '%s' with external command 'gs'.", + cimg_instance, + filename); + } else cimg::fclose(file); + load_pnm(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load image from a .pdf file \newinstance. + static CImg get_load_pdf_external(const char *const filename, const unsigned int resolution=400) { + return CImg().load_pdf_external(filename,resolution); + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_dcraw_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_dcraw_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"", + cimg::dcraw_path(),s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"", + cimg::dcraw_path(),s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::dcraw_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load_pnm(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance. + static CImg get_load_dcraw_external(const char *const filename) { + return CImg().load_dcraw_external(filename); + } + +#ifdef cimg_use_opencv + + // Convert a continuous cv::Mat to a CImg. + static CImg _cvmat2cimg(const cv::Mat &src) { + if (src.channels()==1) return CImg(src.ptr(),src.cols,src.rows,1,1); + else if (src.channels()==3) { // BGR + CImg res(src.cols,src.rows,1,src.channels()); + const unsigned char *ptrs = src.ptr(); + unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2); + cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); } + return res; + } + return CImg(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx"); + } + + // Convert a CImg to a cv::Mat. + cv::Mat _cimg2cvmat() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Instance image is empty.", + cimg_instance); + if (_spectrum==2) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').", + cimg_instance); + if (_depth!=1) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Invalid number of slices (should be '1').", + cimg_instance); + int mat_type = -1; + if (pixel_type()==cimg::type::string()) mat_type = CV_8UC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_8SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_16UC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_16SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_32SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_32FC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_64FC1; + if (mat_type<0) + throw CImgInstanceException(_cimg_instance + "_cvmat2cimg() : pixel type '%s' is not supported.", + cimg_instance,pixel_type()); + cv::Mat res; + std::vector channels(_spectrum); + if (_spectrum>1) { + cimg_forC(*this,c) + channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c)); + cv::merge(channels,res); + } else res = cv::Mat(_height,_width,mat_type,_data).clone(); + return res; + } + +#endif + + //! Load image from a camera stream, using OpenCV. + /** + \param index Index of the camera to capture images from (from 0 to 63). + \param capture_width Width of the desired image ('0' stands for default value). + \param capture_height Height of the desired image ('0' stands for default value). + \param skip_frames Number of frames to skip before the capture. + \param release_camera Tells if the camera resource must be released at the end of the method. + **/ + CImg& load_camera(const unsigned int camera_index=0, + const unsigned int capture_width=0, const unsigned int capture_height=0, + const unsigned int skip_frames=0, const bool release_camera=true) { +#ifdef cimg_use_opencv + if (camera_index>=64) + throw CImgArgumentException(_cimg_instance + "load_camera(): Invalid request for camera #%u " + "(no more than 100 cameras can be managed simultaneously).", + cimg_instance, + camera_index); + static cv::VideoCapture *captures[64] = { 0 }; + static unsigned int captures_w[64], captures_h[64]; + if (release_camera) { + cimg::mutex(9); + if (captures[camera_index]) captures[camera_index]->release(); + delete captures[camera_index]; + captures[camera_index] = 0; + captures_w[camera_index] = captures_h[camera_index] = 0; + cimg::mutex(9,0); + return *this; + } + if (!captures[camera_index]) { + cimg::mutex(9); + captures[camera_index] = new cv::VideoCapture(camera_index); + captures_w[camera_index] = captures_h[camera_index] = 0; + if (!captures[camera_index]->isOpened()) { + delete captures[camera_index]; + captures[camera_index] = 0; + cimg::mutex(9,0); + throw CImgIOException(_cimg_instance + "load_camera(): Failed to initialize camera #%u.", + cimg_instance, + camera_index); + } + cimg::mutex(9,0); + } + cimg::mutex(9); + if (capture_width!=captures_w[camera_index]) { + captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width); + captures_w[camera_index] = capture_width; + } + if (capture_height!=captures_h[camera_index]) { + captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height); + captures_h[camera_index] = capture_height; + } + for (unsigned int i = 0; igrab(); + cv::Mat cvimg; + captures[camera_index]->read(cvimg); + if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this); + cimg::mutex(9,0); + return *this; +#else + cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height); + throw CImgIOException(_cimg_instance + "load_camera(): This function requires features from the OpenCV library " + "('-Dcimg_use_opencv' must be defined).", + cimg_instance); +#endif + } + + //! Load image from a camera stream, using OpenCV \newinstance. + static CImg get_load_camera(const unsigned int camera_index=0, + const unsigned int capture_width=0, const unsigned int capture_height=0, + const unsigned int skip_frames=0, const bool release_camera=true) { + return CImg().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera); + } + + //! Load image using various non-native ways. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_other(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_other(): Specified filename is (null).", + cimg_instance); + + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_magick(filename); } + catch (CImgException&) { + try { load_imagemagick_external(filename); } + catch (CImgException&) { + try { load_graphicsmagick_external(filename); } + catch (CImgException&) { + try { load_cimg(filename); } + catch (CImgException&) { + try { + cimg::fclose(cimg::fopen(filename,"rb")); + } catch (CImgException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_other(): Failed to open file '%s'.", + cimg_instance, + filename); + } + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_other(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load image using various non-native ways \newinstance. + static CImg get_load_other(const char *const filename) { + return CImg().load_other(filename); + } + + //@} + //--------------------------- + // + //! \name Data Output + //@{ + //--------------------------- + + //! Display information about the image data. + /** + \param title Name for the considered image. + \param display_stats Tells to compute and display image statistics. + **/ + const CImg& print(const char *const title=0, const bool display_stats=true) const { + + int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; + CImg st; + if (!is_empty() && display_stats) { + st = get_stats(); + xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; + xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; + } + + const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1, + mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1; + + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type()); + + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum, + (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) + std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared"); + else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared"); + + if (!is_empty()) cimg_foroff(*this,off) { + std::fprintf(cimg::output(),"%g",(double)_data[off]); + if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" "); + if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } + } + if (!is_empty() && display_stats) + std::fprintf(cimg::output(), + " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), " + "%scoords_max%s = (%u,%u,%u,%u).\n", + cimg::t_bold,cimg::t_normal,st[0], + cimg::t_bold,cimg::t_normal,st[1], + cimg::t_bold,cimg::t_normal,st[2], + cimg::t_bold,cimg::t_normal,std::sqrt(st[3]), + cimg::t_bold,cimg::t_normal,xm,ym,zm,vm, + cimg::t_bold,cimg::t_normal,xM,yM,zM,vM); + else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" "); + std::fflush(cimg::output()); + return *this; + } + + //! Display image into a CImgDisplay window. + /** + \param disp Display window. + **/ + const CImg& display(CImgDisplay& disp) const { + disp.display(*this); + return *this; + } + + //! Display image into a CImgDisplay window, in an interactive way. + /** + \param disp Display window. + \param display_info Tells if image information are displayed on the standard output. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _display(disp,0,display_info,XYZ,exit_on_anykey,false); + } + + //! Display image into an interactive window. + /** + \param title Window title + \param display_info Tells if image information are displayed on the standard output. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display(disp,title,display_info,XYZ,exit_on_anykey,false); + } + + const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info, + unsigned int *const XYZ, const bool exit_on_anykey, + const bool exit_on_singleclick) const { + unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; + int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, + old_mouse_x = -1, old_mouse_y = -1; + + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + else disp.set_title("%s",title); + } else if (title) disp.set_title("%s",title); + disp.show().flush(); + + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(dtitle); + + CImg zoom; + for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) { + if (reset_view) { + if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; } + else { + _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2; + _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2; + _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2; + } + x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1; + disp.resize(cimg_fitscreen(_width,_height,_depth),false); + oldw = disp._width; oldh = disp._height; + resize_disp = true; + reset_view = false; + } + if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) { + if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign(); + } else zoom = get_crop(x0,y0,z0,x1,y1,z1); + + const CImg& visu = zoom?zoom:*this; + const unsigned int + dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0, + tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U); + if (!is_empty() && !disp.is_fullscreen() && resize_disp) { + const float + ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh, + dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height()); + const unsigned int + imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM); + disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false); + resize_disp = false; + } + oldw = tw; oldh = th; + + bool + go_up = false, go_down = false, go_left = false, go_right = false, + go_inc = false, go_dec = false, go_in = false, go_out = false, + go_in_center = false; + + disp.set_title("%s",dtitle._data); + if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0); + if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0); + if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0); + + disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; + CImg selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true); + old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; + is_first_select = false; + + if (disp.wheel()) { + if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) && + (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) { + go_left = !(go_right = disp.wheel()>0); + } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + go_down = !(go_up = disp.wheel()>0); + } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + go_out = !(go_in = disp.wheel()>0); go_in_center = false; + } + disp.set_wheel(); + } + + const int + sx0 = selection(0), sy0 = selection(1), sz0 = selection(2), + sx1 = selection(3), sy1 = selection(4), sz1 = selection(5); + if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { + x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; + x0+=sx0; y0+=sy0; z0+=sz0; + if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) { + if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true; + } + resize_disp = true; + } else switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break; + case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { + // Special mode: play stack of frames + const unsigned int + w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)), + h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0)); + float frame_timing = 5; + bool is_stopped = false; + disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0; + for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) { + if (disp.is_resized()) disp.resize(false); + if (!timer) { + visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2])); + (++_XYZ[2])%=visu._depth; + } + if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U; + if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); } + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break; + case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break; + case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; + (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false). + toggle_fullscreen().set_key(key,false); key = 0; + } break; + } + frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing); + disp.wait(20); + } + const unsigned int + w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width, + h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height; + disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel(); + key = 0; + } break; + case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break; + case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break; + case cimg::keyPADSUB : go_out = true; key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break; + case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break; + case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break; + case cimg::keyPAD7 : go_up = go_left = true; key = 0; break; + case cimg::keyPAD9 : go_up = go_right = true; key = 0; break; + case cimg::keyPAD1 : go_down = go_left = true; key = 0; break; + case cimg::keyPAD3 : go_down = go_right = true; key = 0; break; + case cimg::keyPAGEUP : go_inc = true; key = 0; break; + case cimg::keyPAGEDOWN : go_dec = true; key = 0; break; + } + if (go_in) { + const int + mx = go_in_center?disp.width()/2:disp.mouse_x(), + my = go_in_center?disp.height()/2:disp.mouse_y(), + mX = mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my*(height() + (depth()>1?depth():0))/disp.height(); + int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2]; + if (mX=height()) { + X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); + } + if (mX>=width() && mY4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; } + if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; } + if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; } + } + if (go_out) { + const int + delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8, + ndelta_x = delta_x?delta_x:(_width>1), + ndelta_y = delta_y?delta_y:(_height>1), + ndelta_z = delta_z?delta_z:(_depth>1); + x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z; + x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; } + if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; } + if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; } + if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; } + if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; } + if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; } + const float + ratio = (float)(x1-x0)/(y1-y0), + ratiow = (float)disp._width/disp._height, + sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow)); + if (sub>0.01) resize_disp = true; + } + if (go_left) { + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); + if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + } + if (go_right) { + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); + if (x1+ndelta1); + if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; } + else { y1-=y0; y0 = 0; } + } + if (go_down) { + const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1); + if (y1+ndelta1); + if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; } + else { z1-=z0; z0 = 0; } + } + if (go_dec) { + const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1); + if (z1+ndelta + const CImg& display_object3d(CImgDisplay& disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + template + const CImg& _display_object3d(CImgDisplay& disp, const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering, + const int render_static, const int render_motion, + const bool is_double_sided, const float focale, + const float light_x, const float light_y, const float light_z, + const float specular_lightness, const float specular_shininess, + const bool display_axes, float *const pose_matrix, + const bool exit_on_anykey) const { + typedef typename cimg::superset::type tpfloat; + + // Check input arguments + if (is_empty()) { + CImg background; + if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128); + else background.assign(1,2,1,3,32,64,32,116,64,96); + if (disp) background.resize(disp.width(),disp.height(),1,-100,3); + else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + CImgDisplay::screen_height()/2,1),1,-100,3); + return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } else { if (disp) disp.resize(*this,false); } + CImg error_message(1024); + if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgArgumentException(_cimg_instance + "display_object3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); + if (vertices._width && !primitives) { + CImgList nprimitives(vertices._width,1,1,1,1); + cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l; + return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); + if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)", + pixel_type(),vertices._width,primitives._width); + } else if (title) disp.set_title("%s",title); + + // Init 3D objects and compute object statistics + CImg + pose, + rotated_vertices(vertices._width,3), + bbox_vertices, rotated_bbox_vertices, + axes_vertices, rotated_axes_vertices, + bbox_opacities, axes_opacities; + CImgList bbox_primitives, axes_primitives; + CImgList reverse_primitives; + CImgList bbox_colors, bbox_colors2, axes_colors; + unsigned int ns_width = 0, ns_height = 0; + int _is_double_sided = (int)is_double_sided; + bool ndisplay_axes = display_axes; + const CImg + background_color(1,1,1,_spectrum,0), + foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type::max(),255)); + float + Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1, + xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0, + ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0, + zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0; + const float delta = cimg::max(xM - xm,yM - ym,zM - zm); + + rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1, + xm,xM,xM,xm,xm,xM,xM,xm, + ym,ym,yM,yM,ym,ym,yM,yM, + zm,zm,zm,zm,zM,zM,zM,zM); + bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6); + bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]); + bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]); + bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f); + + rotated_axes_vertices = axes_vertices.assign(7,3,1,1, + 0,20,0,0,22,-6,-6, + 0,0,20,0,-6,22,-6, + 0,0,0,20,0,0,22); + axes_opacities.assign(3,1,1,1,1); + axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]); + axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); + + // Begin user interaction loop + CImg visu0(*this,false), visu; + CImg zbuffer(visu0.width(),visu0.height(),1,1,0); + bool init_pose = true, clicked = false, redraw = true; + unsigned int key = 0, font_size = 32; + int + x0 = 0, y0 = 0, x1 = 0, y1 = 0, + nrender_static = render_static, + nrender_motion = render_motion; + disp.show().flush(); + + while (!disp.is_closed() && !key) { + + // Init object pose + if (init_pose) { + const float + ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1, + dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2; + if (centering) + CImg(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose); + else CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose); + if (pose_matrix) { + CImg pose0(pose_matrix,4,3,1,1,false); + pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0); + pose0(3,3) = pose(3,3) = 1; + (pose0*pose).get_crop(0,0,3,2).move_to(pose); + Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15]; + } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; } + init_pose = false; + redraw = true; + } + + // Rotate and draw 3D object + if (redraw) { + const float + r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), + r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), + r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); + if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) { + const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2); + float + *const prv0 = rotated_vertices.data(), + *const prv1 = rotated_vertices.data(0,1), + *const prv2 = rotated_vertices.data(0,2); + cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024)) + cimg_forX(vertices,l) { + const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l]; + prv0[l] = r00*x + r10*y + r20*z + r30; + prv1[l] = r01*x + r11*y + r21*z + r31; + prv2[l] = r02*x + r12*y + r22*z + r32; + } + } + else cimg_forX(bbox_vertices,l) { + const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2); + rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30; + rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31; + rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32; + } + + // Draw objects + const bool render_with_zbuffer = !clicked && nrender_static>0; + visu = visu0; + if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0)) + visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale). + draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale); + else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg::empty(), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale, + width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1,sprite_scale); + // Draw axes + if (ndisplay_axes) { + const float + n = 1e-8f + cimg::hypot(r00,r01,r02), + _r00 = r00/n, _r10 = r10/n, _r20 = r20/n, + _r01 = r01/n, _r11 = r11/n, _r21 = r21/n, + _r02 = r01/n, _r12 = r12/n, _r22 = r22/n, + Xaxes = 25, Yaxes = visu._height - 38.f; + cimg_forX(axes_vertices,l) { + const float + x = axes_vertices(l,0), + y = axes_vertices(l,1), + z = axes_vertices(l,2); + rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z; + rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z; + rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z; + } + axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f; + axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f; + axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f; + visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives, + axes_colors,axes_opacities,1,false,focale). + draw_text((int)(Xaxes + rotated_axes_vertices(4,0)), + (int)(Yaxes + rotated_axes_vertices(4,1)), + "X",axes_colors[0]._data,0,axes_opacities(0,0),13). + draw_text((int)(Xaxes + rotated_axes_vertices(5,0)), + (int)(Yaxes + rotated_axes_vertices(5,1)), + "Y",axes_colors[1]._data,0,axes_opacities(1,0),13). + draw_text((int)(Xaxes + rotated_axes_vertices(6,0)), + (int)(Yaxes + rotated_axes_vertices(6,1)), + "Z",axes_colors[2]._data,0,axes_opacities(2,0),13); + } + visu.display(disp); + if (!clicked || nrender_motion==nrender_static) redraw = false; + } + + // Handle user interaction + if (!redraw) disp.wait(); + if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) { + redraw = true; + if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; } + else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); } + const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT(); + if (disp.button()&1 && !is_keyCTRL) { + const float + R = 0.45f*std::min(disp.width(),disp.height()), + R2 = R*R, + u0 = (float)(x0 - disp.width()/2), + v0 = (float)(y0 - disp.height()/2), + u1 = (float)(x1 - disp.width()/2), + v1 = (float)(y1 - disp.height()/2), + n0 = cimg::hypot(u0,v0), + n1 = cimg::hypot(u1,v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), + u = nv0*nw1 - nw0*nv1, + v = nw0*nu1 - nu0*nw1, + w = nv0*nu1 - nu0*nv1, + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + (CImg::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose); + x0 = x1; y0 = y1; + } + if (disp.button()&2 && !is_keyCTRL) { + if (focale>0) Zoff-=(y0 - y1)*focale/400; + else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; } + x0 = x1; y0 = y1; + } + if (disp.wheel()) { + if (focale>0) Zoff-=disp.wheel()*focale/20; + else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; } + disp.set_wheel(); + } + if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) { + Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; + } + if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) { + init_pose = true; disp.set_button(); x0 = x1; y0 = y1; + pose = CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0); + } + } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } + + CImg filename(32); + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + if (!ns_width || !ns_height || + ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) { + ns_width = disp.screen_width()*3U/4; + ns_height = disp.screen_height()*3U/4; + } + if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false); + else { + ns_width = disp._width; ns_height = disp._height; + disp.resize(disp.screen_width(),disp.screen_height(),false); + } + disp.toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + // Switch single/double-sided primitives. + if (--_is_double_sided==-2) _is_double_sided = 1; + if (_is_double_sided>=0) reverse_primitives.assign(); + else primitives.get_reverse_object3d().move_to(reverse_primitives); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer + if (zbuffer) zbuffer.assign(); + else zbuffer.assign(visu0.width(),visu0.height(),1,1,0); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes + ndisplay_axes = !ndisplay_axes; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points + nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines + nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat + nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded + nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + // Set rendering mode to gouraud-shaded. + nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded + nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp); + visu.save(filename); + (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); + vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file + static unsigned int snap_number = 0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); + vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities). + save(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + +#ifdef cimg_use_board + case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1, + sprite_scale); + board.saveEPS(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1, + sprite_scale); + board.saveSVG(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; +#endif + } + if (disp.is_resized()) { + disp.resize(false); visu0 = get_resize(disp,1); + if (zbuffer) zbuffer.assign(disp.width(),disp.height()); + redraw = true; + } + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + if (pose_matrix) { + std::memcpy(pose_matrix,pose._data,12*sizeof(float)); + pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale; + } + disp.set_button().set_key(key); + return *this; + } + + //! Display 1D graph in an interactive window. + /** + \param disp Display window. + \param plot_type Plot type. Can be { 0=points | 1=segments | 2=splines | 3=bars }. + \param vertex_type Vertex type. + \param labelx Title for the horizontal axis, as a C-string. + \param xmin Minimum value along the X-axis. + \param xmax Maximum value along the X-axis. + \param labely Title for the vertical axis, as a C-string. + \param ymin Minimum value along the X-axis. + \param ymax Maximum value along the X-axis. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); + } + + //! Display 1D graph in an interactive window \overloading. + const CImg& display_graph(const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); + } + + const CImg& _display_graph(CImgDisplay &disp, const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "display_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). + set_title(title?"%s":"CImg<%s>",title?title:pixel_type()); + const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1); + const unsigned int old_normalization = disp.normalization(); + disp.show().flush()._normalization = 0; + + double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; + if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; } + int x0 = 0, x1 = width()*height()*depth() - 1, key = 0; + + for (bool reset_view = true; !key && !disp.is_closed(); ) { + if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; } + CImg zoom(x1 - x0 + 1,1,1,spectrum()); + cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true); + if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; } + if (y0==y1) { --y0; ++y1; } + + const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, + labelx, + nxmin + x0*(nxmax - nxmin)/siz1, + nxmin + x1*(nxmax - nxmin)/siz1, + labely,y0,y1,true); + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + if (selection[0]>=0) { + if (selection[2]<0) reset_view = true; + else { + x1 = x0 + selection[2]; x0+=selection[0]; + if (selection[1]>=0 && selection[3]>=0) { + y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32); + y1-=selection[1]*(y1 - y0)/(disp.height() - 32); + } + } + } else { + bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false; + switch (key = (int)disp.key()) { + case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break; + case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break; + case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break; + case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); + break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); + break; + case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break; + case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break; + case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break; + case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break; + } + if (disp.wheel()) { + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0); + else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); + else go_out = !(go_in = disp.wheel()>0); + key = 0; + } + + if (go_in) { + const int + xsiz = x1 - x0, + mx = (mouse_x - 16)*xsiz/(disp.width() - 32), + cx = x0 + cimg::cut(mx,0,xsiz); + if (x1 - x0>4) { + x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8; + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + const double + ysiz = y1 - y0, + my = (mouse_y - 16)*ysiz/(disp.height() - 32), + cy = y1 - cimg::cut(my,0.,ysiz); + y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8; + } else y0 = y1 = 0; + } + } + if (go_out) { + if (x0>0 || x1<(int)siz1) { + const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1); + const double ndelta_y = (y1 - y0)/8; + x0-=ndelta_x; x1+=ndelta_x; + y0-=ndelta_y; y1+=ndelta_y; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; } + if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; } + } + } + if (go_left) { + const int delta = (x1 - x0)/5, ndelta = delta?delta:1; + if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + go_left = false; + } + if (go_right) { + const int delta = (x1 - x0)/5, ndelta = delta?delta:1; + if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; } + else { x0+=(siz1 - x1); x1 = (int)siz1; } + go_right = false; + } + if (go_up) { + const double delta = (y1 - y0)/10, ndelta = delta?delta:1; + y0+=ndelta; y1+=ndelta; + go_up = false; + } + if (go_down) { + const double delta = (y1 - y0)/10, ndelta = delta?delta:1; + y0-=ndelta; y1-=ndelta; + go_down = false; + } + } + if (!exit_on_anykey && key && key!=(int)cimg::keyESC && + (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + key = 0; + } + } + disp._normalization = old_normalization; + return *this; + } + + //! Save image as a file. + /** + \param filename Filename, as a C-string. + \param number When positive, represents an index added to the filename. Otherwise, no number is added. + \param digits Number of digits used for adding the number to the filename. + \note + - The used file format is defined by the file extension in the filename \p filename. + - Parameter \p number can be used to add a 6-digit number to the filename before saving. + + **/ + const CImg& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save(): Specified filename is (null).", + cimg_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + CImg nfilename(1024); + const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename): + filename; + +#ifdef cimg_save_plugin + cimg_save_plugin(fn); +#endif +#ifdef cimg_save_plugin1 + cimg_save_plugin1(fn); +#endif +#ifdef cimg_save_plugin2 + cimg_save_plugin2(fn); +#endif +#ifdef cimg_save_plugin3 + cimg_save_plugin3(fn); +#endif +#ifdef cimg_save_plugin4 + cimg_save_plugin4(fn); +#endif +#ifdef cimg_save_plugin5 + cimg_save_plugin5(fn); +#endif +#ifdef cimg_save_plugin6 + cimg_save_plugin6(fn); +#endif +#ifdef cimg_save_plugin7 + cimg_save_plugin7(fn); +#endif +#ifdef cimg_save_plugin8 + cimg_save_plugin8(fn); +#endif + // Text formats + if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn); + else if (!cimg::strcasecmp(ext,"csv") || + !cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); + else if (!cimg::strcasecmp(ext,"cpp") || + !cimg::strcasecmp(ext,"hpp") || + !cimg::strcasecmp(ext,"h") || + !cimg::strcasecmp(ext,"c")) return save_cpp(fn); + + // 2D binary formats + else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn); + else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); + else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); + else if (!cimg::strcasecmp(ext,"png")) return save_png(fn); + else if (!cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); + else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn); + else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn); + else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); + + // 3D binary formats + else if (!*ext) { +#ifdef cimg_use_zlib + return save_cimg(fn,true); +#else + return save_cimg(fn,false); +#endif + } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); + else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); + else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn); + else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); + else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + + // Image sequences + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); + return save_other(fn); + } + + //! Save image as an ascii file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_ascii(const char *const filename) const { + return _save_ascii(0,filename); + } + + //! Save image as an Ascii file \overloading. + const CImg& save_ascii(std::FILE *const file) const { + return _save_ascii(file,0); + } + + const CImg& _save_ascii(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_ascii(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++)); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a .cpp source file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_cpp(const char *const filename) const { + return _save_cpp(0,filename); + } + + //! Save image as a .cpp source file \overloading. + const CImg& save_cpp(std::FILE *const file) const { + return _save_cpp(file,0); + } + + const CImg& _save_cpp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_cpp(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + CImg varname(1024); *varname = 0; + if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data); + if (!*varname) cimg_snprintf(varname,varname._width,"unnamed"); + std::fprintf(nfile, + "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" + "%s data_%s[] = { %s\n ", + varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data, + is_empty()?"};":""); + if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) { + std::fprintf(nfile,cimg::type::format(),cimg::type::format((*this)[off])); + if (off==siz) std::fprintf(nfile," };\n"); + else if (!((off + 1)%16)) std::fprintf(nfile,",\n "); + else std::fprintf(nfile,", "); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a DLM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_dlm(const char *const filename) const { + return _save_dlm(0,filename); + } + + //! Save image as a DLM file \overloading. + const CImg& save_dlm(std::FILE *const file) const { + return _save_dlm(file,0); + } + + const CImg& _save_dlm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_dlm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":","); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a BMP file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_bmp(const char *const filename) const { + return _save_bmp(0,filename); + } + + //! Save image as a BMP file \overloading. + const CImg& save_bmp(std::FILE *const file) const { + return _save_bmp(file,0); + } + + const CImg& _save_bmp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_bmp(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + CImg header(54,1,1,1,0); + unsigned char align_buf[4] = { 0 }; + const unsigned int + align = (4 - (3*_width)%4)%4, + buf_size = (3*_width + align)*height(), + file_size = 54 + buf_size; + header[0] = 'B'; header[1] = 'M'; + header[0x02] = file_size&0xFF; + header[0x03] = (file_size>>8)&0xFF; + header[0x04] = (file_size>>16)&0xFF; + header[0x05] = (file_size>>24)&0xFF; + header[0x0A] = 0x36; + header[0x0E] = 0x28; + header[0x12] = _width&0xFF; + header[0x13] = (_width>>8)&0xFF; + header[0x14] = (_width>>16)&0xFF; + header[0x15] = (_width>>24)&0xFF; + header[0x16] = _height&0xFF; + header[0x17] = (_height>>8)&0xFF; + header[0x18] = (_height>>16)&0xFF; + header[0x19] = (_height>>24)&0xFF; + header[0x1A] = 1; + header[0x1B] = 0; + header[0x1C] = 24; + header[0x1D] = 0; + header[0x22] = buf_size&0xFF; + header[0x23] = (buf_size>>8)&0xFF; + header[0x24] = (buf_size>>16)&0xFF; + header[0x25] = (buf_size>>24)&0xFF; + header[0x27] = 0x1; + header[0x2B] = 0x1; + cimg::fwrite(header._data,54,nfile); + + const T + *ptr_r = data(0,_height - 1,0,0), + *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0; + + switch (_spectrum) { + case 1 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + const unsigned char val = (unsigned char)*(ptr_r++); + std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; + } + } break; + case 2 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc(0,nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; + } + } break; + default : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc((unsigned char)(*(ptr_b++)),nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a JPEG file. + /** + \param filename Filename, as a C-string. + \param quality Image quality (in %) + **/ + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return _save_jpeg(0,filename,quality); + } + + //! Save image as a JPEG file \overloading. + const CImg& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { + return _save_jpeg(file,0,quality); + } + + const CImg& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_jpeg(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + +#ifndef cimg_use_jpeg + if (!file) return save_other(filename,quality); + else throw CImgIOException(_cimg_instance + "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.", + cimg_instance); +#else + unsigned int dimbuf = 0; + J_COLOR_SPACE colortype = JCS_RGB; + + switch (_spectrum) { + case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break; + case 2 : dimbuf = 3; colortype = JCS_RGB; break; + case 3 : dimbuf = 3; colortype = JCS_RGB; break; + default : dimbuf = 4; colortype = JCS_CMYK; break; + } + + // Call libjpeg functions + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + jpeg_stdio_dest(&cinfo,nfile); + cinfo.image_width = _width; + cinfo.image_height = _height; + cinfo.input_components = dimbuf; + cinfo.in_color_space = colortype; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); + jpeg_start_compress(&cinfo,TRUE); + + JSAMPROW row_pointer[1]; + CImg buffer(_width*dimbuf); + + while (cinfo.next_scanline& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_magick(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_magick + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_magick(): Instance is multispectral, only the three first channels will be " + "saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + Magick::Image image(Magick::Geometry(_width,_height),"black"); + image.type(Magick::TrueColorType); + image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8)); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = _spectrum>1?data(0,0,0,1):0, + *ptr_b = _spectrum>2?data(0,0,0,2):0; + Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height); + switch (_spectrum) { + case 1 : // Scalar images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++); + ++pixels; + } + break; + case 2 : // RG images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = 0; ++pixels; + } + break; + default : // RGB images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = (Magick::Quantum)*(ptr_b++); + ++pixels; + } + } + image.syncPixels(); + image.write(filename); + return *this; +#else + cimg::unused(bytes_per_pixel); + throw CImgIOException(_cimg_instance + "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + } + + //! Save image as a PNG file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible. + **/ + const CImg& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_png(0,filename,bytes_per_pixel); + } + + //! Save image as a PNG file \overloading. + const CImg& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_png(file,0,bytes_per_pixel); + } + + const CImg& _save_png(std::FILE *const file, const char *const filename, + const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_png(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + +#ifndef cimg_use_png + cimg::unused(bytes_per_pixel); + if (!file) return save_other(filename); + else throw CImgIOException(_cimg_instance + "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.", + cimg_instance); +#else + +#if defined __GNUC__ + const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); + volatile double stmin, stmax = (double)max_min(stmin); +#else + const char *nfilename = filename; + std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb"); + double stmin, stmax = (double)max_min(stmin); +#endif + + if (_depth>1) + cimg::warn(_cimg_instance + "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>4) + cimg::warn(_cimg_instance + "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + // Setup PNG structures for write + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, + user_warning_fn); + if (!png_ptr){ + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr,(png_infopp)0); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + + const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8); + + int color_type; + switch (spectrum()) { + case 1 : color_type = PNG_COLOR_TYPE_GRAY; break; + case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3 : color_type = PNG_COLOR_TYPE_RGB; break; + default : color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + const int interlace_type = PNG_INTERLACE_NONE; + const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; + const int filter_method = PNG_FILTER_TYPE_DEFAULT; + png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method); + png_write_info(png_ptr,info_ptr); + const int byte_depth = bit_depth>>3; + const int numChan = spectrum()>4?4:spectrum(); + const int pixel_bit_depth_flag = numChan * (bit_depth - 1); + + // Allocate Memory for Image Save and Fill pixel data + png_bytep *const imgData = new png_byte*[_height]; + for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width]; + const T *pC0 = data(0,0,0,0); + switch (pixel_bit_depth_flag) { + case 7 : { // Gray 8-bit + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++); + } + } break; + case 14 : { // Gray w/ Alpha 8-bit + const T *pC1 = data(0,0,0,1); + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) { + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + } + } + } break; + case 21 : { // RGB 8-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) { + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + *(ptrd++) = (unsigned char)*(pC2++); + } + } + } break; + case 28 : { // RGB x/ Alpha 8-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); + cimg_forY(*this,y){ + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x){ + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + *(ptrd++) = (unsigned char)*(pC2++); + *(ptrd++) = (unsigned char)*(pC3++); + } + } + } break; + case 15 : { // Gray 16-bit + cimg_forY(*this,y){ + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++); + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width); + } + } break; + case 30 : { // Gray w/ Alpha 16-bit + const T *pC1 = data(0,0,0,1); + cimg_forY(*this,y){ + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width); + } + } break; + case 45 : { // RGB 16-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); + cimg_forY(*this,y) { + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + *(ptrd++) = (unsigned short)*(pC2++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width); + } + } break; + case 60 : { // RGB w/ Alpha 16-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); + cimg_forY(*this,y) { + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + *(ptrd++) = (unsigned short)*(pC2++); + *(ptrd++) = (unsigned short)*(pC3++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width); + } + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_write_image(png_ptr,imgData); + png_write_end(png_ptr,info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + // Deallocate Image Write Memory + cimg_forY(*this,n) delete[] imgData[n]; + delete[] imgData; + + if (!file) cimg::fclose(nfile); + return *this; +#endif + } + + //! Save image as a PNM file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving. + **/ + const CImg& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(0,filename,bytes_per_pixel); + } + + //! Save image as a PNM file \overloading. + const CImg& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(file,0,bytes_per_pixel); + } + + const CImg& _save_pnm(std::FILE *const file, const char *const filename, + const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + stmin,stmax,filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL))); + + std::fprintf(nfile,"P%c\n%u %u\n%u\n", + (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535)); + + switch (_spectrum) { + case 1 : { // Scalar image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Binary PGM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + } break; + case 2 : { // RG image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = 0; + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } break; + default : { // RGB image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = (unsigned char)*(ptr_b++); + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = (unsigned short)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PNK file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pnk(const char *const filename) const { + return _save_pnk(0,filename); + } + + //! Save image as a PNK file \overloading. + const CImg& save_pnk(std::FILE *const file) const { + return _save_pnk(file,0); + } + + const CImg& _save_pnk(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnk(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T *ptr = data(0,0,0,0); + + if (!cimg::type::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file + _save_pnm(file,filename,0); + else if (!cimg::type::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D + std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else if (!cimg::type::is_float()) { // Save as P8: Binary int32-valued 3D + if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max()); + else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max()); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + int *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Save as P9: Binary float-valued 3D + if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max()); + else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max()); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PFM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pfm(const char *const filename) const { + get_mirror('y')._save_pfm(0,filename); + return *this; + } + + //! Save image as a PFM file \overloading. + const CImg& save_pfm(std::FILE *const file) const { + get_mirror('y')._save_pfm(file,0); + return *this; + } + + const CImg& _save_pfm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pfm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pfm(): image instance is multispectral, only the three first channels will be saved " + "in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); + + std::fprintf(nfile,"P%c\n%u %u\n1.0\n", + (_spectrum==1?'f':'F'),_width,_height); + + switch (_spectrum) { + case 1 : { // Scalar image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } break; + case 2 : { // RG image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const unsigned int N = std::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } break; + default : { // RGB image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const unsigned int N = std::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = (float)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a RGB file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_rgb(const char *const filename) const { + return _save_rgb(0,filename); + } + + //! Save image as a RGB file \overloading. + const CImg& save_rgb(std::FILE *const file) const { + return _save_rgb(file,0); + } + + const CImg& _save_rgb(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgb(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=3) + cimg::warn(_cimg_instance + "save_rgb(): image instance has not exactly 3 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT wh = (ulongT)_width*_height; + unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0; + switch (_spectrum) { + case 1 : { // Scalar image + for (ulongT k = 0; k& save_rgba(const char *const filename) const { + return _save_rgba(0,filename); + } + + //! Save image as a RGBA file \overloading. + const CImg& save_rgba(std::FILE *const file) const { + return _save_rgba(file,0); + } + + const CImg& _save_rgba(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgba(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=4) + cimg::warn(_cimg_instance + "save_rgba(): image instance has not exactly 4 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT wh = (ulongT)_width*_height; + unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0, + *ptr4 = _spectrum>3?data(0,0,0,3):0; + switch (_spectrum) { + case 1 : { // Scalar images + for (ulongT k = 0; k{ 0=None | 1=LZW | 2=JPEG }. + \param[out] voxel_size Voxel size, to be stored in the filename. + \param[out] description Description, to be stored in the filename. + \param use_bigtiff Allow to save big tiff files (>4Gb). + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tiff is not defined at compile time the + function uses CImg&save_other(const char*). + **/ + const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0, + + const float *const voxel_size=0, const char *const description=0, + const bool use_bigtiff=true) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_tiff(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_tiff + const bool + _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images + TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); + if (tif) { + cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description); + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "save_tiff(): Failed to open file '%s' for writing.", + cimg_instance, + filename); + return *this; +#else + cimg::unused(compression_type,voxel_size,description,use_bigtiff); + return save_other(filename); +#endif + } + +#ifdef cimg_use_tiff + +#define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \ + const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } + + // [internal] Save a plane into a tiff file + template + const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { + if (is_empty() || !tif || pixel_t) return *this; + const char *const filename = TIFFFileName(tif); + uint32 rowsperstrip = (uint32)-1; + uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; + if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; + else photometric = PHOTOMETRIC_MINISBLACK; + TIFFSetDirectory(tif,directory); + TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width); + TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height); + if (voxel_size) { + const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2]; + TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE); + TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx); + TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy); + CImg s_description(256); + cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz); + TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data()); + } + if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description); + TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); + TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); + if (cimg::type::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); + else if (cimg::type::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); + else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); + double valm, valM = max_min(valm); + TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm); + TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM); + TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); + TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); + TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG: + compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE); + rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); + TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); + TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); + TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); + + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + for (unsigned int row = 0; row<_height; row+=rowsperstrip) { + uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif,row,0); + tsize_t i = 0; + for (unsigned int rr = 0; rr& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { + _cimg_save_tiff("bool",unsigned char,compression_type); + _cimg_save_tiff("unsigned char",unsigned char,compression_type); + _cimg_save_tiff("char",char,compression_type); + _cimg_save_tiff("unsigned short",unsigned short,compression_type); + _cimg_save_tiff("short",short,compression_type); + _cimg_save_tiff("unsigned int",unsigned int,compression_type); + _cimg_save_tiff("int",int,compression_type); + _cimg_save_tiff("unsigned int64",unsigned int,compression_type); + _cimg_save_tiff("int64",int,compression_type); + _cimg_save_tiff("float",float,compression_type); + _cimg_save_tiff("double",float,compression_type); + const char *const filename = TIFFFileName(tif); + throw CImgInstanceException(_cimg_instance + "save_tiff(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + return *this; + } +#endif + + //! Save image as a MINC2 file. + /** + \param filename Filename, as a C-string. + \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from. + **/ + const CImg& save_minc2(const char *const filename, + const char *const imitate_file=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_minc2(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_minc2 + cimg::unused(imitate_file); + return save_other(filename); +#else + minc::minc_1_writer wtr; + if (imitate_file) + wtr.open(filename, imitate_file); + else { + minc::minc_info di; + if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X)); + if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y)); + if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z)); + if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME)); + wtr.open(filename,di,1,NC_FLOAT,0); + } + if (pixel_type()==cimg::type::string()) + wtr.setup_write_byte(); + else if (pixel_type()==cimg::type::string()) + wtr.setup_write_int(); + else if (pixel_type()==cimg::type::string()) + wtr.setup_write_double(); + else + wtr.setup_write_float(); + minc::save_standard_volume(wtr, this->_data); + return *this; +#endif + } + + //! Save image as an ANALYZE7.5 or NIFTI file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_analyze(const char *const filename, const float *const voxel_size=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_analyze(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + std::FILE *file; + CImg hname(1024), iname(1024); + const char *const ext = cimg::split_filename(filename); + short datatype = -1; + if (!*ext) { + cimg_snprintf(hname,hname._width,"%s.hdr",filename); + cimg_snprintf(iname,iname._width,"%s.img",filename); + } + if (!cimg::strncasecmp(ext,"hdr",3)) { + std::strcpy(hname,filename); + std::strncpy(iname,filename,iname._width - 1); + cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); + } + if (!cimg::strncasecmp(ext,"img",3)) { + std::strcpy(hname,filename); + std::strncpy(iname,filename,iname._width - 1); + cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); + } + if (!cimg::strncasecmp(ext,"nii",3)) { + std::strncpy(hname,filename,hname._width - 1); *iname = 0; + } + + CImg header(*iname?348:352,1,1,1,0); + int *const iheader = (int*)header._data; + *iheader = 348; + std::strcpy(header._data + 4,"CImg"); + std::strcpy(header._data + 14," "); + ((short*)&(header[36]))[0] = 4096; + ((char*)&(header[38]))[0] = 114; + ((short*)&(header[40]))[0] = 4; + ((short*)&(header[40]))[1] = (short)_width; + ((short*)&(header[40]))[2] = (short)_height; + ((short*)&(header[40]))[3] = (short)_depth; + ((short*)&(header[40]))[4] = (short)_spectrum; + if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; + if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; + if (datatype<0) + throw CImgIOException(_cimg_instance + "save_analyze(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename); + + ((short*)&(header[70]))[0] = datatype; + ((short*)&(header[72]))[0] = sizeof(T); + ((float*)&(header[108]))[0] = (float)(*iname?0:header.width()); + ((float*)&(header[112]))[0] = 1; + ((float*)&(header[76]))[0] = 0; + if (voxel_size) { + ((float*)&(header[76]))[1] = voxel_size[0]; + ((float*)&(header[76]))[2] = voxel_size[1]; + ((float*)&(header[76]))[3] = voxel_size[2]; + } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1; + file = cimg::fopen(hname,"wb"); + cimg::fwrite(header._data,header.width(),file); + if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } + cimg::fwrite(_data,size(),file); + cimg::fclose(file); + return *this; + } + + //! Save image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param is_compressed Tells if the file contains compressed image data. + **/ + const CImg& save_cimg(const char *const filename, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(filename,is_compressed); + return *this; + } + + //! Save image as a .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(file,is_compressed); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Index of the image inside the file. + \param x0 X-coordinate of the sub-image location. + \param y0 Y-coordinate of the sub-image location. + \param z0 Z-coordinate of the sub-image location. + \param c0 C-coordinate of the sub-image location. + **/ + const CImg& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(filename,n0,x0,y0,z0,c0); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,c0); + return *this; + } + + //! Save blank image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param dx Width of the image. + \param dy Height of the image. + \param dz Depth of the image. + \param dc Number of channels of the image. + \note + - All pixel values of the saved image are set to \c 0. + - Use this method to save large images without having to instantiate and allocate them. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dc); + } + + //! Save blank image as a .cimg file \overloading. + /** + Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int) + with a file stream argument instead of a filename string. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(file,1,dx,dy,dz,dc); + } + + //! Save image as an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_inr(const char *const filename, const float *const voxel_size=0) const { + return _save_inr(0,filename,voxel_size); + } + + //! Save image as an INRIMAGE-4 file \overloading. + const CImg& save_inr(std::FILE *const file, const float *const voxel_size=0) const { + return _save_inr(file,0,voxel_size); + } + + const CImg& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_inr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + int inrpixsize = -1; + const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { + inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; + } + if (!cimg::strcasecmp(pixel_type(),"char")) { + inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; + } + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { + inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; + } + if (!cimg::strcasecmp(pixel_type(),"short")) { + inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; + } + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { + inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"int")) { + inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"float")) { + inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"double")) { + inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; + } + if (inrpixsize<=0) + throw CImgIOException(_cimg_instance + "save_inr(): Unsupported pixel type '%s' for file '%s'", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + CImg header(257); + int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", + _width,_height,_depth,_spectrum); + if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", + voxel_size[0],voxel_size[1],voxel_size[2]); + err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + std::memset(header._data + err,'\n',252 - err); + std::memcpy(header._data + 252,"##}\n",4); + cimg::fwrite(header._data,256,nfile); + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as an OpenEXR file. + /** + \param filename Filename, as a C-string. + \note The OpenEXR file format is described here. + **/ + const CImg& save_exr(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_exr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + +#ifndef cimg_use_openexr + return save_other(filename); +#else + Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba; + switch (_spectrum) { + case 1 : { // Grayscale image + for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_rPandore file specifications + for more information). + **/ + const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { + return _save_pandore(0,filename,colorspace); + } + + //! Save image as a Pandore-5 file \overloading. + /** + Same as save_pandore(const char *,unsigned int) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const { + return _save_pandore(file,0,colorspace); + } + + unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { + unsigned int nbdims = 0; + if (id==2 || id==3 || id==4) { + dims[0] = 1; dims[1] = _width; nbdims = 2; + } + if (id==5 || id==6 || id==7) { + dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; + } + if (id==8 || id==9 || id==10) { + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + } + if (id==16 || id==17 || id==18) { + dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; + } + if (id==19 || id==20 || id==21) { + dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; + } + if (id==22 || id==23 || id==25) { + dims[0] = _spectrum; dims[1] = _width; nbdims = 2; + } + if (id==26 || id==27 || id==29) { + dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; + } + if (id==30 || id==31 || id==33) { + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + } + return nbdims; + } + + const CImg& _save_pandore(std::FILE *const file, const char *const filename, + const unsigned int colorspace) const { + +#define __cimg_save_pandore_case(dtype) \ + dtype *buffer = new dtype[size()]; \ + const T *ptrs = _data; \ + cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer + +#define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ + if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \ + (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \ + unsigned int *iheader = (unsigned int*)(header + 12); \ + nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ + cimg::fwrite(header,36,nfile); \ + if (sizeof(unsigned long)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else if (sizeof(unsigned int)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else if (sizeof(unsigned short)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + __cimg_save_pandore_case(unsigned char); \ + } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \ + else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \ + else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ + if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \ + else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pandore(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0,'C','I','m','g',0,0,0,0,0, + 'N','o',' ','d','a','t','e',0,0,0,0 }; + unsigned int nbdims, dims[5] = { 0 }; + bool saved = false; + _cimg_save_pandore_case(1,1,1,"unsigned char",2); + _cimg_save_pandore_case(1,1,1,"char",3); + _cimg_save_pandore_case(1,1,1,"unsigned short",3); + _cimg_save_pandore_case(1,1,1,"short",3); + _cimg_save_pandore_case(1,1,1,"unsigned int",3); + _cimg_save_pandore_case(1,1,1,"int",3); + _cimg_save_pandore_case(1,1,1,"unsigned int64",3); + _cimg_save_pandore_case(1,1,1,"int64",3); + _cimg_save_pandore_case(1,1,1,"float",4); + _cimg_save_pandore_case(1,1,1,"double",4); + + _cimg_save_pandore_case(0,1,1,"unsigned char",5); + _cimg_save_pandore_case(0,1,1,"char",6); + _cimg_save_pandore_case(0,1,1,"unsigned short",6); + _cimg_save_pandore_case(0,1,1,"short",6); + _cimg_save_pandore_case(0,1,1,"unsigned int",6); + _cimg_save_pandore_case(0,1,1,"int",6); + _cimg_save_pandore_case(0,1,1,"unsigned int64",6); + _cimg_save_pandore_case(0,1,1,"int64",6); + _cimg_save_pandore_case(0,1,1,"float",7); + _cimg_save_pandore_case(0,1,1,"double",7); + + _cimg_save_pandore_case(0,0,1,"unsigned char",8); + _cimg_save_pandore_case(0,0,1,"char",9); + _cimg_save_pandore_case(0,0,1,"unsigned short",9); + _cimg_save_pandore_case(0,0,1,"short",9); + _cimg_save_pandore_case(0,0,1,"unsigned int",9); + _cimg_save_pandore_case(0,0,1,"int",9); + _cimg_save_pandore_case(0,0,1,"unsigned int64",9); + _cimg_save_pandore_case(0,0,1,"int64",9); + _cimg_save_pandore_case(0,0,1,"float",10); + _cimg_save_pandore_case(0,0,1,"double",10); + + _cimg_save_pandore_case(0,1,3,"unsigned char",16); + _cimg_save_pandore_case(0,1,3,"char",17); + _cimg_save_pandore_case(0,1,3,"unsigned short",17); + _cimg_save_pandore_case(0,1,3,"short",17); + _cimg_save_pandore_case(0,1,3,"unsigned int",17); + _cimg_save_pandore_case(0,1,3,"int",17); + _cimg_save_pandore_case(0,1,3,"unsigned int64",17); + _cimg_save_pandore_case(0,1,3,"int64",17); + _cimg_save_pandore_case(0,1,3,"float",18); + _cimg_save_pandore_case(0,1,3,"double",18); + + _cimg_save_pandore_case(0,0,3,"unsigned char",19); + _cimg_save_pandore_case(0,0,3,"char",20); + _cimg_save_pandore_case(0,0,3,"unsigned short",20); + _cimg_save_pandore_case(0,0,3,"short",20); + _cimg_save_pandore_case(0,0,3,"unsigned int",20); + _cimg_save_pandore_case(0,0,3,"int",20); + _cimg_save_pandore_case(0,0,3,"unsigned int64",20); + _cimg_save_pandore_case(0,0,3,"int64",20); + _cimg_save_pandore_case(0,0,3,"float",21); + _cimg_save_pandore_case(0,0,3,"double",21); + + _cimg_save_pandore_case(1,1,0,"unsigned char",22); + _cimg_save_pandore_case(1,1,0,"char",23); + _cimg_save_pandore_case(1,1,0,"unsigned short",23); + _cimg_save_pandore_case(1,1,0,"short",23); + _cimg_save_pandore_case(1,1,0,"unsigned int",23); + _cimg_save_pandore_case(1,1,0,"int",23); + _cimg_save_pandore_case(1,1,0,"unsigned int64",23); + _cimg_save_pandore_case(1,1,0,"int64",23); + _cimg_save_pandore_case(1,1,0,"float",25); + _cimg_save_pandore_case(1,1,0,"double",25); + + _cimg_save_pandore_case(0,1,0,"unsigned char",26); + _cimg_save_pandore_case(0,1,0,"char",27); + _cimg_save_pandore_case(0,1,0,"unsigned short",27); + _cimg_save_pandore_case(0,1,0,"short",27); + _cimg_save_pandore_case(0,1,0,"unsigned int",27); + _cimg_save_pandore_case(0,1,0,"int",27); + _cimg_save_pandore_case(0,1,0,"unsigned int64",27); + _cimg_save_pandore_case(0,1,0,"int64",27); + _cimg_save_pandore_case(0,1,0,"float",29); + _cimg_save_pandore_case(0,1,0,"double",29); + + _cimg_save_pandore_case(0,0,0,"unsigned char",30); + _cimg_save_pandore_case(0,0,0,"char",31); + _cimg_save_pandore_case(0,0,0,"unsigned short",31); + _cimg_save_pandore_case(0,0,0,"short",31); + _cimg_save_pandore_case(0,0,0,"unsigned int",31); + _cimg_save_pandore_case(0,0,0,"int",31); + _cimg_save_pandore_case(0,0,0,"unsigned int64",31); + _cimg_save_pandore_case(0,0,0,"int64",31); + _cimg_save_pandore_case(0,0,0,"float",33); + _cimg_save_pandore_case(0,0,0,"double",33); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a raw data file. + /** + \param filename Filename, as a C-string. + \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false). + \note The .raw format does not store the image dimensions in the output file, + so you have to keep track of them somewhere to be able to read the file correctly afterwards. + **/ + const CImg& save_raw(const char *const filename, const bool is_multiplexed=false) const { + return _save_raw(0,filename,is_multiplexed); + } + + //! Save image as a raw data file \overloading. + /** + Same as save_raw(const char *,bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_raw(std::FILE *const file, const bool is_multiplexed=false) const { + return _save_raw(file,0,is_multiplexed); + } + + const CImg& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_raw(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (pixel_type()==cimg::type::string()) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = _bool2uchar(siz,is_multiplexed); + cimg::fwrite(buf,siz,nfile); + delete[] buf; + } else { // Non boolean data + if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed + else { // Multiplexed + CImg buf(_spectrum); + cimg_forXYZ(*this,x,y,z) { + cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c); + cimg::fwrite(buf._data,_spectrum,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + // Return unsigned char buffer that encodes data of a CImg instance bitwise. + // (buffer needs to be deallocated afterwards, with delete[]). + const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const { + const ulongT _siz = size(); + siz = _siz/8 + (_siz%8?1:0); + unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0; + + if (!is_multiplexed || _spectrum==1) // Non-multiplexed + cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }} + else // Multiplexed + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) { + (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; } + } + if (bit) *ptrd = val; + return buf; + } + + // Fill CImg instance from bitwise data encoded in an unsigned char buffer. + void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) { + const ulongT S = std::min(siz*8,size()); + const unsigned char *ptrs = buf; + unsigned char val = 0, mask = 0; + T *ptrd = _data; + if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed + for (ulongT off = 0; off>=1)) { val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?1:0); + } + else if (S) { // Multiplexed + ulongT off = 0; + for (int z = 0; z>=1)) { val = *(ptrs++); ++off; mask = 128; } + (*this)(x,y,z,c) = (T)((val&mask)?1:0); + } + } + } + + //! Save image as a .yuv video file. + /** + \param filename Filename, as a C-string. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false). + \note Each slice of the instance image is considered to be a single frame of the output video file. + **/ + const CImg& save_yuv(const char *const filename, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(filename,chroma_subsampling,is_rgb); + return *this; + } + + //! Save image as a .yuv video file \overloading. + /** + Same as save_yuv(const char*,const unsigned int,const bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(file,chroma_subsampling,is_rgb); + return *this; + } + + //! Save 3D object as an Object File Format (.off) file. + /** + \param filename Filename, as a C-string. + \param primitives List of 3D object primitives. + \param colors List of 3D object colors. + \note + - Instance image contains the vertices data of the 3D object. + - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format. + Such primitives will be lost or simplified during file saving. + - The .off file format is described here. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + const char *const filename) const { + return _save_off(primitives,colors,0,filename); + } + + //! Save 3D object as an Object File Format (.off) file \overloading. + /** + Same as save_off(const CImgList&,const CImgList&,const char*) const + with a file stream argument instead of a filename string. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file) const { + return _save_off(primitives,colors,file,0); + } + + template + const CImg& _save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_off(): Specified filename is (null).", + cimg_instance); + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "save_off(): Empty instance, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + CImgList opacities; + CImg error_message(1024); + if (!is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgInstanceException(_cimg_instance + "save_off(): Invalid specified 3D object, for file '%s' (%s).", + cimg_instance, + filename?filename:"(FILE*)",error_message.data()); + + const CImg default_color(1,3,1,1,(tc)std::min((int)cimg::type::max(),200)); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + unsigned int supported_primitives = 0; + cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives; + std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width); + cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n", + (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); + cimglist_for(primitives,l) { + const CImg& color = l1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f; + switch (psiz) { + case 1 : std::fprintf(nfile,"1 %u %f %f %f\n", + (unsigned int)primitives(l,0),r,g,b); break; + case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + (unsigned int)primitives(l,1),r,g,b); break; + case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; + case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 6 : { + const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 9 : { + const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + (unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 12 : { + const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save volumetric image as a video (using the OpenCV library when available). + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). + \param keep_open Tells if the video writer associated to the specified filename + must be kept open or not (to allow frames to be added in the same file afterwards). + **/ + const CImg& save_video(const char *const filename, const unsigned int fps=25, + const char *codec=0, const bool keep_open=false) const { + if (is_empty()) { CImgList().save_video(filename,fps,codec,keep_open); return *this; } + CImgList list; + get_split('z').move_to(list); + list.save_video(filename,fps,codec,keep_open); + return *this; + } + + //! Save volumetric image as a video, using custom external binary. + /** + \param filename Filename, as a C-string. + \param fps Video framerate. + \param codec Video codec, as a C-string. + \param bitrate Video bitrate. + \note + - Each slice of the instance image is considered to be a single frame of the output video file. + - This method uses \c custom, an external executable binary provided by + custom. + It must be installed for the method to succeed. + **/ + const CImg& save_custom_external(const char *const filename, const unsigned int fps=25, + const char *const codec=0, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_custom_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImgList list; + get_split('z').move_to(list); + list.save_custom_external(filename,fps,codec,bitrate); + return *this; + } + + //! Save image using gzip external binary. + /** + \param filename Filename, as a C-string. + \note This method uses \c gzip, an external executable binary provided by + gzip. + It must be installed for the method to succeed. + **/ + const CImg& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_gzip_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::gzip_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimg_instance, + filename); + + else cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image using GraphicsMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c gm, an external executable binary provided by + GraphicsMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "GraphicsMagick only writes the first image slice.", + cimg_instance,filename); + +#ifdef cimg_use_png +#define _cimg_sge_extension1 "png" +#define _cimg_sge_extension2 "png" +#else +#define _cimg_sge_extension1 "pgm" +#define _cimg_sge_extension2 "ppm" +#endif + CImg command(1024), filename_tmp(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(), + _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + +#ifdef cimg_use_png + save_png(filename_tmp); +#else + save_pnm(filename_tmp); +#endif + cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"", + cimg::graphicsmagick_path(),quality, + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::graphicsmagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image using ImageMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c convert, an external executable binary provided by + ImageMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_imagemagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "ImageMagick only writes the first image slice.", + cimg_instance,filename); +#ifdef cimg_use_png +#define _cimg_sie_extension1 "png" +#define _cimg_sie_extension2 "png" +#else +#define _cimg_sie_extension1 "pgm" +#define _cimg_sie_extension2 "ppm" +#endif + CImg command(1024), filename_tmp(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(), + cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); +#ifdef cimg_use_png + save_png(filename_tmp); +#else + save_pnm(filename_tmp); +#endif + cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"", + cimg::imagemagick_path(),quality, + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::imagemagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_imagemagick_external(): Failed to save file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image as a Dicom file. + /** + \param filename Filename, as a C-string. + \note This method uses \c medcon, an external executable binary provided by + (X)Medcon. + It must be installed for the method to succeed. + **/ + const CImg& save_medcon_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_medcon_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImg command(1024), filename_tmp(256), body(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save_analyze(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::medcon_path()); + std::remove(filename_tmp); + cimg::split_filename(filename_tmp,body); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data); + std::remove(filename_tmp); + + file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"m000-%s",filename); + file = cimg::std_fopen(command,"rb"); + if (!file) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + std::rename(command,filename); + return *this; + } + + // Save image for non natively supported formats. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note + - The filename extension tells about the desired file format. + - This method tries to save the instance image as a file, using external tools from + ImageMagick or + GraphicsMagick. + At least one of these tool must be installed for the method to succeed. + - It is recommended to use the generic method save(const char*, int) const instead, + as it can handle some file formats natively. + **/ + const CImg& save_other(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_other(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "ImageMagick or GraphicsMagick only writes the first image slice.", + cimg_instance,filename); + + const unsigned int omode = cimg::exception_mode(); + bool is_saved = true; + cimg::exception_mode(0); + try { save_magick(filename); } + catch (CImgException&) { + try { save_imagemagick_external(filename,quality); } + catch (CImgException&) { + try { save_graphicsmagick_external(filename,quality); } + catch (CImgException&) { + is_saved = false; + } + } + } + cimg::exception_mode(omode); + if (!is_saved) + throw CImgIOException(_cimg_instance + "save_other(): Failed to save file '%s'. Format is not natively supported, " + "and no external commands succeeded.", + cimg_instance, + filename); + return *this; + } + + //! Serialize a CImg instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { + return CImgList(*this,true).get_serialize(is_compressed); + } + + // [internal] Return a 40x38 color logo of a 'danger' item. + static CImg _logo40x38() { + CImg res(40,38,1,3); + const unsigned char *ptrs = cimg::logo40x38; + T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2); + for (ulongT off = 0; off<(ulongT)res._width*res._height;) { + const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++); + for (unsigned int l = 0; l structure + # + # + # + #------------------------------------------ + */ + //! Represent a list of images CImg. + template + struct CImgList { + unsigned int _width, _allocated_width; + CImg *_data; + + //! Simple iterator type, to loop through each image of a list. + /** + \note + - The \c CImgList::iterator type is defined as a CImg*. + - You may use it like this: + \code + CImgList<> list; // Assuming this image list is not empty + for (CImgList<>::iterator it = list.begin(); it* iterator; + + //! Simple const iterator type, to loop through each image of a \c const list instance. + /** + \note + - The \c CImgList::const_iterator type is defined to be a const CImg*. + - Similar to CImgList::iterator, but for constant list instances. + **/ + typedef const CImg* const_iterator; + + //! Pixel value type. + /** + Refer to the pixels value type of the images in the list. + \note + - The \c CImgList::value_type type of a \c CImgList is defined to be a \c T. + It is then similar to CImg::value_type. + - \c CImgList::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimglist_plugin +#include cimglist_plugin +#endif +#ifdef cimglist_plugin1 +#include cimglist_plugin1 +#endif +#ifdef cimglist_plugin2 +#include cimglist_plugin2 +#endif +#ifdef cimglist_plugin3 +#include cimglist_plugin3 +#endif +#ifdef cimglist_plugin4 +#include cimglist_plugin4 +#endif +#ifdef cimglist_plugin5 +#include cimglist_plugin5 +#endif +#ifdef cimglist_plugin6 +#include cimglist_plugin6 +#endif +#ifdef cimglist_plugin7 +#include cimglist_plugin7 +#endif +#ifdef cimglist_plugin8 +#include cimglist_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + Destroy current list instance. + \note + - Any allocated buffer is deallocated. + - Destroying an empty list does nothing actually. + **/ + ~CImgList() { + delete[] _data; + } + + //! Default constructor. + /** + Construct a new empty list instance. + \note + - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its + image buffer pointer data(). + - An empty list may be reassigned afterwards, with the family of the assign() methods. + In all cases, the type of pixels stays \c T. + **/ + CImgList(): + _width(0),_allocated_width(0),_data(0) {} + + //! Construct list containing empty images. + /** + \param n Number of empty images. + \note Useful when you know by advance the number of images you want to manage, as + it will allocate the right amount of memory for the list, without needs for reallocation + (that may occur when starting from an empty list and inserting several images in it). + **/ + explicit CImgList(const unsigned int n):_width(n) { + if (n) _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + else { _allocated_width = 0; _data = 0; } + } + + //! Construct list containing images of specified size. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \note Pixel values are not initialized and may probably contain garbage. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + } + + //! Construct list containing images of specified size, and initialize pixel values. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val Initialization value for images pixels. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T& val): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + } + + //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val0 First value of the initializing integers sequence. + \param val1 Second value of the initializing integers sequence. + \warning You must specify at least width*height*depth*spectrum values in your argument list, + or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...): + _width(0),_allocated_width(0),_data(0) { +#define _CImgList_stdarg(t) { \ + assign(n,width,height,depth,spectrum); \ + const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \ + T *ptrd = _data->_data; \ + va_list ap; \ + va_start(ap,val1); \ + for (ulongT l = 0, s = 0, i = 0; iwidth*height*depth*spectrum values in your argument list, + or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...): + _width(0),_allocated_width(0),_data(0) { + _CImgList_stdarg(double); + } + + //! Construct list containing copies of an input image. + /** + \param n Number of images. + \param img Input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img. + **/ + template + CImgList(const unsigned int n, const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + } + + //! Construct list from one image. + /** + \param img Input image to copy in the constructed list. + \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img. + **/ + template + explicit CImgList(const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(1); + _data[0].assign(img,is_shared); + } + + //! Construct list from two images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + } + + //! Construct list from three images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + } + + //! Construct list from four images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); + } + + //! Construct list from five images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); + } + + //! Construct list from six images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + } + + //! Construct list from seven images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); + } + + //! Construct list from eight images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param img8 Eighth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + } + + //! Construct list copy. + /** + \param list Input list to copy. + \note The shared state of each element of the constructed list is kept the same as in \c list. + **/ + template + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + } + + //! Construct list copy \specialization. + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared); + } + + //! Construct list copy, and force the shared state of the list elements. + /** + \param list Input list to copy. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImgList& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],is_shared); + } + + //! Construct list by reading the content of a file. + /** + \param filename Filename, as a C-string. + **/ + explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) { + assign(filename); + } + + //! Construct list from the content of a display window. + /** + \param disp Display window to get content from. + \note Constructed list contains a single image only. + **/ + explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) { + assign(disp); + } + + //! Return a list with elements being shared copies of images in the list instance. + /** + \note list2 = list1.get_shared() is equivalent to list2.assign(list1,true). + **/ + CImgList get_shared() { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Return a list with elements being shared copies of images in the list instance \const. + const CImgList get_shared() const { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Destructor \inplace. + /** + \see CImgList(). + **/ + CImgList& assign() { + delete[] _data; + _width = _allocated_width = 0; + _data = 0; + return *this; + } + + //! Destructor \inplace. + /** + Equivalent to assign(). + \note Only here for compatibility with STL naming conventions. + **/ + CImgList& clear() { + return assign(); + } + + //! Construct list containing empty images \inplace. + /** + \see CImgList(unsigned int). + **/ + CImgList& assign(const unsigned int n) { + if (!n) return assign(); + if (_allocated_width(n<<2)) { + delete[] _data; + _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + } + _width = n; + return *this; + } + + //! Construct list containing images of specified size \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + return *this; + } + + //! Construct list containing images of specified size, and initialize pixel values \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T& val) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + return *this; + } + + //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) { + _CImgList_stdarg(int); + return *this; + } + + //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace. + /** + \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, + const double val0, const double val1, ...) { + _CImgList_stdarg(double); + return *this; + } + + //! Construct list containing copies of an input image \inplace. + /** + \see CImgList(unsigned int, const CImg&, bool). + **/ + template + CImgList& assign(const unsigned int n, const CImg& img, const bool is_shared=false) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + return *this; + } + + //! Construct list from one image \inplace. + /** + \see CImgList(const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img, const bool is_shared=false) { + assign(1); + _data[0].assign(img,is_shared); + return *this; + } + + //! Construct list from two images \inplace. + /** + \see CImgList(const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const bool is_shared=false) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + return *this; + } + + //! Construct list from three images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + return *this; + } + + //! Construct list from four images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); + return *this; + } + + //! Construct list from five images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); + return *this; + } + + //! Construct list from six images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + return *this; + } + + //! Construct list from seven images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); + return *this; + } + + //! Construct list from eight images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + return *this; + } + + //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace. + /** + \see CImgList(const CImgList&, bool is_shared). + **/ + template + CImgList& assign(const CImgList& list, const bool is_shared=false) { + cimg::unused(is_shared); + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + return *this; + } + + //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization. + CImgList& assign(const CImgList& list, const bool is_shared=false) { + if (this==&list) return *this; + CImgList res(list._width); + cimglist_for(res,l) res[l].assign(list[l],is_shared); + return res.move_to(*this); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& assign(const char *const filename) { + return load(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& assign(const CImgDisplay &disp) { + return assign(CImg(disp)); + } + + //! Transfer the content of the list instance to another list. + /** + \param list Destination list. + \note When returning, the current list instance is empty and the initial content of \c list is destroyed. + **/ + template + CImgList& move_to(CImgList& list) { + list.assign(_width); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[l]); + assign(); + return list; + } + + //! Transfer the content of the list instance at a specified position in another list. + /** + \param list Destination list. + \param pos Index of the insertion in the list. + \note When returning, the list instance is empty and the initial content of \c list is preserved + (only images indexes may be modified). + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos) { + if (is_empty()) return list; + const unsigned int npos = pos>list._width?list._width:pos; + list.insert(_width,npos); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[npos + l]); + assign(); + return list; + } + + //! Swap all fields between two list instances. + /** + \param list List to swap fields with. + \note Can be used to exchange the content of two lists in a fast way. + **/ + CImgList& swap(CImgList& list) { + cimg::swap(_width,list._width,_allocated_width,list._allocated_width); + cimg::swap(_data,list._data); + return list; + } + + //! Return a reference to an empty list. + /** + \note Can be used to define default values in a function taking a CImgList as an argument. + \code + void f(const CImgList& list=CImgList::empty()); + \endcode + **/ + static CImgList& empty() { + static CImgList _empty; + return _empty.assign(); + } + + //! Return a reference to an empty list \const. + static const CImgList& const_empty() { + static const CImgList _empty; + return _empty; + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Return a reference to one image element of the list. + /** + \param pos Index of the image element. + **/ + CImg& operator()(const unsigned int pos) { +#if cimg_verbosity>=3 + if (pos>=_width) { + cimg::warn(_cimglist_instance + "operator(): Invalid image request, at position [%u].", + cimglist_instance, + pos); + return *_data; + } +#endif + return _data[pos]; + } + + //! Return a reference to one image of the list. + /** + \param pos Index of the image element. + **/ + const CImg& operator()(const unsigned int pos) const { + return const_cast*>(this)->operator()(pos); + } + + //! Return a reference to one pixel value of one image of the list. + /** + \param pos Index of the image element. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list(n,x,y,z,c) is equivalent to list[n](x,y,z,c). + **/ + T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + return (*this)[pos](x,y,z,c); + } + + //! Return a reference to one pixel value of one image of the list \const. + const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return (*this)[pos](x,y,z,c); + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + operator CImg*() { + return _data; + } + + //! Return pointer to the first image of the list \const. + operator const CImg*() const { + return _data; + } + + //! Construct list from one image \inplace. + /** + \param img Input image to copy in the constructed list. + \note list = img; is equivalent to list.assign(img);. + **/ + template + CImgList& operator=(const CImg& img) { + return assign(img); + } + + //! Construct list from another list. + /** + \param list Input list to copy. + \note list1 = list2 is equivalent to list1.assign(list2);. + **/ + template + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list from another list \specialization. + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& operator=(const char *const filename) { + return assign(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return a non-shared copy of a list. + /** + \note +list is equivalent to CImgList(list,false). + It forces the copy to have non-shared elements. + **/ + CImgList operator+() const { + return CImgList(*this,false); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end. + /** + \param img Image inserted at the end of the instance copy. + \note Define a convenient way to create temporary lists of images, as in the following code: + \code + (img1,img2,img3,img4).display("My four images"); + \endcode + **/ + template + CImgList& operator,(const CImg& img) { + return insert(img); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end \const. + template + CImgList operator,(const CImg& img) const { + return (+*this).insert(img); + } + + //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end. + /** + \param list List inserted at the end of the instance copy. + **/ + template + CImgList& operator,(const CImgList& list) { + return insert(list); + } + + //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const. + template + CImgList& operator,(const CImgList& list) const { + return (+*this).insert(list); + } + + //! Return image corresponding to the appending of all images of the instance list along specified axis. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \note list>'x' is equivalent to list.get_append('x'). + **/ + CImg operator>(const char axis) const { + return get_append(axis,0); + } + + //! Return list corresponding to the splitting of all images of the instance list along specified axis. + /** + \param axis Axis used for image splitting. + \note list<'x' is equivalent to list.get_split('x'). + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to size() but returns result as a (signed) integer. + **/ + int width() const { + return (int)_width; + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to width() but returns result as an unsigned integer. + **/ + unsigned int size() const { + return _width; + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + CImg *data() { + return _data; + } + + //! Return pointer to the first image of the list \const. + const CImg *data() const { + return _data; + } + + //! Return pointer to the pos-th image of the list. + /** + \param pos Index of the image element to access. + \note list.data(n); is equivalent to list.data + n;. + **/ +#if cimg_verbosity>=3 + CImg *data(const unsigned int pos) { + if (pos>=size()) + cimg::warn(_cimglist_instance + "data(): Invalid pointer request, at position [%u].", + cimglist_instance, + pos); + return _data + pos; + } + + const CImg *data(const unsigned int l) const { + return const_cast*>(this)->data(l); + } +#else + CImg *data(const unsigned int l) { + return _data + l; + } + + //! Return pointer to the pos-th image of the list \const. + const CImg *data(const unsigned int l) const { + return _data + l; + } +#endif + + //! Return iterator to the first image of the list. + /** + **/ + iterator begin() { + return _data; + } + + //! Return iterator to the first image of the list \const. + const_iterator begin() const { + return _data; + } + + //! Return iterator to one position after the last image of the list. + /** + **/ + iterator end() { + return _data + _width; + } + + //! Return iterator to one position after the last image of the list \const. + const_iterator end() const { + return _data + _width; + } + + //! Return reference to the first image of the list. + /** + **/ + CImg& front() { + return *_data; + } + + //! Return reference to the first image of the list \const. + const CImg& front() const { + return *_data; + } + + //! Return a reference to the last image of the list. + /** + **/ + const CImg& back() const { + return *(_data + _width - 1); + } + + //! Return a reference to the last image of the list \const. + CImg& back() { + return *(_data + _width - 1); + } + + //! Return pos-th image of the list. + /** + \param pos Index of the image element to access. + **/ + CImg& at(const int pos) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "at(): Empty instance.", + cimglist_instance); + + return _data[cimg::cut(pos,0,width() - 1)]; + } + + //! Access to pixel value with Dirichlet boundary conditions. + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions. + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); + } + + T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); + } + + //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); + } + + T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); + } + + T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const. + T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const. + T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); + } + + T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const. + T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const. + T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); + } + + T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Return \c true if list is empty. + /** + **/ + bool is_empty() const { + return (!_data || !_width); + } + + //! Test if number of image elements is equal to specified value. + /** + \param size_n Number of image elements to test. + **/ + bool is_sameN(const unsigned int size_n) const { + return _width==size_n; + } + + //! Test if number of image elements is equal between two images lists. + /** + \param list Input list to compare with. + **/ + template + bool is_sameN(const CImgList& list) const { + return is_sameN(list._width); + } + + // Define useful functions to check list dimensions. + // (cannot be documented because macro-generated). +#define _cimglist_def_is_same1(axis) \ + bool is_same##axis(const unsigned int val) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \ + return res; \ + } \ + bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \ + return is_sameN(n) && is_same##axis(val); \ + } \ + +#define _cimglist_def_is_same2(axis1,axis2) \ + bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \ + return res; \ + } \ + bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \ + return is_sameN(n) && is_same##axis1##axis2(val1,val2); \ + } \ + +#define _cimglist_def_is_same3(axis1,axis2,axis3) \ + bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \ + const unsigned int val3) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \ + return res; \ + } \ + bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \ + const unsigned int val2, const unsigned int val3) const { \ + return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \ + } \ + +#define _cimglist_def_is_same(axis) \ + template bool is_same##axis(const CImg& img) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \ + return res; \ + } \ + template bool is_same##axis(const CImgList& list) const { \ + const unsigned int lmin = std::min(_width,list._width); \ + bool res = true; \ + for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ + return (is_sameN(n) && is_same##axis(img)); \ + } \ + template bool is_sameN##axis(const CImgList& list) const { \ + return (is_sameN(list) && is_same##axis(list)); \ + } + + _cimglist_def_is_same(XY) + _cimglist_def_is_same(XZ) + _cimglist_def_is_same(XC) + _cimglist_def_is_same(YZ) + _cimglist_def_is_same(YC) + _cimglist_def_is_same(XYZ) + _cimglist_def_is_same(XYC) + _cimglist_def_is_same(YZC) + _cimglist_def_is_same(XYZC) + _cimglist_def_is_same1(X) + _cimglist_def_is_same1(Y) + _cimglist_def_is_same1(Z) + _cimglist_def_is_same1(C) + _cimglist_def_is_same2(X,Y) + _cimglist_def_is_same2(X,Z) + _cimglist_def_is_same2(X,C) + _cimglist_def_is_same2(Y,Z) + _cimglist_def_is_same2(Y,C) + _cimglist_def_is_same2(Z,C) + _cimglist_def_is_same3(X,Y,Z) + _cimglist_def_is_same3(X,Y,C) + _cimglist_def_is_same3(X,Z,C) + _cimglist_def_is_same3(Y,Z,C) + + //! Test if dimensions of each image of the list match specified arguments. + /** + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameXYZC(const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) const { + bool res = true; + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc); + return res; + } + + //! Test if list dimensions match specified arguments. + /** + \param n Number of images in the list. + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameNXYZC(const unsigned int n, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) const { + return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc); + } + + //! Test if list contains one particular pixel location. + /** + \param n Index of the image whom checked pixel value belong to. + \param x X-coordinate of the checked pixel value. + \param y Y-coordinate of the checked pixel value. + \param z Z-coordinate of the checked pixel value. + \param c C-coordinate of the checked pixel value. + **/ + bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) return false; + return n>=0 && n=0 && x<_data[n].width() && y>=0 && y<_data[n].height() && + z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum(); + } + + //! Test if list contains image with specified index. + /** + \param n Index of the checked image. + **/ + bool containsN(const int n) const { + if (is_empty()) return false; + return n>=0 && n + bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const { + if (is_empty()) return false; + cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; } + return false; + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \param[out] z Z-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y,z). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { + t c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y) const { + t z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x). + **/ + template + bool contains(const T& pixel, t& n, t& x) const { + t y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \note If true, set coordinates (n). + **/ + template + bool contains(const T& pixel, t& n) const { + t x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + **/ + bool contains(const T& pixel) const { + unsigned int n, x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if the list contains the image 'img'. + /** + \param img Reference to image to test. + \param[out] n Index of image in the list, if test succeeds. + \note If true, returns the position (n) of the image in the list. + **/ + template + bool contains(const CImg& img, t& n) const { + if (is_empty()) return false; + const CImg *const ptr = &img; + cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; } + return false; + } + + //! Test if the list contains the image img. + /** + \param img Reference to image to test. + **/ + bool contains(const CImg& img) const { + unsigned int n; + return contains(img,n); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + //! Return a reference to the minimum pixel value of the instance list. + /** + **/ + T& min() { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the maximum pixel value of the instance list \const. + const T& max() const { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T max_value = *ptr_max; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + T& min_max(t& max_val) { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min_max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + const T& min_max(t& max_val) const { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min_max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well. + /** + \param[out] min_val Value of the minimum value found. + **/ + template + T& max_min(t& min_val) { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max_min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max_min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " + "at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + if (is_shared) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified shared image " + "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).", + cimglist_instance, + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos); + + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list + _data = new_data; + *_data = img; + } else { + if (new_data) { // Insert with re-allocation + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); + delete[] _data; + _data = new_data; + } else if (npos!=_width - 1) // Insert without re-allocation + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; + _data[npos] = img; + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization. + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " + "at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list + _data = new_data; + if (is_shared && img) { + _data->_width = img._width; + _data->_height = img._height; + _data->_depth = img._depth; + _data->_spectrum = img._spectrum; + _data->_is_shared = true; + _data->_data = img._data; + } else *_data = img; + } + else { + if (new_data) { // Insert with re-allocation + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + if (is_shared && img) { + new_data[npos]._width = img._width; + new_data[npos]._height = img._height; + new_data[npos]._depth = img._depth; + new_data[npos]._spectrum = img._spectrum; + new_data[npos]._is_shared = true; + new_data[npos]._data = img._data; + } else { + new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; + new_data[npos]._data = 0; + new_data[npos] = img; + } + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); + delete[] _data; + _data = new_data; + } else { // Insert without re-allocation + if (npos!=_width - 1) + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + if (is_shared && img) { + _data[npos]._width = img._width; + _data[npos]._height = img._height; + _data[npos]._depth = img._depth; + _data[npos]._spectrum = img._spectrum; + _data[npos]._is_shared = true; + _data[npos]._data = img._data; + } else { + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; + _data[npos] = img; + } + } + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance. + template + CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(img,pos,is_shared); + } + + //! Insert n empty images img into the current image list, at position \p pos. + /** + \param n Number of empty images to insert. + \param pos Index of the insertion. + **/ + CImgList& insert(const unsigned int n, const unsigned int pos=~0U) { + CImg empty; + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i get_insert(const unsigned int n, const unsigned int pos=~0U) const { + return (+*this).insert(n,pos); + } + + //! Insert \c n copies of the image \c img into the current image list, at position \c pos. + /** + \param n Number of image copies to insert. + \param img Image to insert by copy. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of \c img or not. + **/ + template + CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, + const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + insert(img,npos,is_shared); + for (unsigned int i = 1; i + CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,img,pos,is_shared); + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos. + /** + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared); + else insert(CImgList(list),npos,is_shared); + return *this; + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance. + template + CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(list,pos,is_shared); + } + + //! Insert n copies of the list \c list at position \c pos of the current list. + /** + \param n Number of list copies to insert. + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i + CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,list,pos,is_shared); + } + + //! Remove all images between from indexes. + /** + \param pos1 Starting index of the removal. + \param pos2 Ending index of the removal. + **/ + CImgList& remove(const unsigned int pos1, const unsigned int pos2) { + const unsigned int + npos1 = pos1=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + else { + if (tpos2>=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + + for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign(); + const unsigned int nb = 1 + npos2 - npos1; + if (!(_width-=nb)) return assign(); + if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation + if (npos1!=_width) + std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + std::memset((void*)(_data + _width),0,sizeof(CImg)*nb); + } else { // Removing items with reallocation + _allocated_width>>=4; + while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; + CImg *const new_data = new CImg[_allocated_width]; + if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos1); + if (npos1!=_width) + std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + if (_width!=_allocated_width) + std::memset((void*)(new_data + _width),0,sizeof(CImg)*(_allocated_width - _width)); + std::memset((void*)_data,0,sizeof(CImg)*(_width + nb)); + delete[] _data; + _data = new_data; + } + } + return *this; + } + + //! Remove all images between from indexes \newinstance. + CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { + return (+*this).remove(pos1,pos2); + } + + //! Remove image at index \c pos from the image list. + /** + \param pos Index of the image to remove. + **/ + CImgList& remove(const unsigned int pos) { + return remove(pos,pos); + } + + //! Remove image at index \c pos from the image list \newinstance. + CImgList get_remove(const unsigned int pos) const { + return (+*this).remove(pos); + } + + //! Remove last image. + /** + **/ + CImgList& remove() { + return remove(_width - 1); + } + + //! Remove last image \newinstance. + CImgList get_remove() const { + return (+*this).remove(); + } + + //! Reverse list order. + CImgList& reverse() { + for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]); + return *this; + } + + //! Reverse list order \newinstance. + CImgList get_reverse() const { + return (+*this).reverse(); + } + + //! Return a sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList& images(const unsigned int pos0, const unsigned int pos1) { + return get_images(pos0,pos1).move_to(*this); + } + + //! Return a sublist \newinstance. + CImgList get_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l]); + return res; + } + + //! Return a shared sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); + return res; + } + + //! Return a shared sublist \newinstance. + const CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); + return res; + } + + //! Return a single image which is the appending of all images of the current CImgList instance. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg get_append(const char axis, const float align=0) const { + if (is_empty()) return CImg(); + if (_width==1) return +((*this)[0]); + unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0; + CImg res; + switch (cimg::lowercase(axis)) { + case 'x' : { // Along the X-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx+=img._width; + dy = std::max(dy,img._height); + dz = std::max(dz,img._depth); + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._height==1 && img._depth==1 && img._spectrum==1 && + res._height==1 && res._depth==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._width); + else + res.draw_image(pos, + (int)(align*(dy - img._height)), + (int)(align*(dz - img._depth)), + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._width; + } + } break; + case 'y' : { // Along the Y-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy+=img._height; + dz = std::max(dz,img._depth); + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._depth==1 && img._spectrum==1 && + res._width==1 && res._depth==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._height); + else + res.draw_image((int)(align*(dx - img._width)), + pos, + (int)(align*(dz - img._depth)), + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._height; + } + } break; + case 'z' : { // Along the Z-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy = std::max(dy,img._height); + dz+=img._depth; + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._height==1 && img._spectrum==1 && + res._width==1 && res._height==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._depth); + else + res.draw_image((int)(align*(dx - img._width)), + (int)(align*(dy - img._height)), + pos, + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._depth; + } + } break; + default : { // Along the C-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy = std::max(dy,img._height); + dz = std::max(dz,img._depth); + dc+=img._spectrum; + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._height==1 && img._depth==1 && + res._width==1 && res._height==1 && res._depth==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum); + else + res.draw_image((int)(align*(dx - img._width)), + (int)(align*(dy - img._height)), + (int)(align*(dz - img._depth)), + pos, + img); + } + pos+=img._spectrum; + } + } + } + return res; + } + + //! Return a list where each image has been split along the specified axis. + /** + \param axis Axis to split images along. + \param nb Number of split parts for each image. + **/ + CImgList& split(const char axis, const int nb=-1) { + return get_split(axis,nb).move_to(*this); + } + + //! Return a list where each image has been split along the specified axis \newinstance. + CImgList get_split(const char axis, const int nb=-1) const { + CImgList res; + cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U); + return res; + } + + //! Insert image at the end of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_back(const CImg& img) { + return insert(img); + } + + //! Insert image at the front of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_front(const CImg& img) { + return insert(img,0); + } + + //! Insert list at the end of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_back(const CImgList& list) { + return insert(list); + } + + //! Insert list at the front of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_front(const CImgList& list) { + return insert(list,0); + } + + //! Remove last image. + /** + **/ + CImgList& pop_back() { + return remove(_width - 1); + } + + //! Remove first image. + /** + **/ + CImgList& pop_front() { + return remove(0); + } + + //! Remove image pointed by iterator. + /** + \param iter Iterator pointing to the image to remove. + **/ + CImgList& erase(const iterator iter) { + return remove(iter - _data); + } + + //@} + //---------------------------------- + // + //! \name Data Input + //@{ + //---------------------------------- + + //! Display a simple interactive interface to select images or sublists. + /** + \param disp Window instance to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \param exit_on_anykey Exit function when any key is pressed. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(CImgDisplay &disp, const bool feature_type=true, + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false); + } + + //! Display a simple interactive interface to select images or sublists. + /** + \param title Title of a new window used to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \param exit_on_anykey Exit function when any key is pressed. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(const char *const title, const bool feature_type=true, + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); + } + + CImg _select(CImgDisplay &disp, const char *const title, const bool feature_type, + const char axis, const float align, const bool exit_on_anykey, + const unsigned int orig, const bool resize_disp, + const bool exit_on_rightbutton, const bool exit_on_wheel) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "select(): Empty instance.", + cimglist_instance); + + // Create image correspondence table and get list dimensions for visualization. + CImgList _indices; + unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + if (h>max_height) max_height = h; + sum_width+=w; sum_height+=h; + if (axis=='x') CImg(w,1,1,1,(unsigned int)l).move_to(_indices); + else CImg(h,1,1,1,(unsigned int)l).move_to(_indices); + } + const CImg indices0 = _indices>'x'; + + // Create display window. + if (!disp) { + if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); + else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); + if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else { + if (title) disp.set_title("%s",title); + disp.move_inside_screen(); + } + if (resize_disp) { + if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false); + else disp.resize(cimg_fitscreen(max_width,sum_height,1),false); + } + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).show_mouse(); + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + + // Enter event loop. + CImg visu0, visu; + CImg indices; + CImg positions(_width,4,1,1,-1); + int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1; + bool is_clicked = false, is_selected = false, text_down = false, update_display = true; + unsigned int key = 0, font_size = 32; + + while (!is_selected && !disp.is_closed() && !key) { + + // Create background image. + if (!visu0) { + visu0.assign(disp._width,disp._height,1,3,0); visu.assign(); + (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices); + unsigned int _ind = 0; + const CImg onexone(1,1,1,1,(T)0); + if (axis=='x') + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) + cimglist_for(*this,ind) { + unsigned int x0 = 0; + while (x0 &src = _data[ind]?_data[ind]:onexone; + CImg res; + src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2). + move_to(res); + const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true); + res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)x0; + positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height())); + positions(ind,2)+=res._width; + positions(ind,3)+=res._height - 1; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) + cimglist_for(*this,ind) { + unsigned int y0 = 0; + while (y0 &src = _data[ind]?_data[ind]:onexone; + CImg res; + src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2). + move_to(res); + const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false); + res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width())); + positions(ind,1) = positions(ind,3) = (int)y0; + positions(ind,2)+=res._width - 1; + positions(ind,3)+=res._height; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + if (axis=='x') --positions(_ind,2); else --positions(_ind,3); + update_display = true; + } + + if (!visu || oindex0!=index0 || oindex1!=index1) { + if (index0>=0 && index1>=0) { + visu.assign(visu0,false); + const int indm = std::min(index0,index1), indM = std::max(index0,index1); + for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) { + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + background_color,0.2f); + if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) || + (axis!='x' && positions(ind,3) - positions(ind,1)>=8)) + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + foreground_color,0.9f,0xAAAAAAAA); + } + if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down, + orig + indm,orig + indM,indM - indm + 1); + else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down, + orig + index0, + _data[index0]._width, + _data[index0]._height, + _data[index0]._depth, + _data[index0]._spectrum); + update_display = true; + } else visu.assign(); + } + if (!visu) { visu.assign(visu0,true); update_display = true; } + if (update_display) { visu.display(disp); update_display = false; } + disp.wait(); + + // Manage user events. + const int xm = disp.mouse_x(), ym = disp.mouse_y(); + int index = -1; + + if (xm>=0) { + index = (int)indices(axis=='x'?xm:ym); + if (disp.button()&1) { + if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; } + oindex1 = index1; index1 = index; + if (!feature_type) is_selected = true; + } else { + if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; } + else is_selected = true; + } + } else { + if (is_clicked) { + if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; } + else index1 = -1; + } else index0 = index1 = -1; + } + + if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; } + if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; } + if (disp.wheel() && exit_on_wheel) is_selected = true; + + CImg filename(32); + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp); + visu0.save(filename); + (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + } + disp.set_key(key,false).wait(); key = 0; + } break; + case cimg::keyO : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp); + save(filename); + (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + disp.set_key(key,false).wait(); key = 0; + } break; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }} + else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }} + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + CImg res(1,2,1,1,-1); + if (is_selected) { + if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1)); + else res.fill(index0); + } + if (!(disp.button()&2)) disp.set_button(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + disp.set_key(key); + return res; + } + + //! Load a list from a file. + /** + \param filename Filename to read data from. + **/ + CImgList& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load(): Specified filename is (null).", + cimglist_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg filename_local(256); + load(cimg::load_network(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + bool is_loaded = true; + try { +#ifdef cimglist_load_plugin + cimglist_load_plugin(filename); +#endif +#ifdef cimglist_load_plugin1 + cimglist_load_plugin1(filename); +#endif +#ifdef cimglist_load_plugin2 + cimglist_load_plugin2(filename); +#endif +#ifdef cimglist_load_plugin3 + cimglist_load_plugin3(filename); +#endif +#ifdef cimglist_load_plugin4 + cimglist_load_plugin4(filename); +#endif +#ifdef cimglist_load_plugin5 + cimglist_load_plugin5(filename); +#endif +#ifdef cimglist_load_plugin6 + cimglist_load_plugin6(filename); +#endif +#ifdef cimglist_load_plugin7 + cimglist_load_plugin7(filename); +#endif +#ifdef cimglist_load_plugin8 + cimglist_load_plugin8(filename); +#endif + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) load_cimg(filename); + else if (!cimg::strcasecmp(ext,"rec") || + !cimg::strcasecmp(ext,"par")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + + // If nothing loaded, try to guess file format from magic number in file. + if (!is_loaded && !is_stdin) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to open file '%s'.", + cimglist_instance, + filename); + } + + const char *const f_type = cimg::ftype(file,filename); + cimg::fclose(file); + is_loaded = true; + try { + if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + } + + // If nothing loaded, try to load file as a single image. + if (!is_loaded) { + assign(1); + try { + _data->load(filename); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to recognize format of file '%s'.", + cimglist_instance, + filename); + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load a list from a file \newinstance. + static CImgList get_load(const char *const filename) { + return CImgList().load(filename); + } + + //! Load a list from a .cimg file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_cimg(const char *const filename) { + return _load_cimg(0,filename); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename) { + return CImgList().load_cimg(filename); + } + + //! Load a list from a .cimg file. + /** + \param file File to read data from. + **/ + CImgList& load_cimg(std::FILE *const file) { + return _load_cimg(file,0); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file) { + return CImgList().load_cimg(file); + } + + CImgList& _load_cimg(std::FILE *const file, const char *const filename) { +#ifdef cimg_use_zlib +#define _cimgz_load_cimg_case(Tss) { \ + Bytef *const cbuf = new Bytef[csiz]; \ + cimg::fread(cbuf,(size_t)csiz,nfile); \ + if (is_bool) { \ + CImg raw(W*H*D*C/8); \ + uLongf destlen = (uLongf)raw.size(); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ + img.assign(W,H,D,C); \ + img._uchar2bool(raw,raw.size(),false); \ + } else { \ + CImg raw(W,H,D,C); \ + uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } \ + delete[] cbuf; \ +} +#else +#define _cimgz_load_cimg_case(Tss) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \ + cimglist_instance, \ + filename?filename:"(FILE*)"); +#endif + +#define _cimg_load_cimg_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + const bool is_bool = cimg::type::string()==cimg::type::string(); \ + for (unsigned int l = 0; l=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; csiz = 0; \ + if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:("(FILE*)")); \ + if (W*H*D*C>0) { \ + CImg &img = _data[l]; \ + if (err==5) _cimgz_load_cimg_case(Tss) \ + else { \ + img.assign(W,H,D,C); \ + T *ptrd = img._data; \ + if (is_bool) { \ + CImg raw; \ + for (ulongT to_read = img.size(); to_read; ) { \ + raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + CImg(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\ + _uchar2bool(raw,raw._width,false); \ + to_read-=raw._width; \ + } \ + } else { \ + CImg raw; \ + for (ulongT to_read = img.size(); to_read; ) { \ + raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + const Tss *ptrs = raw._data; \ + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + to_read-=raw._width; \ + } \ + } \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + + const ulongT cimg_iobuffer = (ulongT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N = 0, W, H, D, C; + cimg_uint64 csiz; + int i, err; + do { + j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; + } while (*tmp=='#' && i>=0); + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + assign(N); + _cimg_load_cimg_case("bool",bool); + _cimg_load_cimg_case("unsigned_char",unsigned char); + _cimg_load_cimg_case("uchar",unsigned char); + _cimg_load_cimg_case("char",char); + _cimg_load_cimg_case("unsigned_short",unsigned short); + _cimg_load_cimg_case("ushort",unsigned short); + _cimg_load_cimg_case("short",short); + _cimg_load_cimg_case("unsigned_int",unsigned int); + _cimg_load_cimg_case("uint",unsigned int); + _cimg_load_cimg_case("int",int); + _cimg_load_cimg_case("unsigned_long",ulongT); + _cimg_load_cimg_case("ulong",ulongT); + _cimg_load_cimg_case("long",longT); + _cimg_load_cimg_case("unsigned_int64",uint64T); + _cimg_load_cimg_case("uint64",uint64T); + _cimg_load_cimg_case("int64",int64T); + _cimg_load_cimg_case("float",float); + _cimg_load_cimg_case("double",double); + + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype._data,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a sublist list from a (non compressed) .cimg file. + /** + \param filename Filename to read data from. + \param n0 Starting index of images to read (~0U for max). + \param n1 Ending index of images to read (~0U for max). + \param x0 Starting X-coordinates of image regions to read. + \param y0 Starting Y-coordinates of image regions to read. + \param z0 Starting Z-coordinates of image regions to read. + \param c0 Starting C-coordinates of image regions to read. + \param x1 Ending X-coordinates of image regions to read (~0U for max). + \param y1 Ending Y-coordinates of image regions to read (~0U for max). + \param z1 Ending Z-coordinates of image regions to read (~0U for max). + \param c1 Ending C-coordinates of image regions to read (~0U for max). + **/ + CImgList& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sublist list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \overloading. + CImgList& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + CImgList& _load_cimg(std::FILE *const file, const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { +#define _cimg_load_cimg_case2(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l<=nn1; ++l) { \ + j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; \ + if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:"(FILE*)"); \ + if (W*H*D*C>0) { \ + if (l=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const unsigned int \ + _nx1 = nx1==~0U?W - 1:nx1, \ + _ny1 = ny1==~0U?H - 1:ny1, \ + _nz1 = nz1==~0U?D - 1:nz1, \ + _nc1 = nc1==~0U?C - 1:nc1; \ + if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \ + throw CImgArgumentException(_cimglist_instance \ + "load_cimg(): Invalid specified coordinates " \ + "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \ + "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \ + cimglist_instance, \ + n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \ + CImg raw(1 + _nx1 - nx0); \ + CImg &img = _data[l - nn0]; \ + img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \ + T *ptrd = img._data; \ + ulongT skipvb = nc0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \ + const ulongT skipzb = nz0*W*H*sizeof(Tss); \ + if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \ + const ulongT skipyb = ny0*W*sizeof(Tss); \ + if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \ + const ulongT skipxb = nx0*sizeof(Tss); \ + if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \ + const Tss *ptrs = raw._data; \ + for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + unsigned int + nn0 = std::min(n0,n1), nn1 = std::max(n0,n1), + nx0 = std::min(x0,x1), nx1 = std::max(x0,x1), + ny0 = std::min(y0,y1), ny1 = std::max(y0,y1), + nz0 = std::min(z0,z1), nz1 = std::max(z0,z1), + nc0 = std::min(c0,c1), nc1 = std::max(c0,c1); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N, W, H, D, C; + int i, err; + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + nn1 = n1==~0U?N - 1:n1; + if (nn1>=N) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " + "because file '%s' contains only %u images.", + cimglist_instance, + n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N); + assign(1 + nn1 - n0); + _cimg_load_cimg_case2("bool",bool); + _cimg_load_cimg_case2("unsigned_char",unsigned char); + _cimg_load_cimg_case2("uchar",unsigned char); + _cimg_load_cimg_case2("char",char); + _cimg_load_cimg_case2("unsigned_short",unsigned short); + _cimg_load_cimg_case2("ushort",unsigned short); + _cimg_load_cimg_case2("short",short); + _cimg_load_cimg_case2("unsigned_int",unsigned int); + _cimg_load_cimg_case2("uint",unsigned int); + _cimg_load_cimg_case2("int",int); + _cimg_load_cimg_case2("unsigned_long",ulongT); + _cimg_load_cimg_case2("ulong",ulongT); + _cimg_load_cimg_case2("long",longT); + _cimg_load_cimg_case2("unsigned_int64",uint64T); + _cimg_load_cimg_case2("uint64",uint64T); + _cimg_load_cimg_case2("int64",int64T); + _cimg_load_cimg_case2("float",float); + _cimg_load_cimg_case2("double",double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype._data,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_parrec(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_parrec(): Specified filename is (null).", + cimglist_instance); + + CImg body(1024), filenamepar(1024), filenamerec(1024); + *body = *filenamepar = *filenamerec = 0; + const char *const ext = cimg::split_filename(filename,body); + if (!std::strcmp(ext,"par")) { + std::strncpy(filenamepar,filename,filenamepar._width - 1); + cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data); + } + if (!std::strcmp(ext,"PAR")) { + std::strncpy(filenamepar,filename,filenamepar._width - 1); + cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data); + } + if (!std::strcmp(ext,"rec")) { + std::strncpy(filenamerec,filename,filenamerec._width - 1); + cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data); + } + if (!std::strcmp(ext,"REC")) { + std::strncpy(filenamerec,filename,filenamerec._width - 1); + cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data); + } + std::FILE *file = cimg::fopen(filenamepar,"r"); + + // Parse header file + CImgList st_slices; + CImgList st_global; + CImg line(256); *line = 0; + int err; + do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.')); + do { + unsigned int sn,size_x,size_y,pixsize; + float rs,ri,ss; + err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss); + if (err==7) { + CImg::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices); + unsigned int i; for (i = 0; i::vector(size_x,size_y,sn).move_to(st_global); + else { + CImg &vec = st_global[i]; + if (size_x>vec[0]) vec[0] = size_x; + if (size_y>vec[1]) vec[1] = size_y; + vec[2] = sn; + } + st_slices[st_slices._width - 1][7] = (float)i; + } + } while (err==7); + + // Read data + std::FILE *file2 = cimg::fopen(filenamerec,"rb"); + cimglist_for(st_global,l) { + const CImg& vec = st_global[l]; + CImg(vec[0],vec[1],vec[2]).move_to(*this); + } + + cimglist_for(st_slices,l) { + const CImg& vec = st_slices[l]; + const unsigned int + sn = (unsigned int)vec[0] - 1, + pixsize = (unsigned int)vec[1], + size_x = (unsigned int)vec[2], + size_y = (unsigned int)vec[3], + imn = (unsigned int)vec[7]; + const float ri = vec[4], rs = vec[5], ss = vec[6]; + switch (pixsize) { + case 8 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 16 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 32 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + default : + cimg::fclose(file); + cimg::fclose(file2); + throw CImgIOException(_cimglist_instance + "load_parrec(): Unsupported %d-bits pixel type for file '%s'.", + cimglist_instance, + pixsize,filename); + } + } + cimg::fclose(file); + cimg::fclose(file2); + if (!_width) + throw CImgIOException(_cimglist_instance + "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.", + cimglist_instance, + filename); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file \newinstance. + static CImgList get_load_parrec(const char *const filename) { + return CImgList().load_parrec(filename); + } + + //! Load a list from a YUV image sequence file. + /** + \param filename Filename to read data from. + \param size_x Width of the images. + \param size_y Height of the images. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param yuv2rgb Apply YUV to RGB transformation during reading. + **/ + CImgList& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(0,filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from a YUV image sequence file \newinstance. + static CImgList get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \overloading. + CImgList& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(file,0,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \newinstance. + static CImgList get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + CImgList& _load_yuv(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling, + const unsigned int first_frame, const unsigned int last_frame, + const unsigned int step_frame, const bool yuv2rgb) { + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + nfirst_frame = first_frame YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2); + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool stop_flag = false; + int err; + if (nfirst_frame) { + err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR); + if (err) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_yuv(): File '%s' doesn't contain frame number %u.", + cimglist_instance, + filename?filename:"(FILE*)",nfirst_frame); + } + } + unsigned int frame; + for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) { + YUV.get_shared_channel(0).fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile); + if (err!=(int)(YUV._width*YUV._height)) { + stop_flag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + UV.fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile); + if (err!=(int)(UV.size())) { + stop_flag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1); + ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2); + const unsigned int wd = YUV._width; + switch (chroma_subsampling) { + case 420 : + cimg_forY(UV,y) { + cimg_forX(UV,x) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd2[wd] = V; *(ptrd2)++ = V; + ptrd2[wd] = V; *(ptrd2)++ = V; + } + ptrd1+=wd; ptrd2+=wd; + } + break; + case 422 : + cimg_forXY(UV,x,y) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + *(ptrd1++) = U; *(ptrd1++) = U; + *(ptrd2++) = V; *(ptrd2++) = V; + } + break; + default : + YUV.draw_image(0,0,0,1,UV); + } + if (yuv2rgb) YUV.YCbCrtoRGB(); + insert(YUV); + if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + } + } + } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_yuv() : Missing data in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame) + cimg::warn(_cimglist_instance + "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", + cimglist_instance, + nlast_frame,frame - 1,filename?filename:"(FILE*)"); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a video file, using OpenCV library. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U'). + \param step_frame Step value for frame reading. + \note If step_frame==0, the current video stream is forced to be released (without any frames read). + **/ + CImgList& load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { +#ifndef cimg_use_opencv + if (first_frame || last_frame!=~0U || step_frame>1) + throw CImgArgumentException(_cimglist_instance + "load_video() : File '%s', arguments 'first_frame', 'last_frame' " + "and 'step_frame' requires features from the OpenCV library " + "('-Dcimg_use_opencv' must be defined).", + cimglist_instance,filename); + return load_custom_external(filename); +#else + static cv::VideoCapture *captures[32] = { 0 }; + static CImgList filenames(32); + static CImg positions(32,1,1,1,0); + static int last_used_index = -1; + + // Detect if a video capture already exists for the specified filename. + cimg::mutex(9); + int index = -1; + if (filename) { + if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { + index = last_used_index; + } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { + index = l; break; + } + } else index = last_used_index; + cimg::mutex(9,0); + + // Release stream if needed. + if (!step_frame || (index>=0 && positions[index]>first_frame)) { + if (index>=0) { + cimg::mutex(9); + captures[index]->release(); + delete captures[index]; + captures[index] = 0; + positions[index] = 0; + filenames[index].assign(); + if (last_used_index==index) last_used_index = -1; + index = -1; + cimg::mutex(9,0); + } else + if (filename) + cimg::warn(_cimglist_instance + "load_video() : File '%s', no opened video stream associated with filename found.", + cimglist_instance,filename); + else + cimg::warn(_cimglist_instance + "load_video() : No opened video stream found.", + cimglist_instance,filename); + if (!step_frame) return *this; + } + + // Find empty slot for capturing video stream. + if (index<0) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_video(): No already open video reader found. You must specify a " + "non-(null) filename argument for the first call.", + cimglist_instance); + else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } + if (index<0) + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', no video reader slots available. " + "You have to release some of your previously opened videos.", + cimglist_instance,filename); + cimg::mutex(9); + captures[index] = new cv::VideoCapture(filename); + positions[index] = 0; + if (!captures[index]->isOpened()) { + delete captures[index]; + captures[index] = 0; + cimg::mutex(9,0); + cimg::fclose(cimg::fopen(filename,"rb")); // Check file availability + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', unable to detect format of video file.", + cimglist_instance,filename); + } + CImg::string(filename).move_to(filenames[index]); + cimg::mutex(9,0); + } + + cimg::mutex(9); + const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count)); + cimg::mutex(9,0); + assign(); + + // Skip frames if requested. + bool go_on = true; + unsigned int &pos = positions[index]; + while (posgrab()) { cimg::mutex(9,0); go_on = false; break; } + cimg::mutex(9,0); + ++pos; + } + + // Read and convert frames. + const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame); + while (go_on && pos<=_last_frame) { + cv::Mat cvimg; + cimg::mutex(9); + if (captures[index]->read(cvimg)) { CImg::_cvmat2cimg(cvimg).move_to(*this); ++pos; } + else go_on = false; + cimg::mutex(9,0); + if (go_on) + for (unsigned int i = 1; go_on && igrab()) go_on = false; + cimg::mutex(9,0); + } + } + + if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary + cimg::mutex(9); + captures[index]->release(); + delete captures[index]; + captures[index] = 0; + filenames[index].assign(); + positions[index] = 0; + index = -1; + cimg::mutex(9,0); + } + + cimg::mutex(9); + last_used_index = index; + cimg::mutex(9,0); + + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', unable to locate frame %u.", + cimglist_instance,filename,first_frame); + return *this; +#endif + } + + //! Load an image from a video file, using OpenCV library \newinstance. + static CImgList get_load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImgList().load_video(filename,first_frame,last_frame,step_frame); + } + + //! Load an image from a video file using the external tool 'custom'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_custom_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_custom_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), filename_tmp2(256); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); + cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"", + cimg::custom_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp2)._system_strescape().data()); + cimg::system(command, cimg::custom_path()); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + assign(); + unsigned int i = 1; + for (bool stop_flag = false; !stop_flag; ++i) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i); + CImg img; + try { img.load_pnm(filename_tmp2); } + catch (CImgException&) { stop_flag = true; } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + } + cimg::exception_mode(omode); + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_custom_external(): Failed to open file '%s' with external command 'custom'.", + cimglist_instance, + filename); + return *this; + } + + //! Load an image from a video file using the external tool 'custom' \newinstance. + static CImgList get_load_custom_external(const char *const filename) { + return CImgList().load_custom_external(filename); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gif_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_gif_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + if (!_load_gif_external(filename,false)) + if (!_load_gif_external(filename,true)) + try { assign(CImg().load_other(filename)); } catch (CImgException&) { assign(); } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_gif_external(): Failed to open file '%s'.", + cimglist_instance,filename); + return *this; + } + + CImgList& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data); + else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::imagemagick_path()); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + assign(); + + // Try to read a single frame gif. + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data); + CImg img; + try { img.load_png(filename_tmp2); } + catch (CImgException&) { } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + else { // Try to read animated gif + unsigned int i = 0; + for (bool stop_flag = false; !stop_flag; ++i) { + if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i); + else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i); + try { img.load_png(filename_tmp2); } + catch (CImgException&) { stop_flag = true; } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance. + static CImgList get_load_gif_external(const char *const filename) { + return CImgList().load_gif_external(filename); + } + + //! Load a gzipped list, using external tool 'gunzip'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::gunzip_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Failed to open file '%s'.", + cimglist_instance, + filename); + + } else cimg::fclose(file); + load(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load a gzipped list, using external tool 'gunzip' \newinstance. + static CImgList get_load_gzip_external(const char *const filename) { + return CImgList().load_gzip_external(filename); + } + + //! Load images from a TIFF file. + /** + \param filename Filename to read data from. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + \param[out] voxel_size Voxel size, as stored in the filename. + \param[out] description Description, as stored in the filename. + **/ + CImgList& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + const unsigned int + nfirst_frame = first_frame::get_load_tiff(filename)); +#else +#if cimg_verbosity<3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn(_cimglist_instance + "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since " + "file '%s' contains %u image(s).", + cimglist_instance, + nfirst_frame,nlast_frame,nstep_frame,filename,nb_images); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + assign(1 + (nlast_frame - nfirst_frame)/nstep_frame); + TIFFSetDirectory(tif,0); + cimglist_for(*this,l) + _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description); + TIFFClose(tif); + } else throw CImgIOException(_cimglist_instance + "load_tiff(): Failed to open file '%s'.", + cimglist_instance, + filename); + return *this; +#endif + } + + //! Load a multi-page TIFF file \newinstance. + static CImgList get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); + } + + //@} + //---------------------------------- + // + //! \name Data Output + //@{ + //---------------------------------- + + //! Print information about the list on the standard output. + /** + \param title Label set to the information displayed. + \param display_stats Tells if image statistics must be computed and displayed. + **/ + const CImgList& print(const char *const title=0, const bool display_stats=true) const { + unsigned int msiz = 0; + cimglist_for(*this,l) msiz+=_data[l].size(); + msiz*=sizeof(T); + const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type()); + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_allocated_width, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1)); + else std::fprintf(cimg::output(),".\n"); + + char tmp[16] = { 0 }; + cimglist_for(*this,ll) { + cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll); + std::fprintf(cimg::output()," "); + _data[ll].print(tmp,display_stats); + if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); } + } + std::fflush(cimg::output()); + return *this; + } + + //! Display the current CImgList instance in an existing CImgDisplay window (by reference). + /** + \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + \note This function displays the list images of the current CImgList instance into an existing + CImgDisplay window. + Images of the list are appended in a single temporary image for visualization purposes. + The function returns immediately. + **/ + const CImgList& display(CImgDisplay &disp, const char axis='x', const float align=0) const { + disp.display(*this,axis,align); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + \param disp Display window. + \param display_info Tells if image information are displayed on the standard output. + \param axis Alignment axis for images viewing. + \param align Appending alignment. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + \note This function opens a new window with a specific title and displays the list images of the + current CImgList instance into it. + Images of the list are appended in a single temporary image for visualization purposes. + The function returns when a key is pressed or the display window is closed by the user. + **/ + const CImgList& display(CImgDisplay &disp, const bool display_info, + const char axis='x', const float align=0, + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { + bool is_exit = false; + return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); + } + + //! Display the current CImgList instance in a new display window. + /** + \param title Title of the opening display window. + \param display_info Tells if list information must be written on standard output. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImgList& display(const char *const title=0, const bool display_info=true, + const char axis='x', const float align=0, + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { + CImgDisplay disp; + bool is_exit = false; + return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); + } + + const CImgList& _display(CImgDisplay &disp, const char *const title, const CImgList *const titles, + const bool display_info, const char axis, const float align, unsigned int *const XYZ, + const bool exit_on_anykey, const unsigned int orig, const bool is_first_call, + bool &is_exit) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "display(): Empty instance.", + cimglist_instance); + if (!disp) { + if (axis=='x') { + unsigned int sum_width = 0, max_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + sum_width+=w; + if (h>max_height) max_height = h; + } + disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1); + } else { + unsigned int max_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + sum_height+=h; + } + disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1); + } + if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else if (title) disp.set_title("%s",title); + else if (titles) disp.set_title("%s",titles->__display()._data); + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(disp.title()); + disp.show().flush(); + + if (_width==1) { + const unsigned int dw = disp._width, dh = disp._height; + if (!is_first_call) + disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false); + disp.set_title("%s (%ux%ux%ux%u)", + dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum); + _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call); + if (disp.key()) is_exit = true; + disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); + } else { + bool disp_resize = !is_first_call; + while (!disp.is_closed() && !is_exit) { + const CImg s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true); + disp_resize = true; + if (s[0]<0 && !disp.wheel()) { // No selections done + if (disp.button()&2) { disp.flush(); break; } + is_exit = true; + } else if (disp.wheel()) { // Zoom in/out + const int wheel = disp.wheel(); + disp.set_wheel(); + if (!is_first_call && wheel<0) break; + if (wheel>0 && _width>=4) { + const unsigned int + delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)), + ind0 = (unsigned int)std::max(0,s[0] - (int)delta), + ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta); + if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) { + const CImgList sublist = get_shared_images(ind0,ind1); + CImgList t_sublist; + if (titles) t_sublist = titles->get_shared_images(ind0,ind1); + sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, + orig + ind0,false,is_exit); + } + } + } else if (s[0]!=0 || s[1]!=width() - 1) { + const CImgList sublist = get_shared_images(s[0],s[1]); + CImgList t_sublist; + if (titles) t_sublist = titles->get_shared_images(s[0],s[1]); + sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, + orig + s[0],false,is_exit); + } + disp.set_title("%s",dtitle.data()); + } + } + return *this; + } + + // [internal] Return string to describe display title. + CImg __display() const { + CImg res, str; + cimglist_for(*this,l) { + CImg::string((char*)_data[l]).move_to(str); + if (l!=width() - 1) { + str.resize(str._width + 1,1,1,1,0); + str[str._width - 2] = ','; + str[str._width - 1] = ' '; + } + res.append(str,'x'); + } + if (!res) return CImg(1,1,1,1,0).move_to(res); + cimg::strellipsize(res,128,false); + if (_width>1) { + const unsigned int l = (unsigned int)std::strlen(res); + if (res._width<=l + 16) res.resize(l + 16,1,1,1,0); + cimg_snprintf(res._data + l,16," (#%u)",_width); + } + return res; + } + + //! Save list into a file. + /** + \param filename Filename to write data to. + \param number When positive, represents an index added to the filename. Otherwise, no number is added. + \param digits Number of digits used for adding the number to the filename. + **/ + const CImgList& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save(): Specified filename is (null).", + cimglist_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + CImg nfilename(1024); + const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename): + filename; + +#ifdef cimglist_save_plugin + cimglist_save_plugin(fn); +#endif +#ifdef cimglist_save_plugin1 + cimglist_save_plugin1(fn); +#endif +#ifdef cimglist_save_plugin2 + cimglist_save_plugin2(fn); +#endif +#ifdef cimglist_save_plugin3 + cimglist_save_plugin3(fn); +#endif +#ifdef cimglist_save_plugin4 + cimglist_save_plugin4(fn); +#endif +#ifdef cimglist_save_plugin5 + cimglist_save_plugin5(fn); +#endif +#ifdef cimglist_save_plugin6 + cimglist_save_plugin6(fn); +#endif +#ifdef cimglist_save_plugin7 + cimglist_save_plugin7(fn); +#endif +#ifdef cimglist_save_plugin8 + cimglist_save_plugin8(fn); +#endif + if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); +#ifdef cimg_use_tiff + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); +#endif + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + else { + if (_width==1) _data[0].save(fn,-1); + else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); } + } + return *this; + } + + //! Tell if an image list can be saved as one single file. + /** + \param filename Filename, as a C-string. + \return \c true if the file format supports multiple images, \c false otherwise. + **/ + static bool is_saveable(const char *const filename) { + const char *const ext = cimg::split_filename(filename); + if (!cimg::strcasecmp(ext,"cimgz") || +#ifdef cimg_use_tiff + !cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff") || +#endif + !cimg::strcasecmp(ext,"yuv") || + !cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return true; + return false; + } + + //! Save image sequence as a GIF animated file. + /** + \param filename Filename to write data to. + \param fps Number of desired frames per second. + \param nb_loops Number of loops (\c 0 for infinite looping). + **/ + const CImgList& save_gif_external(const char *const filename, const float fps=25, + const unsigned int nb_loops=0) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; + +#ifdef cimg_use_png +#define _cimg_save_gif_extension "png" +#else +#define _cimg_save_gif_extension "ppm" +#endif + + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1); + CImg::string(filename_tmp2).move_to(filenames); + if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); + else _data[l].save(filename_tmp2); + } + cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops); + CImg::string(command).move_to(filenames,0); + cimg_snprintf(command,command._width,"\"%s\"", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); + CImg _command = filenames>'x'; + cimg_for(_command,p,char) if (!*p) *p = ' '; + _command.back() = 0; + + cimg::system(_command, cimg::imagemagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]); + return *this; + } + + //! Save list as a YUV image sequence file. + /** + \param filename Filename to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(const char *const filename=0, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(0,filename,chroma_subsampling,is_rgb); + } + + //! Save image sequence into a YUV file. + /** + \param file File to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(file,0,chroma_subsampling,is_rgb); + } + + const CImgList& _save_yuv(std::FILE *const file, const char *const filename, + const unsigned int chroma_subsampling, + const bool is_rgb) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + w0 = (*this)[0]._width, h0 = (*this)[0]._height, + width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + const CImg &frame = (*this)[l]; + cimg_forZ(frame,z) { + CImg YUV; + if (sizeof(T)==1 && !is_rgb && + frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3) + YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true); + else { + YUV = frame.get_slice(z); + if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0); + if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0); + if (is_rgb) YUV.RGBtoYCbCr(); + } + if (chroma_subsampling==444) + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile); + else { + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile); + CImg UV = YUV.get_channels(1,2); + UV.resize(UV._width/cfx,UV._height/cfy,1,2,2); + cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param filename Filename to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(const char *const filename, const bool is_compressed=false) const { + return _save_cimg(0,filename,is_compressed); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, " + "saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); +#endif + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const bool is_bool = ptype==cimg::type::string(); + if (!is_bool && std::strstr(ptype,"unsigned")==ptype) + std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); + else + std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); + + cimglist_for(*this,l) { + const CImg& img = _data[l]; + std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + Bytef *cbuf = 0; + uLongf csiz = 0; + + if (is_bool) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = ref._bool2uchar(siz,false); + csiz = siz + siz/100 + 16; + cbuf = new Bytef[csiz]; + failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz); + if (!failed_to_compress) { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + } + delete[] buf; + } else { // Non-boolean data + const ulongT siz = sizeof(T)*ref.size(); + csiz = siz + siz/100 + 16; + cbuf = new Bytef[csiz]; + failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz); + if (!failed_to_compress) { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + } + } + if (failed_to_compress) + cimg::warn(_cimglist_instance + "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); + delete[] cbuf; +#endif + } + if (failed_to_compress) { // Write non-compressed + std::fputc('\n',nfile); + if (is_bool) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = ref._bool2uchar(siz,false); + cimg::fwrite(buf,siz,nfile); + delete[] buf; + } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data + } + } else std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param file File to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(std::FILE *file, const bool is_compressed=false) const { + return _save_cimg(file,0,is_compressed); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { +#define _cimg_save_cimg_case(Ts,Tss) \ + if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l0) { \ + if (l=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const CImg& img = (*this)[l - n0]; \ + const T *ptrs = img._data; \ + const unsigned int \ + x1 = x0 + img._width - 1, \ + y1 = y0 + img._height - 1, \ + z1 = z0 + img._depth - 1, \ + c1 = c0 + img._spectrum - 1, \ + nx1 = x1>=W?W - 1:x1, \ + ny1 = y1>=H?H - 1:y1, \ + nz1 = z1>=D?D - 1:z1, \ + nc1 = c1>=C?C - 1:c1; \ + CImg raw(1 + nx1 - x0); \ + const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v = 1 + nc1 - c0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z = 1 + nz1 - z0; z; --z) { \ + const unsigned int skipyb = y0*W*sizeof(Tss); \ + if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + ny1 - y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ + raw.assign(ptrs, raw._width); \ + ptrs+=img._width; \ + if (endian) cimg::invert_endianness(raw._data,raw._width); \ + cimg::fwrite(raw._data,raw._width,nfile); \ + const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_cimg(): Empty instance, for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); + bool saved = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N, W, H, D, C; + int i, err; + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "save_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + const unsigned int lmax = std::min(N,n0 + _width); + _cimg_save_cimg_case("bool",bool); + _cimg_save_cimg_case("unsigned_char",unsigned char); + _cimg_save_cimg_case("uchar",unsigned char); + _cimg_save_cimg_case("char",char); + _cimg_save_cimg_case("unsigned_short",unsigned short); + _cimg_save_cimg_case("ushort",unsigned short); + _cimg_save_cimg_case("short",short); + _cimg_save_cimg_case("unsigned_int",unsigned int); + _cimg_save_cimg_case("uint",unsigned int); + _cimg_save_cimg_case("int",int); + _cimg_save_cimg_case("unsigned_int64",uint64T); + _cimg_save_cimg_case("uint64",uint64T); + _cimg_save_cimg_case("int64",int64T); + _cimg_save_cimg_case("float",float); + _cimg_save_cimg_case("double",double); + if (!saved) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "save_cimg(): Unsupported data type '%s' for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)",str_pixeltype._data); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param filename Filename to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(0,filename,n0,x0,y0,z0,c0); + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param file File to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(file,0,n0,x0,y0,z0,c0); + } + + static void _save_empty_cimg(std::FILE *const file, const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T); + std::fprintf(nfile,"%u %s\n",nb,pixel_type()); + for (unsigned int i=nb; i; --i) { + std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc); + for (ulongT off = siz; off; --off) std::fputc(0,nfile); + } + if (!file) cimg::fclose(nfile); + } + + //! Save empty (non-compressed) .cimg file with specified dimensions. + /** + \param filename Filename to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc); + } + + //! Save empty .cimg file with specified dimensions. + /** + \param file File to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(file,0,nb,dx,dy,dz,dc); + } + + //! Save list as a TIFF file. + /** + \param filename Filename to write data to. + \param compression_type Compression mode used to write data. + \param voxel_size Voxel size, to be stored in the filename. + \param description Description, to be stored in the filename. + \param use_bigtiff Allow to save big tiff files (>4Gb). + **/ + const CImgList& save_tiff(const char *const filename, const unsigned int compression_type=0, + const float *const voxel_size=0, const char *const description=0, + const bool use_bigtiff=true) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_tiff(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_tiff + if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff); + else cimglist_for(*this,l) { + CImg nfilename(1024); + cimg::number_filename(filename,l,6,nfilename); + _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff); + } +#else + ulongT siz = 0; + cimglist_for(*this,l) siz+=_data[l].size(); + const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images + TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); + if (tif) { + for (unsigned int dir = 0, l = 0; l<_width; ++l) { + const CImg& img = (*this)[l]; + cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); + } + TIFFClose(tif); + } else + throw CImgIOException(_cimglist_instance + "save_tiff(): Failed to open stream for file '%s'.", + cimglist_instance, + filename); +#endif + return *this; + } + + //! Save list as a gzipped file, using external tool 'gzip'. + /** + \param filename Filename to write data to. + **/ + const CImgList& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Specified filename is (null).", + cimglist_instance); + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + + if (is_saveable(body)) { + save(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::gzip_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimglist_instance, + filename); + else cimg::fclose(file); + std::remove(filename_tmp); + } else { + CImg nfilename(1024); + cimglist_for(*this,l) { + cimg::number_filename(body,l,6,nfilename); + if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); + _data[l].save_gzip_external(nfilename); + } + } + return *this; + } + + //! Save image sequence (using the OpenCV library when available). + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). + \param keep_open Tells if the video writer associated to the specified filename + must be kept open or not (to allow frames to be added in the same file afterwards). + **/ + const CImgList& save_video(const char *const filename, const unsigned int fps=25, + const char *codec=0, const bool keep_open=false) const { +#ifndef cimg_use_opencv + cimg::unused(codec,keep_open); + return save_custom_external(filename,fps); +#else + try { + static cv::VideoWriter *writers[32] = { 0 }; + static CImgList filenames(32); + static CImg sizes(32,2,1,1,0); + static int last_used_index = -1; + + // Detect if a video writer already exists for the specified filename. + cimg::mutex(9); + int index = -1; + if (filename) { + if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { + index = last_used_index; + } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { + index = l; break; + } + } else index = last_used_index; + cimg::mutex(9,0); + + // Find empty slot for capturing video stream. + if (index<0) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_video(): No already open video writer found. You must specify a " + "non-(null) filename argument for the first call.", + cimglist_instance); + else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } + if (index<0) + throw CImgIOException(_cimglist_instance + "save_video(): File '%s', no video writer slots available. " + "You have to release some of your previously opened videos.", + cimglist_instance,filename); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_video(): Instance list is empty.", + cimglist_instance); + const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0; + if (!W || !H) + throw CImgInstanceException(_cimglist_instance + "save_video(): Frame [0] is an empty image.", + cimglist_instance); + const char + *const _codec = codec && *codec?codec:"h264", + codec0 = cimg::uppercase(_codec[0]), + codec1 = _codec[0]?cimg::uppercase(_codec[1]):0, + codec2 = _codec[1]?cimg::uppercase(_codec[2]):0, + codec3 = _codec[2]?cimg::uppercase(_codec[3]):0; + cimg::mutex(9); + writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H)); + if (!writers[index]->isOpened()) { + delete writers[index]; + writers[index] = 0; + cimg::mutex(9,0); + throw CImgIOException(_cimglist_instance + "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.", + cimglist_instance,filename, + codec0,codec1,codec2,codec3); + } + CImg::string(filename).move_to(filenames[index]); + sizes(index,0) = W; + sizes(index,1) = H; + cimg::mutex(9,0); + } + + if (!is_empty()) { + const unsigned int W = sizes(index,0), H = sizes(index,1); + cimg::mutex(9); + cimglist_for(*this,l) { + CImg &src = _data[l]; + if (src.is_empty()) + cimg::warn(_cimglist_instance + "save_video(): Skip empty frame %d for file '%s'.", + cimglist_instance,l,filename); + if (src._depth>1 || src._spectrum>3) + cimg::warn(_cimglist_instance + "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). " + "Some image data may be ignored when writing frame into video file '%s'.", + cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename); + if (src._width==W && src._height==H && src._spectrum==3) + writers[index]->write(CImg(src)._cimg2cvmat()); + else { + CImg _src(src,false); + _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H); + _src.resize(W,H,1,3,_src._spectrum==1); + writers[index]->write(_src._cimg2cvmat()); + } + } + cimg::mutex(9,0); + } + + cimg::mutex(9); + if (!keep_open) { + delete writers[index]; + writers[index] = 0; + filenames[index].assign(); + sizes(index,0) = sizes(index,1) = 0; + last_used_index = -1; + } else last_used_index = index; + cimg::mutex(9,0); + } catch (CImgIOException &e) { + if (!keep_open) return save_custom_external(filename,fps); + throw e; + } + return *this; +#endif + } + + //! Save image sequence, using the external tool 'custom'. + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression. + \param bitrate Output bitrate + **/ + const CImgList& save_custom_external(const char *const filename, const unsigned int fps=25, + const char *const codec=0, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_custom_external(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + const char + *const ext = cimg::split_filename(filename), + *const _codec = codec?codec: + !cimg::strcasecmp(ext,"flv")?"flv": + !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video"; + + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; + cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0])) + throw CImgInstanceException(_cimglist_instance + "save_custom_external(): Invalid instance dimensions for file '%s'.", + cimglist_instance, + filename); + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); + CImg::string(filename_tmp2).move_to(filenames); + CImg tmp = _data[l].get_shared(); + if (tmp._width%2 || tmp._height%2) // Force output to have an even number of columns and rows + tmp.assign(tmp.get_resize(tmp._width + (tmp._width%2),tmp._height + (tmp._height%2),1,-100,0),false); + if (tmp._depth>1 || tmp._spectrum!=3) // Force output to be one slice, in color + tmp.assign(tmp.get_resize(-100,-100,1,3),false); + tmp.save_pnm(filename_tmp2); + } + cimg_snprintf(command,command._width, + "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"", + cimg::custom_path(), + CImg::string(filename_tmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::custom_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_custom_external(): Failed to save file '%s' with external command 'custom'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for(*this,l) std::remove(filenames[l]); + return *this; + } + + //! Serialize a CImgList instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "get_serialize(): Unable to compress data unless zlib is enabled, " + "storing them uncompressed.", + cimglist_instance); +#endif + CImgList stream; + CImg tmpstr(128); + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + if (std::strstr(ptype,"unsigned")==ptype) + cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); + else + cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); + CImg::string(tmpstr,false).move_to(stream); + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + CImg::string(tmpstr,false).move_to(stream); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + const ulongT siz = sizeof(T)*ref.size(); + uLongf csiz = (ulongT)compressBound(siz); + Bytef *const cbuf = new Bytef[csiz]; + if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) + cimg::warn(_cimglist_instance + "get_serialize(): Failed to save compressed data, saving them uncompressed.", + cimglist_instance); + else { + cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz); + CImg::string(tmpstr,false).move_to(stream); + CImg(cbuf,csiz).move_to(stream); + delete[] cbuf; + failed_to_compress = false; + } +#endif + } + if (failed_to_compress) { // Write in a non-compressed way + CImg::string("\n",false).move_to(stream); + stream.insert(1); + stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true); + } + } else CImg::string("\n",false).move_to(stream); + } + cimglist_apply(stream,unroll)('y'); + return stream>'y'; + } + + //! Unserialize a CImg serialized buffer into a CImgList list. + template + static CImgList get_unserialize(const CImg& buffer) { +#ifdef cimg_use_zlib +#define _cimgz_unserialize_case(Tss) { \ + Bytef *cbuf = 0; \ + if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type::string()) { \ + cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ + for (ulongT k = 0; k::get_unserialize(): Unable to unserialize compressed data " \ + "unless zlib is enabled.", \ + pixel_type()); +#endif + +#define _cimg_unserialize_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \ + "image #%u in serialized buffer.", \ + pixel_type(),W,H,D,C,l); \ + if (W*H*D*C>0) { \ + CImg raw; \ + CImg &img = res._data[l]; \ + if (err==5) _cimgz_unserialize_case(Tss) \ + else { \ + raw.assign(W,H,D,C); \ + CImg _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \ + if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \ + else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \ + } \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } \ + } \ + loaded = true; \ + } + + if (buffer.is_empty()) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).", + pixel_type()); + CImgList res; + const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); + bool loaded = false, endian = cimg::endianness(), is_bytef = false; + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N = 0, W, H, D, C; + uint64T csiz; + int i, err; + cimg::unused(is_bytef); + do { + j = 0; while ((i=(int)*stream)!='\n' && stream::get_unserialize(): CImg header not found in serialized buffer.", + pixel_type()); + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + res.assign(N); + _cimg_unserialize_case("bool",bool); + _cimg_unserialize_case("unsigned_char",unsigned char); + _cimg_unserialize_case("uchar",unsigned char); + _cimg_unserialize_case("char",char); + _cimg_unserialize_case("unsigned_short",unsigned short); + _cimg_unserialize_case("ushort",unsigned short); + _cimg_unserialize_case("short",short); + _cimg_unserialize_case("unsigned_int",unsigned int); + _cimg_unserialize_case("uint",unsigned int); + _cimg_unserialize_case("int",int); + _cimg_unserialize_case("unsigned_int64",uint64T); + _cimg_unserialize_case("uint64",uint64T); + _cimg_unserialize_case("int64",int64T); + _cimg_unserialize_case("float",float); + _cimg_unserialize_case("double",double); + if (!loaded) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined " + "in serialized buffer.", + pixel_type(),str_pixeltype._data); + return res; + } + + //@} + //---------------------------------- + // + //! \name Others + //@{ + //---------------------------------- + + //! Return a CImg pre-defined font with requested height. + /** + \param font_height Height of the desired font (exact match for 13,23,53,103). + \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width. + **/ + static const CImgList& font(const unsigned int requested_height, const bool is_variable_width=true) { + if (!requested_height) return CImgList::const_empty(); + cimg::mutex(11); + static const unsigned char font_resizemap[] = { + 0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30, + 32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52, + 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165, + 166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179, + 180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192, + 193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205, + 206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218, + 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231, + 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, + 244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 }; + static const char *const *font_data[] = { + cimg::data_font_small, + cimg::data_font_normal, + cimg::data_font_large, + cimg::data_font_huge }; + static const unsigned int + font_width[] = { 10,26,52,104 }, + font_height[] = { 13,32,64,128 }, + font_M[] = { 86,91,91,47 }, + font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*), + sizeof(cimg::data_font_normal)/sizeof(char*), + sizeof(cimg::data_font_large)/sizeof(char*), + sizeof(cimg::data_font_huge)/sizeof(char*) }; + static const unsigned char font_is_binary[] = { 1,0,0,1 }; + static CImg font_base[4]; + + unsigned int ind = + requested_height<=font_height[0]?0U: + requested_height<=font_height[1]?1U: + requested_height<=font_height[2]?2U:3U; + + // Decompress nearest base font data if needed. + CImg &basef = font_base[ind]; + if (!basef) { + basef.assign(256*font_width[ind],font_height[ind]); + + unsigned char *ptrd = basef; + const unsigned char *const ptrde = basef.end(); + + // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb). + CImg dataf; + for (unsigned int k = 0; k::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x'); + + // Uncompress font data (decode RLE). + const unsigned int M = font_M[ind]; + if (font_is_binary[ind]) + for (const char *ptrs = dataf; *ptrs; ++ptrs) { + const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n; + if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } + else { std::memset(ptrd,v,ptrde - ptrd); break; } + } + else + for (const char *ptrs = dataf; *ptrs; ++ptrs) { + int n = (int)*ptrs - M - 32, v = 0; + if (n>=0) { v = 85*n; n = 1; } + else { + n = -n; + v = (int)*(++ptrs) - M - 32; + if (v<0) { v = 0; --ptrs; } else v*=85; + } + if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } + else { std::memset(ptrd,v,ptrde - ptrd); break; } + } + } + + // Find optimal font cache location to return. + static CImgList fonts[16]; + static bool is_variable_widths[16] = { 0 }; + ind = ~0U; + for (int i = 0; i<16; ++i) + if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) { + ind = (unsigned int)i; break; // Found empty slot or cached font + } + if (ind==~0U) { // No empty slots nor existing font in cache + fonts->assign(); + std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList)); + std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool)); + std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList)); // Free a slot in cache for new font + } + CImgList &font = fonts[ind]; + + // Render requested font. + if (!font) { + is_variable_widths[ind] = is_variable_width; + basef.get_split('x',256).move_to(font); + +// cimg::tic(); + + if (requested_height!=font[0]._height) + cimglist_for(font,l) { + font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5); + cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr]; + } + +// cimg::toc(); +// std::exit(0); + + if (is_variable_width) { // Crop font + cimglist_for(font,l) { + CImg& letter = font[l]; + int xmin = letter.width(), xmax = 0; + cimg_forX(letter,x) { // Find xmin + cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; } + if (xmin!=letter.width()) break; + } + cimg_rofX(letter,x) { // Find xmax + cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; } + if (xmax) break; + } + if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1); + } + font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0); + if (' ' + 256& FFT(const char axis, const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + CImg::FFT(_data[0],_data[1],axis,invert); + return *this; + } + + //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance. + CImgList get_FFT(const char axis, const bool invert=false) const { + return CImgList(*this,false).FFT(axis,invert); + } + + //! Compute n-D Fast Fourier Transform. + /** + \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. + **/ + CImgList& FFT(const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + + CImg::FFT(_data[0],_data[1],invert); + return *this; + } + + //! Compute n-D Fast Fourier Transform \newinstance. + CImgList get_FFT(const bool invert=false) const { + return CImgList(*this,false).FFT(invert); + } + + //! Reverse primitives orientations of a 3D object. + /** + **/ + CImgList& reverse_object3d() { + cimglist_for(*this,l) { + CImg& p = _data[l]; + switch (p.size()) { + case 2 : case 3: cimg::swap(p[0],p[1]); break; + case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break; + case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break; + case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break; + case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break; + } + } + return *this; + } + + //! Reverse primitives orientations of a 3D object \newinstance. + CImgList get_reverse_object3d() const { + return (+*this).reverse_object3d(); + } + + //@} + }; // struct CImgList { ... + + // Completion of previously declared functions + //-------------------------------------------- + namespace cimg { + + // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. + // (throw a CImgIOException when macro 'cimg_use_r' is defined). + inline FILE* _stdin(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stdin; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + inline FILE* _stdout(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stdout; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + inline FILE* _stderr(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stderr; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + // Open a file (similar to std:: fopen(), but with wide character support on Windows). + inline std::FILE *std_fopen(const char *const path, const char *const mode) { + std::FILE *const res = std::fopen(path,mode); + if (res) return res; +#if cimg_OS==2 + // Try alternative method, with wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (err) { + CImg wpath((unsigned int)err); + err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err); + if (err) { // Convert 'mode' to a wide-character string + err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0); + if (err) { + CImg wmode((unsigned int)err); + if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err)) + return _wfopen(wpath,wmode); + } + } + } +#endif + return 0; + } + + //! Search path of an executable (Windows only). +#if cimg_OS==2 + inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) { + char *ptr = 0; + DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr); + return err!=0; + } +#endif + + //! Get the file or directory attributes with support for UTF-8 paths (Windows only). +#if cimg_OS==2 + inline DWORD win_getfileattributes(const char *const path) { + DWORD res = GetFileAttributesA(path); + if (res==INVALID_FILE_ATTRIBUTES) { + // Try alternative method, with wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (err) { + CImg wpath((unsigned int)err); + if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath); + } + } + return res; + } +#endif + + //! Get/set path to the Program Files/ directory (Windows only). + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the program files. + **/ +#if cimg_OS==2 + inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(MAX_PATH); + *s_path = 0; + // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). +#if !defined(__INTEL_COMPILER) + if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) { + const char *const pfPath = std::getenv("PROGRAMFILES"); + if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1); + else std::strcpy(s_path,"C:\\PROGRA~1"); + } +#else + std::strcpy(s_path,"C:\\PROGRA~1"); +#endif + } + cimg::mutex(7,0); + return s_path; + } +#endif + + //! Get/set path to the \c curl binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c curl binary. + **/ + inline const char *curl_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\curl.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"curl.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./curl"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"curl"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c dcraw binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c dcraw binary. + **/ + inline const char *dcraw_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\dcraw.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"dcraw.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./dcraw"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"dcraw"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the custom's \c custom binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c custom binary. + **/ + inline const char *custom_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("custom.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\custom.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"custom.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./custom"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"custom"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the GraphicsMagick's \c gm binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gm binary. + **/ + inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\gm.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gm.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gm"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gm"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c gunzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gunzip binary. + **/ + inline const char *gunzip_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\gunzip.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gunzip.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gunzip"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gunzip"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c gzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gzip binary. + **/ + inline const char *gzip_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\gzip.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gzip.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gzip"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gzip"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the ImageMagick's \c convert binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c convert binary. + **/ + inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + for (int l = 0; l<2 && !path_found; ++l) { + const char *const s_exe = l?"convert":"magick"; + cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe); + } +#else + std::strcpy(s_path,"./magick"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + if (!path_found) { + std::strcpy(s_path,"./convert"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"convert"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the Medcon's \c medcon binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c medcon binary. + **/ + inline const char* medcon_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\medcon.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"medcon.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./medcon"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"medcon"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to store temporary files. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path where temporary files can be saved. + **/ + inline const char* temporary_path(const char *const user_path, const bool reinit_path) { +#define _cimg_test_temporary_path(p) \ + if (!path_found) { \ + cimg_snprintf(s_path,s_path._width,"%s",p); \ + cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ + if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ + } + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + CImg tmp(1024), filename_tmp(256); + std::FILE *file = 0; + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand()); + char *tmpPath = std::getenv("TMP"); + if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); } + if (tmpPath) _cimg_test_temporary_path(tmpPath); +#if cimg_OS==2 + _cimg_test_temporary_path("C:\\WINNT\\Temp"); + _cimg_test_temporary_path("C:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("C:\\Temp"); + _cimg_test_temporary_path("C:"); + _cimg_test_temporary_path("D:\\WINNT\\Temp"); + _cimg_test_temporary_path("D:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("D:\\Temp"); + _cimg_test_temporary_path("D:"); +#else + _cimg_test_temporary_path("/tmp"); + _cimg_test_temporary_path("/var/tmp"); +#endif + if (!path_found) { + *s_path = 0; + std::strncpy(tmp,filename_tmp,tmp._width - 1); + if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } + } + if (!path_found) { + cimg::mutex(7,0); + throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n"); + } + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c wget binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c wget binary. + **/ + inline const char *wget_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\wget.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"wget.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./wget"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"wget"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + + // [internal] Sorting function, used by cimg::files(). + inline int _sort_files(const void* a, const void* b) { + const CImg &sa = *(CImg*)a, &sb = *(CImg*)b; + return std::strcmp(sa._data,sb._data); + } + + //! Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, + const unsigned int digits, char *const str) { + if (!filename) { if (str) *str = 0; return 0; } + const unsigned int siz = (unsigned int)std::strlen(filename); + CImg format(16), body(siz + 32); + const char *const ext = cimg::split_filename(filename,body); + if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits); + else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits); + cimg_snprintf(str,1024,format._data,body._data,number,ext); + return str; + } + + //! Return list of files/directories in specified directory. + /** + \param path Path to the directory. Set to 0 for current directory. + \param is_pattern Tell if specified path has a matching pattern in it. + \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }. + \param include_path Tell if \c path must be included in resulting filenames. + \return A list of filenames. + **/ + inline CImgList files(const char *const path, const bool is_pattern=false, + const unsigned int mode=2, const bool include_path=false) { + if (!path || !*path) return files("*",true,mode,include_path); + CImgList res; + + // If path is a valid folder name, ignore argument 'is_pattern'. + const bool _is_pattern = is_pattern && !cimg::is_directory(path); + bool is_root = false, is_current = false; + cimg::unused(is_root,is_current); + + // Clean format of input path. + CImg pattern, _path = CImg::string(path); +#if cimg_OS==2 + for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/'; +#endif + char *pd = _path; + for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; } + *pd = 0; + unsigned int lp = (unsigned int)std::strlen(_path); + if (!_is_pattern && lp && _path[lp - 1]=='/') { + _path[lp - 1] = 0; --lp; +#if cimg_OS!=2 + is_root = !*_path; +#endif + } + + // Separate folder path and matching pattern. + if (_is_pattern) { + const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data()); + CImg::string(_path).move_to(pattern); + if (bpos) { + _path[bpos - 1] = 0; // End 'path' at last slash +#if cimg_OS!=2 + is_root = !*_path; +#endif + } else { // No path to folder specified, assuming current folder + is_current = true; *_path = 0; + } + lp = (unsigned int)std::strlen(_path); + } + + // Windows version. +#if cimg_OS==2 + if (!_is_pattern) { + pattern.assign(lp + 3); + std::memcpy(pattern,_path,lp); + pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0; + } + WIN32_FIND_DATAA file_data; + const HANDLE dir = FindFirstFileA(pattern.data(),&file_data); + if (dir==INVALID_HANDLE_VALUE) return CImgList::const_empty(); + do { + const char *const filename = file_data.cFileName; + if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { + const unsigned int lf = (unsigned int)std::strlen(filename); + const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0; + if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) { + if (include_path) { + CImg full_filename((lp?lp+1:0) + lf + 1); + if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; } + std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1); + full_filename.move_to(res); + } else CImg(filename,lf + 1).move_to(res); + } + } + } while (FindNextFileA(dir,&file_data)); + FindClose(dir); + + // Unix version (posix). +#elif cimg_OS == 1 + DIR *const dir = opendir(is_root?"/":is_current?".":_path.data()); + if (!dir) return CImgList::const_empty(); + struct dirent *ent; + while ((ent=readdir(dir))!=0) { + const char *const filename = ent->d_name; + if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { + const unsigned int lf = (unsigned int)std::strlen(filename); + CImg full_filename(lp + lf + 2); + + if (!is_current) { + full_filename.assign(lp + lf + 2); + if (lp) std::memcpy(full_filename,_path,lp); + full_filename[lp] = '/'; + std::memcpy(full_filename._data + lp + 1,filename,lf + 1); + } else full_filename.assign(filename,lf + 1); + + struct stat st; + if (stat(full_filename,&st)==-1) continue; + const bool is_directory = (st.st_mode & S_IFDIR)!=0; + if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) { + if (include_path) { + if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) + full_filename.move_to(res); + } else { + if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) + CImg(filename,lf + 1).move_to(res); + } + } + } + } + closedir(dir); +#endif + + // Sort resulting list by lexicographic order. + if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg),_sort_files); + + return res; + } + + //! Try to guess format from an image file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + \return C-string containing the guessed file format, or \c 0 if nothing has been guessed. + **/ + inline const char *ftype(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::ftype(): Specified filename is (null)."); + static const char + *const _pnm = "pnm", + *const _pfm = "pfm", + *const _bmp = "bmp", + *const _gif = "gif", + *const _jpg = "jpg", + *const _off = "off", + *const _pan = "pan", + *const _png = "png", + *const _tif = "tif", + *const _inr = "inr", + *const _dcm = "dcm"; + const char *f_type = 0; + CImg header; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + header._load_raw(file,filename,512,1,1,1,false,false,0); + const unsigned char *const uheader = (unsigned char*)header._data; + if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF + else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE + f_type = _inr; + else if (!std::strncmp(header,"PANDORE",7)) // PANDORE + f_type = _pan; + else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM + f_type = _dcm; + else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG + f_type = _jpg; + else if (header[0]=='B' && header[1]=='M') // BMP + f_type = _bmp; + else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && + (header[4]=='7' || header[4]=='9')) // GIF + f_type = _gif; + else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && + uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG + f_type = _png; + else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) // TIFF + f_type = _tif; + else { // PNM or PFM + CImgList _header = header.get_split(CImg::vector('\n'),0,false); + cimglist_for(_header,l) { + if (_header(l,0)=='#') continue; + if (_header[l]._width==2 && _header(l,0)=='P') { + const char c = _header(l,1); + if (c=='f' || c=='F') { f_type = _pfm; break; } + if (c>='1' && c<='9') { f_type = _pnm; break; } + } + f_type = 0; break; + } + } + } catch (CImgIOException&) { } + cimg::exception_mode(omode); + return f_type; + } + + //! Load file from network as a local temporary file. + /** + \param url URL of the filename, as a C-string. + \param[out] filename_local C-string containing the path to a local copy of \c filename. + \param timeout Maximum time (in seconds) authorized for downloading the file from the URL. + \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure. + \param referer Referer used, as a C-string. + \return Value of \c filename_local. + \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download. + **/ + inline char *load_network(const char *const url, char *const filename_local, + const unsigned int timeout, const bool try_fallback, + const char *const referer) { + if (!url) + throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); + if (!filename_local) + throw CImgArgumentException("cimg::load_network(): Specified destination string is (null)."); + if (!network_mode()) + throw CImgIOException("cimg::load_network(): Loading files from network is disabled."); + + const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext; + CImg ext = CImg::string(_ext); + std::FILE *file = 0; + *filename_local = 0; + if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0; + else cimg::strwindows_reserved(ext); + do { + cimg_snprintf(filename_local,256,"%s%c%s%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data); + if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file); + } while (file); + +#ifdef cimg_use_curl + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + CURL *curl = 0; + CURLcode res; + curl = curl_easy_init(); + if (curl) { + file = cimg::fopen(filename_local,"wb"); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,file); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L); + curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); + if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout); + if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L); + if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + cimg::fseek(file,0,SEEK_END); // Check if file size is 0 + const cimg_ulong siz = cimg::ftell(file); + cimg::fclose(file); + if (siz>0 && res==CURLE_OK) { + cimg::exception_mode(omode); + return filename_local; + } else std::remove(filename_local); + } + } catch (...) { } + cimg::exception_mode(omode); + if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url); +#endif + + CImg command((unsigned int)std::strlen(url) + 64); + cimg::unused(try_fallback); + + // Try with 'curl' first. + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,timeout,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),timeout,filename_local, + CImg::string(url)._system_strescape().data()); + } else { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),filename_local, + CImg::string(url)._system_strescape().data()); + } + cimg::system(command, cimg::curl_path()); + + if (!(file=cimg::std_fopen(filename_local,"rb"))) { + + // Try with 'wget' otherwise. + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,timeout,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),timeout,filename_local, + CImg::string(url)._system_strescape().data()); + } else { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),filename_local, + CImg::string(url)._system_strescape().data()); + } + cimg::system(command, cimg::wget_path()); + + if (!(file=cimg::std_fopen(filename_local,"rb"))) + throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands " + "'wget' or 'curl'.",url); + cimg::fclose(file); + + // Try gunzip it. + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(filename_local,command); + cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"", + gunzip_path(),filename_local); + cimg::system(command, gunzip_path()); + file = cimg::std_fopen(filename_local,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(command,filename_local); + file = cimg::std_fopen(filename_local,"rb"); + } + } + cimg::fseek(file,0,SEEK_END); // Check if file size is 0 + if (std::ftell(file)<=0) + throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands " + "'wget' or 'curl'.",url); + cimg::fclose(file); + return filename_local; + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline cimg_uint64 tictoc(const bool is_tic) { + cimg::mutex(2); + static CImg times(64); + static unsigned int pos = 0; + const cimg_uint64 t1 = cimg::time(); + if (is_tic) { + // Tic + times[pos++] = t1; + if (pos>=times._width) + throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'."); + cimg::mutex(2,0); + return t1; + } + + // Toc + if (!pos) + throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made."); + const cimg_uint64 + t0 = times[--pos], + dt = t1>=t0?(t1 - t0):cimg::type::max(); + const unsigned int + edays = (unsigned int)(dt/86400000.), + ehours = (unsigned int)((dt - edays*86400000.)/3600000.), + emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.), + esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.), + ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.); + if (!edays && !ehours && !emin && !esec) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n", + cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal); + else { + if (!edays && !ehours && !emin) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal); + else { + if (!edays && !ehours) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal); + else{ + if (!edays) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal); + else{ + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal); + } + } + } + } + cimg::mutex(2,0); + return dt; + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const cimg_ulong size) { + static CImg res(256); + cimg::mutex(5); + if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":""); + else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); } + else if (size<1024*1024*1024LU) { + const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize); + } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); } + cimg::mutex(5,0); + return res; + } + + //! Display a simple dialog box, and wait for the user's response. + /** + \param title Title of the dialog window. + \param msg Main message displayed inside the dialog window. + \param button1_label Label of the 1st button. + \param button2_label Label of the 2nd button (\c 0 to hide button). + \param button3_label Label of the 3rd button (\c 0 to hide button). + \param button4_label Label of the 4th button (\c 0 to hide button). + \param button5_label Label of the 5th button (\c 0 to hide button). + \param button6_label Label of the 6th button (\c 0 to hide button). + \param logo Image logo displayed at the left of the main message. + \param is_centered Tells if the dialog window must be centered on the screen. + \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user. + \note + - Up to 6 buttons can be defined in the dialog window. + - The function returns when a user clicked one of the button or closed the dialog window. + - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box. + At least one button must be specified. + **/ + template + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const CImg& logo, const bool is_centered=false) { +#if cimg_display==0 + cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + logo._data,is_centered); + throw CImgIOException("cimg::dialog(): No display available."); +#else + static const unsigned char + black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; + + // Create buttons and canvas graphics + CImgList buttons, cbuttons, sbuttons; + if (button1_label) { + CImg().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons); + if (button2_label) { + CImg().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons); + if (button3_label) { + CImg().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons); + if (button4_label) { + CImg().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons); + if (button5_label) { + CImg().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons); + if (button6_label) { + CImg().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons); + }}}}}} + if (!buttons._width) + throw CImgArgumentException("cimg::dialog(): No buttons have been defined."); + cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3); + + unsigned int bw = 0, bh = 0; + cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); } + bw+=8; bh+=8; + if (bw<64) bw = 64; + if (bw>128) bw = 128; + if (bh<24) bh = 24; + if (bh>48) bh = 48; + + CImg button(bw,bh,1,3); + button.draw_rectangle(0,0,bw - 1,bh - 1,gray); + button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white); + button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black); + button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2); + CImg sbutton(bw,bh,1,3); + sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray); + sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black); + sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black); + sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white); + sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black); + sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2); + sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true). + draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); + sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false). + draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); + CImg cbutton(bw,bh,1,3); + cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2). + draw_rectangle(2,2,bw - 3,bh - 3,gray); + cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true). + draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); + cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false). + draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); + + cimglist_for(buttons,ll) { + CImg(cbutton). + draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]). + move_to(cbuttons); + CImg(sbutton). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). + move_to(sbuttons); + CImg(button). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). + move_to(buttons[ll]); + } + + CImg canvas; + if (msg) + ((CImg().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas); + + const unsigned int + bwall = (buttons._width - 1)*(12 + bw) + bw, + w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall), + h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh), + lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)), + ly = (h - 12 - bh - logo._height)/2, + tx = lx + logo._width + 12, + ty = (h - 12 - bh - canvas._height)/2, + bx = (w - bwall)/2, + by = h - 12 - bh; + + if (canvas._data) + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w - 1,h - 1,gray). + draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). + draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black). + draw_image(tx,ty,canvas); + else + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w - 1,h - 1,gray). + draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). + draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black); + if (logo._data) canvas.draw_image(lx,ly,logo); + + unsigned int xbuttons[6] = { 0 }; + cimglist_for(buttons,lll) { + xbuttons[lll] = bx + (bw + 12)*lll; + canvas.draw_image(xbuttons[lll],by,buttons[lll]); + } + + // Open window and enter events loop + CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false); + if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2, + (CImgDisplay::screen_height() - disp.height())/2); + bool stop_flag = false, refresh = false; + int oselected = -1, oclicked = -1, selected = -1, clicked = -1; + while (!disp.is_closed() && !stop_flag) { + if (refresh) { + if (clicked>=0) + CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); + else { + if (selected>=0) + CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); + else canvas.display(disp); + } + refresh = false; + } + disp.wait(15); + if (disp.is_resized()) disp.resize(disp,false); + + if (disp.button()&1) { + oclicked = clicked; + clicked = -1; + cimglist_for(buttons,l) + if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) && + disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) { + clicked = selected = l; + refresh = true; + } + if (clicked!=oclicked) refresh = true; + } else if (clicked>=0) stop_flag = true; + + if (disp.key()) { + oselected = selected; + switch (disp.key()) { + case cimg::keyESC : selected = -1; stop_flag = true; break; + case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break; + case cimg::keyTAB : + case cimg::keyARROWRIGHT : + case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break; + case cimg::keyARROWLEFT : + case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break; + } + disp.set_key(); + if (selected!=oselected) refresh = true; + } + } + if (!disp) selected = -1; + return selected; +#endif + } + + //! Display a simple dialog box, and wait for the user's response \specialization. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const bool is_centered) { + return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + CImg::_logo40x38(),is_centered); + } + + //! Evaluate math expression. + /** + \param expression C-string describing the formula to evaluate. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \return Result of the formula evaluation. + \note Set \c expression to \c 0 to keep evaluating the last specified \c expression. + \par Example + \code + const double + res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1' + res2 = cimg::eval(0,1,1); // will return '1' too + \endcode + **/ + inline double eval(const char *const expression, const double x, const double y, const double z, const double c) { + static const CImg empty; + return empty.eval(expression,x,y,z,c); + } + + template + inline CImg::type> eval(const char *const expression, const CImg& xyzc) { + static const CImg empty; + return empty.eval(expression,xyzc); + } + + } // namespace cimg { ... +} // namespace cimg_library { ... + +//! Short alias name. +namespace cil = cimg_library_suffixed; + +#ifdef _cimg_redefine_False +#define False 0 +#endif +#ifdef _cimg_redefine_True +#define True 1 +#endif +#ifdef _cimg_redefine_Status +#define Status int +#endif +#ifdef _cimg_redefine_Success +#define Success 0 +#endif +#ifdef _cimg_redefine_min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_PI +#define PI 3.141592653589793238462643383 +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +// Local Variables: +// mode: c++ +// End: diff --git a/libs/common/cnpy/cnpy.cpp b/libs/common/cnpy/cnpy.cpp new file mode 100644 index 0000000..ef021fe --- /dev/null +++ b/libs/common/cnpy/cnpy.cpp @@ -0,0 +1,401 @@ +// Copyright (C) 2011 Carl Rogers +// Released under MIT License +// license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php + +#include "cnpy.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +char cnpy::BigEndianTest(int size) +{ + if (size == 1) + return '|'; + int x = 1; + return (((char*)&x)[0]) ? '<' : '>'; +} + +char cnpy::map_type(const std::type_info& t) +{ + if (t == typeid(float)) + return 'f'; + if (t == typeid(double)) + return 'f'; + if (t == typeid(long double)) + return 'f'; + + if (t == typeid(int)) + return 'i'; + if (t == typeid(char)) + return 'i'; + if (t == typeid(signed char)) + return 'i'; + if (t == typeid(short)) + return 'i'; + if (t == typeid(long)) + return 'i'; + if (t == typeid(long long)) + return 'i'; + + if (t == typeid(unsigned char)) + return 'u'; + if (t == typeid(unsigned short)) + return 'u'; + if (t == typeid(unsigned long)) + return 'u'; + if (t == typeid(unsigned long long)) + return 'u'; + if (t == typeid(unsigned int)) + return 'u'; + + if (t == typeid(bool)) + return 'b'; + + if (t == typeid(std::complex)) + return 'c'; + if (t == typeid(std::complex)) + return 'c'; + if (t == typeid(std::complex)) + return 'c'; + + else + return '?'; +} + +template <> +std::vector& cnpy::operator+=(std::vector& lhs, const std::string rhs) +{ + lhs.insert(lhs.end(), rhs.begin(), rhs.end()); + return lhs; +} + +template <> +std::vector& cnpy::operator+=(std::vector& lhs, const char* rhs) +{ + // write in little endian + size_t len = strlen(rhs); + lhs.reserve(len); + for (size_t byte = 0; byte < len; byte++) { + lhs.push_back(rhs[byte]); + } + return lhs; +} + +void cnpy::parse_npy_header(unsigned char* buffer, size_t& word_size, std::vector& shape, bool& fortran_order, + std::string& typeName) +{ + // std::string magic_string(buffer,6); + uint8_t major_version = *reinterpret_cast(buffer + 6); + uint8_t minor_version = *reinterpret_cast(buffer + 7); + uint16_t header_len = *reinterpret_cast(buffer + 8); + std::string header(reinterpret_cast(buffer + 9), header_len); + + size_t loc1, loc2; + + // fortran order + loc1 = header.find("fortran_order") + 16; + fortran_order = (header.substr(loc1, 4) == "True" ? true : false); + if (fortran_order) + throw std::runtime_error("npy input file: 'fortran_order' must be false, use: arr2 = np.ascontiguousarray(arr1)"); + + // shape + loc1 = header.find("("); + loc2 = header.find(")"); + + std::regex num_regex("[0-9][0-9]*"); + std::smatch sm; + shape.clear(); + + std::string str_shape = header.substr(loc1 + 1, loc2 - loc1 - 1); + while (std::regex_search(str_shape, sm, num_regex)) { + shape.push_back(std::stoi(sm[0].str())); + str_shape = sm.suffix().str(); + } + + // endian, word size, data type + // byte order code | stands for not applicable. + // not sure when this applies except for byte array + loc1 = header.find("descr") + 9; + bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false); + assert(littleEndian); + + // char type = header[loc1+1]; + // assert(type == map_type(T)); + + std::string str_ws = header.substr(loc1 + 2); + loc2 = str_ws.find("'"); + word_size = atoi(str_ws.substr(0, loc2).c_str()); + if (header.substr(loc1 + 1, 1) == "i") { + typeName = "int"; + } else if (header.substr(loc1 + 1, 1) == "u") { + typeName = "uint"; + } else if (header.substr(loc1 + 1, 1) == "f") { + typeName = "float"; + } + typeName = typeName + std::to_string(word_size * 8); +} + +void cnpy::parse_npy_header(FILE* fp, size_t& word_size, std::vector& shape, bool& fortran_order, + std::string& typeName) +{ + char buffer[256]; + size_t res = fread(buffer, sizeof(char), 11, fp); + if (res != 11) + throw std::runtime_error("parse_npy_header: failed fread"); + std::string header = fgets(buffer, 256, fp); + assert(header[header.size() - 1] == '\n'); + + size_t loc1, loc2; + + // fortran order + loc1 = header.find("fortran_order"); + if (loc1 == std::string::npos) + throw std::runtime_error("parse_npy_header: failed to find header keyword: 'fortran_order'"); + loc1 += 16; + fortran_order = (header.substr(loc1, 4) == "True" ? true : false); + if (fortran_order) + throw std::runtime_error("npy input file: 'fortran_order' must be false, use: arr2 = np.ascontiguousarray(arr1)"); + + // shape + loc1 = header.find("("); + loc2 = header.find(")"); + if (loc1 == std::string::npos || loc2 == std::string::npos) + throw std::runtime_error("parse_npy_header: failed to find header keyword: '(' or ')'"); + + std::regex num_regex("[0-9][0-9]*"); + std::smatch sm; + shape.clear(); + + std::string str_shape = header.substr(loc1 + 1, loc2 - loc1 - 1); + while (std::regex_search(str_shape, sm, num_regex)) { + shape.push_back(std::stoi(sm[0].str())); + str_shape = sm.suffix().str(); + } + + // endian, word size, data type + // byte order code | stands for not applicable. + // not sure when this applies except for byte array + loc1 = header.find("descr"); + if (loc1 == std::string::npos) + throw std::runtime_error("parse_npy_header: failed to find header keyword: 'descr'"); + loc1 += 9; + bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false); + assert(littleEndian); + + // char type = header[loc1+1]; + // assert(type == map_type(T)); + + std::string str_ws = header.substr(loc1 + 2); + loc2 = str_ws.find("'"); + word_size = atoi(str_ws.substr(0, loc2).c_str()); + if (header.substr(loc1 + 1, 1) == "i") { + typeName = "int"; + } else if (header.substr(loc1 + 1, 1) == "u") { + typeName = "uint"; + } else if (header.substr(loc1 + 1, 1) == "f") { + typeName = "float"; + } + typeName = typeName + std::to_string(word_size * 8); +} + +void cnpy::parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size, size_t& global_header_offset) +{ + std::vector footer(22); + fseek(fp, -22, SEEK_END); + size_t res = fread(&footer[0], sizeof(char), 22, fp); + if (res != 22) + throw std::runtime_error("parse_zip_footer: failed fread"); + + uint16_t disk_no, disk_start, nrecs_on_disk, comment_len; + disk_no = *(uint16_t*)&footer[4]; + disk_start = *(uint16_t*)&footer[6]; + nrecs_on_disk = *(uint16_t*)&footer[8]; + nrecs = *(uint16_t*)&footer[10]; + global_header_size = *(uint32_t*)&footer[12]; + global_header_offset = *(uint32_t*)&footer[16]; + comment_len = *(uint16_t*)&footer[20]; + + assert(disk_no == 0); + assert(disk_start == 0); + assert(nrecs_on_disk == nrecs); + assert(comment_len == 0); +} + +cnpy::NpyArray load_the_npy_file(FILE* fp) +{ + std::vector shape; + size_t word_size; + std::string typeName; + bool fortran_order; + cnpy::parse_npy_header(fp, word_size, shape, fortran_order, typeName); + + cnpy::NpyArray arr(shape, word_size, fortran_order, typeName); + size_t nread = fread(arr.data(), 1, arr.num_bytes(), fp); + if (nread != arr.num_bytes()) + throw std::runtime_error("load_the_npy_file: failed fread"); + return arr; +} + +cnpy::NpyArray load_the_npz_array(FILE* fp, uint32_t compr_bytes, uint32_t uncompr_bytes) +{ + std::vector buffer_compr(compr_bytes); + std::vector buffer_uncompr(uncompr_bytes); + size_t nread = fread(&buffer_compr[0], 1, compr_bytes, fp); + if (nread != compr_bytes) + throw std::runtime_error("load_the_npy_file: failed fread"); + + int err; + z_stream d_stream; + + d_stream.zalloc = Z_NULL; + d_stream.zfree = Z_NULL; + d_stream.opaque = Z_NULL; + d_stream.avail_in = 0; + d_stream.next_in = Z_NULL; + err = inflateInit2(&d_stream, -MAX_WBITS); + + d_stream.avail_in = compr_bytes; + d_stream.next_in = &buffer_compr[0]; + d_stream.avail_out = uncompr_bytes; + d_stream.next_out = &buffer_uncompr[0]; + + err = inflate(&d_stream, Z_FINISH); + err = inflateEnd(&d_stream); + + std::vector shape; + size_t word_size; + bool fortran_order; + std::string typeName; + cnpy::parse_npy_header(&buffer_uncompr[0], word_size, shape, fortran_order, typeName); + + cnpy::NpyArray array(shape, word_size, fortran_order, typeName); + + size_t offset = uncompr_bytes - array.num_bytes(); + memcpy(array.data(), &buffer_uncompr[0] + offset, array.num_bytes()); + + return array; +} + +cnpy::npz_t cnpy::npz_load(std::string fname) +{ + FILE* fp = fopen(fname.c_str(), "rb"); + + if (!fp) { + throw std::runtime_error("npz_load: Error! Unable to open file " + fname + "!"); + } + + cnpy::npz_t arrays; + + while (1) { + std::vector local_header(30); + size_t headerres = fread(&local_header[0], sizeof(char), 30, fp); + if (headerres != 30) + throw std::runtime_error("npz_load: failed fread"); + + // if we've reached the global header, stop reading + if (local_header[2] != 0x03 || local_header[3] != 0x04) + break; + + // read in the variable name + uint16_t name_len = *(uint16_t*)&local_header[26]; + std::string varname(name_len, ' '); + size_t vname_res = fread(&varname[0], sizeof(char), name_len, fp); + if (vname_res != name_len) + throw std::runtime_error("npz_load: failed fread"); + + // erase the lagging .npy + varname.erase(varname.end() - 4, varname.end()); + + // read in the extra field + uint16_t extra_field_len = *(uint16_t*)&local_header[28]; + if (extra_field_len > 0) { + std::vector buff(extra_field_len); + size_t efield_res = fread(&buff[0], sizeof(char), extra_field_len, fp); + if (efield_res != extra_field_len) + throw std::runtime_error("npz_load: failed fread"); + } + + uint16_t compr_method = *reinterpret_cast(&local_header[0] + 8); + uint32_t compr_bytes = *reinterpret_cast(&local_header[0] + 18); + uint32_t uncompr_bytes = *reinterpret_cast(&local_header[0] + 22); + + if (compr_method == 0) { + arrays[varname] = load_the_npy_file(fp); + } else { + arrays[varname] = load_the_npz_array(fp, compr_bytes, uncompr_bytes); + } + } + + fclose(fp); + return arrays; +} + +cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) +{ + FILE* fp = fopen(fname.c_str(), "rb"); + + if (!fp) + throw std::runtime_error("npz_load: Unable to open file " + fname); + + while (1) { + std::vector local_header(30); + size_t header_res = fread(&local_header[0], sizeof(char), 30, fp); + if (header_res != 30) + throw std::runtime_error("npz_load: failed fread"); + + // if we've reached the global header, stop reading + if (local_header[2] != 0x03 || local_header[3] != 0x04) + break; + + // read in the variable name + uint16_t name_len = *(uint16_t*)&local_header[26]; + std::string vname(name_len, ' '); + size_t vname_res = fread(&vname[0], sizeof(char), name_len, fp); + if (vname_res != name_len) + throw std::runtime_error("npz_load: failed fread"); + vname.erase(vname.end() - 4, vname.end()); // erase the lagging .npy + + // read in the extra field + uint16_t extra_field_len = *(uint16_t*)&local_header[28]; + fseek(fp, extra_field_len, SEEK_CUR); // skip past the extra field + + uint16_t compr_method = *reinterpret_cast(&local_header[0] + 8); + uint32_t compr_bytes = *reinterpret_cast(&local_header[0] + 18); + uint32_t uncompr_bytes = *reinterpret_cast(&local_header[0] + 22); + + if (vname == varname) { + NpyArray array = (compr_method == 0) ? load_the_npy_file(fp) : load_the_npz_array(fp, compr_bytes, uncompr_bytes); + fclose(fp); + return array; + } else { + // skip past the data + uint32_t size = *(uint32_t*)&local_header[22]; + fseek(fp, size, SEEK_CUR); + } + } + + fclose(fp); + + // if we get here, we haven't found the variable in the file + throw std::runtime_error("npz_load: Variable name " + varname + " not found in " + fname); +} + +cnpy::NpyArray cnpy::npy_load(std::string fname) +{ + FILE* fp = fopen(fname.c_str(), "rb"); + + if (!fp) + throw std::runtime_error("npy_load: Unable to open file " + fname); + + NpyArray arr = load_the_npy_file(fp); + + fclose(fp); + return arr; +} diff --git a/libs/common/cnpy/cnpy.h b/libs/common/cnpy/cnpy.h new file mode 100644 index 0000000..4dbc48a --- /dev/null +++ b/libs/common/cnpy/cnpy.h @@ -0,0 +1,316 @@ +// Copyright (C) 2011 Carl Rogers +// Released under MIT License +// license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php + +#ifndef LIBCNPY_H_ +#define LIBCNPY_H_ + +// #include "Logging.h" +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cnpy { + +struct NpyArray +{ + NpyArray(const std::vector& _shape, size_t _word_size, bool _fortran_order, std::string _typeName) + : shape(_shape) + , word_size(_word_size) + , fortran_order(_fortran_order) + , typeName(_typeName) + { + num_vals = 1; + for (size_t i = 0; i < shape.size(); i++) + num_vals *= shape[i]; + data_holder = std::shared_ptr>(new std::vector(num_vals * word_size)); + } + + NpyArray() + : shape(0) + , word_size(0) + , fortran_order(0) + , num_vals(0) + {} + + template + T* data() + { + return reinterpret_cast(&(*data_holder)[0]); + } + + template + const T* data() const + { + return reinterpret_cast(&(*data_holder)[0]); + } + + template + std::vector as_vec() const + { + const T* p = data(); + return std::vector(p, p + num_vals); + } + + size_t num_bytes() const { return data_holder->size(); } + + std::shared_ptr> data_holder; + std::vector shape; + size_t word_size; + bool fortran_order; + size_t num_vals; + std::string typeName; +}; + +using npz_t = std::map; + +char BigEndianTest(int size); +char map_type(const std::type_info& t); +template +std::vector create_npy_header(const std::vector& shape); +void parse_npy_header(FILE* fp, size_t& word_size, std::vector& shape, bool& fortran_order, + std::string& typeName); +void parse_npy_header(unsigned char* buffer, size_t& word_size, std::vector& shape, bool& fortran_order, + std::string& typeName); +void parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size, size_t& global_header_offset); +npz_t npz_load(std::string fname); +NpyArray npz_load(std::string fname, std::string varname); +NpyArray npy_load(std::string fname); + +template +std::vector& operator+=(std::vector& lhs, const T rhs) +{ + // write in little endian + for (size_t byte = 0; byte < sizeof(T); byte++) { + char val = *((char*)&rhs + byte); + lhs.push_back(val); + } + return lhs; +} + +template <> +std::vector& operator+=(std::vector& lhs, const std::string rhs); +template <> +std::vector& operator+=(std::vector& lhs, const char* rhs); + +template +int npy_save(std::string fname, const T* data, const std::vector shape, std::string mode = "w") +{ + std::ofstream ofs(fname, std::ios::out); + if (!ofs.is_open()) { + return -1; + } + ofs.close(); + FILE* fp = NULL; + std::vector true_data_shape; // if appending, the shape of existing + new data + + if (mode == "a") + fp = fopen(fname.c_str(), "r+b"); + + if (fp) { + // file exists. we need to append to it. read the header, modify the array size + size_t word_size; + bool fortran_order; + std::string typeName; + parse_npy_header(fp, word_size, true_data_shape, fortran_order, typeName); + assert(!fortran_order); + + if (word_size != sizeof(T)) { + std::cout << "libnpy error: " << fname << " has word size " << word_size << " but npy_save appending data sized " + << sizeof(T) << "\n"; + assert(word_size == sizeof(T)); + } + if (true_data_shape.size() != shape.size()) { + std::cout << "libnpy error: npy_save attempting to append misdimensioned data to " << fname << "\n"; + assert(true_data_shape.size() != shape.size()); + } + + for (size_t i = 1; i < shape.size(); i++) { + if (shape[i] != true_data_shape[i]) { + std::cout << "libnpy error: npy_save attempting to append misshaped data to " << fname << "\n"; + assert(shape[i] == true_data_shape[i]); + } + } + true_data_shape[0] += shape[0]; + } else { + fp = fopen(fname.c_str(), "wb"); + true_data_shape = shape; + } + + std::vector header = create_npy_header(true_data_shape); + size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies()); + + fseek(fp, 0, SEEK_SET); + fwrite(&header[0], sizeof(char), header.size(), fp); + fseek(fp, 0, SEEK_END); + fwrite(data, sizeof(T), nels, fp); + fclose(fp); + return 0; +} + +template +void npz_save(std::string zipname, std::string fname, const T* data, const std::vector& shape, + std::string mode = "w") +{ + // first, append a .npy to the fname + fname += ".npy"; + + // now, on with the show + FILE* fp = NULL; + uint16_t nrecs = 0; + size_t global_header_offset = 0; + std::vector global_header; + + if (mode == "a") + fp = fopen(zipname.c_str(), "r+b"); + + if (fp) { + // zip file exists. we need to add a new npy file to it. + // first read the footer. this gives us the offset and size of the global header + // then read and store the global header. + // below, we will write the the new data at the start of the global header then append the global header and footer + // below it + size_t global_header_size; + parse_zip_footer(fp, nrecs, global_header_size, global_header_offset); + fseek(fp, global_header_offset, SEEK_SET); + global_header.resize(global_header_size); + size_t res = fread(&global_header[0], sizeof(char), global_header_size, fp); + if (res != global_header_size) { + throw std::runtime_error("npz_save: header read error while adding to existing zip"); + } + fseek(fp, global_header_offset, SEEK_SET); + } else { + fp = fopen(zipname.c_str(), "wb"); + } + + std::vector npy_header = create_npy_header(shape); + + size_t nels = std::accumulate(shape.begin(), shape.end(), 1, std::multiplies()); + size_t nbytes = nels * sizeof(T) + npy_header.size(); + + // get the CRC of the data to be added + uint32_t crc = crc32(0L, (uint8_t*)&npy_header[0], npy_header.size()); + crc = crc32(crc, (uint8_t*)data, nels * sizeof(T)); + + // build the local header + std::vector local_header; + local_header += "PK"; // first part of sig + local_header += (uint16_t)0x0403; // second part of sig + local_header += (uint16_t)20; // min version to extract + local_header += (uint16_t)0; // general purpose bit flag + local_header += (uint16_t)0; // compression method + local_header += (uint16_t)0; // file last mod time + local_header += (uint16_t)0; // file last mod date + local_header += (uint32_t)crc; // crc + local_header += (uint32_t)nbytes; // compressed size + local_header += (uint32_t)nbytes; // uncompressed size + local_header += (uint16_t)fname.size(); // fname length + local_header += (uint16_t)0; // extra field length + local_header += fname; + + // build global header + global_header += "PK"; // first part of sig + global_header += (uint16_t)0x0201; // second part of sig + global_header += (uint16_t)20; // version made by + global_header.insert(global_header.end(), local_header.begin() + 4, local_header.begin() + 30); + global_header += (uint16_t)0; // file comment length + global_header += (uint16_t)0; // disk number where file starts + global_header += (uint16_t)0; // internal file attributes + global_header += (uint32_t)0; // external file attributes + global_header += (uint32_t) + global_header_offset; // relative offset of local file header, since it begins where the global header used to begin + global_header += fname; + + // build footer + std::vector footer; + footer += "PK"; // first part of sig + footer += (uint16_t)0x0605; // second part of sig + footer += (uint16_t)0; // number of this disk + footer += (uint16_t)0; // disk where footer starts + footer += (uint16_t)(nrecs + 1); // number of records on this disk + footer += (uint16_t)(nrecs + 1); // total number of records + footer += (uint32_t)global_header.size(); // nbytes of global headers + footer += + (uint32_t)(global_header_offset + nbytes + local_header.size()); // offset of start of global headers, since global + // header now starts after newly written array + footer += (uint16_t)0; // zip file comment length + + // write everything + fwrite(&local_header[0], sizeof(char), local_header.size(), fp); + fwrite(&npy_header[0], sizeof(char), npy_header.size(), fp); + fwrite(data, sizeof(T), nels, fp); + fwrite(&global_header[0], sizeof(char), global_header.size(), fp); + fwrite(&footer[0], sizeof(char), footer.size(), fp); + fclose(fp); +} + +template +void npy_save(std::string fname, const std::vector data, std::string mode = "w") +{ + std::vector shape; + shape.push_back(data.size()); + npy_save(fname, &data[0], shape, mode); +} + +template +void npz_save(std::string zipname, std::string fname, const std::vector data, std::string mode = "w") +{ + std::vector shape; + shape.push_back(data.size()); + npz_save(zipname, fname, &data[0], shape, mode); +} + +template +std::vector create_npy_header(const std::vector& shape) +{ + const char* tpye_name = typeid(T).name(); + std::vector dict; + dict += "{'descr': '"; + dict += BigEndianTest(sizeof(T)); + if (std::string(tpye_name) == "N4rknn7float16E") { + dict += "f"; + } else { + dict += map_type(typeid(T)); + } + dict += std::to_string(sizeof(T)); + dict += "', 'fortran_order': False, 'shape': ("; + dict += std::to_string(shape[0]); + for (size_t i = 1; i < shape.size(); i++) { + dict += ", "; + dict += std::to_string(shape[i]); + } + if (shape.size() == 1) + dict += ","; + dict += "), }"; + // pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n + int remainder = 16 - (10 + dict.size()) % 16; + dict.insert(dict.end(), remainder, ' '); + dict.back() = '\n'; + + std::vector header; + header += (char)0x93; + header += "NUMPY"; + header += (char)0x01; // major version of numpy format + header += (char)0x00; // minor version of numpy format + header += (uint16_t)dict.size(); + header.insert(header.end(), dict.begin(), dict.end()); + + return header; +} + +} // namespace cnpy + +#endif diff --git a/libs/common/cnpy/logging.h b/libs/common/cnpy/logging.h new file mode 100644 index 0000000..4cd247e --- /dev/null +++ b/libs/common/cnpy/logging.h @@ -0,0 +1,1662 @@ +// Copyright (c) 1999, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Author: Ray Sidney +// +// This file contains #include information about logging-related stuff. +// Pretty much everybody needs to #include this file so that they can +// log various happenings. +// +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include +#include +#include +#include +#include +#include +#include +#if 1 +# include +#endif +#include + +#if defined(_MSC_VER) +#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) +#define GLOG_MSVC_POP_WARNING() __pragma(warning(pop)) +#else +#define GLOG_MSVC_PUSH_DISABLE_WARNING(n) +#define GLOG_MSVC_POP_WARNING() +#endif + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at logging.h.in to see how they're calculated (based on your config). +#if 1 +#include // the normal place uint16_t is defined +#endif +#if 1 +#include // the normal place u_int16_t is defined +#endif +#if 1 +#include // a third place for uint16_t or u_int16_t +#endif + +#if 0 +#include +#endif + +namespace google { + +#if 1 // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif 1 // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif 0 // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +} + +// The global value of GOOGLE_STRIP_LOG. All the messages logged to +// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed. +// If it can be determined at compile time that the message will not be +// printed, the statement will be compiled out. +// +// Example: to strip out all INFO and WARNING messages, use the value +// of 2 below. To make an exception for WARNING messages from a single +// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including +// base/logging.h +#ifndef GOOGLE_STRIP_LOG +#define GOOGLE_STRIP_LOG 0 +#endif + +// GCC can be told that a certain branch is not likely to be taken (for +// instance, a CHECK failure), and use that information in static analysis. +// Giving it this information can help it optimize for the common case in +// the absence of better information (ie. -fprofile-arcs). +// +#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN +#if 1 +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x +#endif +#endif + +#ifndef GOOGLE_PREDICT_FALSE +#if 1 +#define GOOGLE_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_FALSE(x) x +#endif +#endif + +#ifndef GOOGLE_PREDICT_TRUE +#if 1 +#define GOOGLE_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#else +#define GOOGLE_PREDICT_TRUE(x) x +#endif +#endif + + +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can capture log messages in a string, rather than reporting them +// immediately: +// +// vector errors; +// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; +// +// This pushes back the new error onto 'errors'; if given a NULL pointer, +// it reports the error via LOG(ERROR). +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// You can also do occasional logging (log every n'th occurrence of an +// event): +// +// LOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special google::COUNTER value is used +// to identify which repetition is happening. +// +// You can also do occasional conditional logging (log every n'th +// occurrence of an event, when condition is satisfied): +// +// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << google::COUNTER +// << "th big cookie"; +// +// You can log messages the first N times your code executes a line. E.g. +// +// LOG_FIRST_N(INFO, 20) << "Got the " << google::COUNTER << "th cookie"; +// +// Outputs log messages for the first 20 times it is executed. +// +// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. +// These log to syslog as well as to the normal logs. If you use these at +// all, you need to be aware that syslog can drastically reduce performance, +// especially if it is configured for remote logging! Don't use these +// unless you fully understand this and have a concrete need to use them. +// Even then, try to minimize your use of them. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// DLOG_EVERY_N(INFO, 10) << "Got the " << google::COUNTER << "th cookie"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} +// b. VLOG(1) and lower messages to be printed from file.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" +// d. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) wildcards. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" +// condition macros for sample cases, when some extra computation and +// preparation for logs is not needed. +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// VLOG_EVERY_N(1, 10) +// << "I'm printed every 10th occurrence, and when you run the program " +// "with --v=1 or more. Present occurence is " << google::COUNTER; +// VLOG_IF_EVERY_N(1, (size > 1024), 10) +// << "I'm printed on every 10th occurence of case when size is more " +// " than 1024, when you run the program with --v=1 or more. "; +// "Present occurence is " << google::COUNTER; +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// Note that messages of a given severity are logged not only in the +// logfile for that severity, but also in all logfiles of lower severity. +// E.g., a message of severity FATAL will be logged to the logfiles of +// severity FATAL, ERROR, WARNING, and INFO. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Unless otherwise specified, logs will be written to the filename +// "...log..", followed +// by the date, time, and pid (you can't prevent the date, time, and pid +// from being in the filename). +// +// The logging code takes two flags: +// --v=# set the verbose level +// --logtostderr log all the messages to stderr instead of to logfiles + +// LOG LINE PREFIX FORMAT +// +// Log lines have this form: +// +// Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +// +// where the fields are defined as follows: +// +// L A single character, representing the log level +// (eg 'I' for INFO) +// mm The month (zero padded; ie May is '05') +// dd The day (zero padded) +// hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds +// threadid The space-padded thread ID as returned by GetTID() +// (this matches the PID on Linux) +// file The file name +// line The line number +// msg The user-supplied message +// +// Example: +// +// I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog +// I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395 +// +// NOTE: although the microseconds are useful for comparing events on +// a single machine, clocks on different machines may not be well +// synchronized. Hence, use caution when comparing the low bits of +// timestamps from different machines. + +#ifndef DECLARE_VARIABLE +#define MUST_UNDEF_GFLAGS_DECLARE_MACROS +#define DECLARE_VARIABLE(type, shorttype, name, tn) \ + namespace fL##shorttype { \ + extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name + +// bool specialization +#define DECLARE_bool(name) \ + DECLARE_VARIABLE(bool, B, name, bool) + +// int32 specialization +#define DECLARE_int32(name) \ + DECLARE_VARIABLE(google::int32, I, name, int32) + +// Special case for string, because we have to specify the namespace +// std::string, which doesn't play nicely with our FLAG__namespace hackery. +#define DECLARE_string(name) \ + namespace fLS { \ + extern GOOGLE_GLOG_DLL_DECL std::string& FLAGS_##name; \ + } \ + using fLS::FLAGS_##name +#endif + +// Set whether log messages go to stderr instead of logfiles +DECLARE_bool(logtostderr); + +// Set whether log messages go to stderr in addition to logfiles. +DECLARE_bool(alsologtostderr); + +// Set color messages logged to stderr (if supported by terminal). +DECLARE_bool(colorlogtostderr); + +// Log messages at a level >= this flag are automatically sent to +// stderr in addition to log files. +DECLARE_int32(stderrthreshold); + +// Set whether the log prefix should be prepended to each line of output. +DECLARE_bool(log_prefix); + +// Log messages at a level <= this flag are buffered. +// Log messages at a higher level are flushed immediately. +DECLARE_int32(logbuflevel); + +// Sets the maximum number of seconds which logs may be buffered for. +DECLARE_int32(logbufsecs); + +// Log suppression level: messages logged at a lower level than this +// are suppressed. +DECLARE_int32(minloglevel); + +// If specified, logfiles are written into this directory instead of the +// default logging directory. +DECLARE_string(log_dir); + +// Set the log file mode. +DECLARE_int32(logfile_mode); + +// Sets the path of the directory into which to put additional links +// to the log files. +DECLARE_string(log_link); + +DECLARE_int32(v); // in vlog_is_on.cc + +// Sets the maximum log file size (in MB). +DECLARE_int32(max_log_size); + +// Sets whether to avoid logging to the disk if the disk is full. +DECLARE_bool(stop_logging_if_full_disk); + +#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef DECLARE_VARIABLE +#undef DECLARE_bool +#undef DECLARE_int32 +#undef DECLARE_string +#endif + +// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for +// security reasons. See LOG(severtiy) below. + +// A few definitions of macros that don't generate much code. Since +// LOG(INFO) and its ilk are used all over our code, it's +// better to have compact code for these operations. + +#if GOOGLE_STRIP_LOG == 0 +#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_INFO(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_INFO, message) +#else +#define COMPACT_GOOGLE_LOG_INFO google::NullStream() +#define LOG_TO_STRING_INFO(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 1 +#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_WARNING) +#define LOG_TO_STRING_WARNING(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_WARNING, message) +#else +#define COMPACT_GOOGLE_LOG_WARNING google::NullStream() +#define LOG_TO_STRING_WARNING(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 2 +#define COMPACT_GOOGLE_LOG_ERROR google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ERROR) +#define LOG_TO_STRING_ERROR(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ERROR, message) +#else +#define COMPACT_GOOGLE_LOG_ERROR google::NullStream() +#define LOG_TO_STRING_ERROR(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_FATAL(message) google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_FATAL, message) +#else +#define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal() +#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal() +#endif + +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define DCHECK_IS_ON() 0 +#else +#define DCHECK_IS_ON() 1 +#endif + +// For DFATAL, we want to use LogMessage (as opposed to +// LogMessageFatal), to be consistent with the original behavior. +#if !DCHECK_IS_ON() +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR +#elif GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_DFATAL google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_FATAL) +#else +#define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal() +#endif + +#define GOOGLE_LOG_INFO(counter) google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, &google::LogMessage::SendToLog) +#define SYSLOG_INFO(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_INFO, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_WARNING, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_FATAL, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToSyslogAndLog) + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) +// A very useful logging macro to log windows errors: +#define LOG_SYSRESULT(result) \ + if (FAILED(HRESULT_FROM_WIN32(result))) { \ + LPSTR message = NULL; \ + LPSTR msg = reinterpret_cast(&message); \ + DWORD message_length = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM, \ + 0, result, 0, msg, 100, NULL); \ + if (message_length > 0) { \ + google::LogMessage(__FILE__, __LINE__, google::GLOG_ERROR, 0, \ + &google::LogMessage::SendToLog).stream() \ + << reinterpret_cast(message); \ + LocalFree(message); \ + } \ + } +#endif + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define SYSLOG(severity) SYSLOG_ ## severity(0).stream() + +namespace google { + +// They need the definitions of integer types. +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Initialize google's logging library. You will see the program name +// specified by argv0 in log outputs. +GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0); + +// Shutdown google's logging library. +GOOGLE_GLOG_DLL_DECL void ShutdownGoogleLogging(); + +// Install a function which will be called after LOG(FATAL). +GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); + +class LogSink; // defined below + +// If a non-NULL sink pointer is given, we push this message to that sink. +// For LOG_TO_SINK we then do normal LOG(severity) logging as well. +// This is useful for capturing messages and passing/storing them +// somewhere more specific than the global log of the process. +// Argument types: +// LogSink* sink; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +#define LOG_TO_SINK(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::GLOG_ ## severity, \ + static_cast(sink), true).stream() +#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::GLOG_ ## severity, \ + static_cast(sink), false).stream() + +// If a non-NULL string pointer is given, we write this message to that string. +// We then do normal LOG(severity) logging as well. +// This is useful for capturing messages and storing them somewhere more +// specific than the global log of the process. +// Argument types: +// string* message; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +// NOTE: LOG(severity) expands to LogMessage().stream() for the specified +// severity. +#define LOG_TO_STRING(severity, message) \ + LOG_TO_STRING_##severity(static_cast(message)).stream() + +// If a non-NULL pointer is given, we push the message onto the end +// of a vector of strings; otherwise, we report it with LOG(severity). +// This is handy for capturing messages and perhaps passing them back +// to the caller, rather than reporting them immediately. +// Argument types: +// LogSeverity severity; +// vector *outvec; +// The cast is to disambiguate NULL arguments. +#define LOG_STRING(severity, outvec) \ + LOG_TO_STRING_##severity(static_cast*>(outvec)).stream() + +#define LOG_IF(severity, condition) \ + static_cast(0), \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) \ + static_cast(0), \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by DCHECK_IS_ON(), so the check will be executed regardless of +// compilation mode. Therefore, it is safe to do things like: +// CHECK(fp->Write(x) == 4) +#define CHECK(condition) \ + LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A container for a string pointer which can be evaluated to a bool - +// true iff the pointer is NULL. +struct CheckOpString { + CheckOpString(std::string* str) : str_(str) { } + // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), + // so there's no point in cleaning up str_. + operator bool() const { + return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL); + } + std::string* str_; +}; + +// Function is overloaded for integral types to allow static const +// integrals declared in classes and not defined to be used as arguments to +// CHECK* macros. It's not encouraged though. +template +inline const T& GetReferenceableValue(const T& t) { return t; } +inline char GetReferenceableValue(char t) { return t; } +inline unsigned char GetReferenceableValue(unsigned char t) { return t; } +inline signed char GetReferenceableValue(signed char t) { return t; } +inline short GetReferenceableValue(short t) { return t; } +inline unsigned short GetReferenceableValue(unsigned short t) { return t; } +inline int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline long GetReferenceableValue(long t) { return t; } +inline unsigned long GetReferenceableValue(unsigned long t) { return t; } +inline long long GetReferenceableValue(long long t) { return t; } +inline unsigned long long GetReferenceableValue(unsigned long long t) { + return t; +} + +// This is a dummy class to define the following operator. +struct DummyClassToDefineOperator {}; + +} + +// Define global operator<< to declare using ::operator<<. +// This declaration will allow use to use CHECK macros for user +// defined classes which have operator<< (e.g., stl_logging.h). +inline std::ostream& operator<<( + std::ostream& out, const google::DummyClassToDefineOperator&) { + return out; +} + +namespace google { + +// This formats a value for a failing CHECK_XX statement. Ordinarily, +// it uses the definition for operator<<, with a few special cases below. +template +inline void MakeCheckOpValueString(std::ostream* os, const T& v) { + (*os) << v; +} + +// Overrides for char types provide readable values for unprintable +// characters. +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const char& v); +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const signed char& v); +template <> GOOGLE_GLOG_DLL_DECL +void MakeCheckOpValueString(std::ostream* os, const unsigned char& v); + +// Build the error message string. Specify no inlining for code size. +template +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) + __attribute__((noinline)); + +namespace base { +namespace internal { + +// If "s" is less than base_logging::INFO, returns base_logging::INFO. +// If "s" is greater than base_logging::FATAL, returns +// base_logging::ERROR. Otherwise, returns "s". +LogSeverity NormalizeSeverity(LogSeverity s); + +} // namespace internal + +// A helper class for formatting "expr (V1 vs. V2)" in a CHECK_XX +// statement. See MakeCheckOpString for sample usage. Other +// approaches were considered: use of a template method (e.g., +// base::BuildCheckOpString(exprtext, base::Print, &v1, +// base::Print, &v2), however this approach has complications +// related to volatile arguments and function-pointer arguments). +class GOOGLE_GLOG_DLL_DECL CheckOpMessageBuilder { + public: + // Inserts "exprtext" and " (" to the stream. + explicit CheckOpMessageBuilder(const char *exprtext); + // Deletes "stream_". + ~CheckOpMessageBuilder(); + // For inserting the first variable. + std::ostream* ForVar1() { return stream_; } + // For inserting the second variable (adds an intermediate " vs. "). + std::ostream* ForVar2(); + // Get the result (inserts the closing ")"). + std::string* NewString(); + + private: + std::ostringstream *stream_; +}; + +} // namespace base + +template +std::string* MakeCheckOpString(const T1& v1, const T2& v2, const char* exprtext) { + base::CheckOpMessageBuilder comb(exprtext); + MakeCheckOpValueString(comb.ForVar1(), v1); + MakeCheckOpValueString(comb.ForVar2(), v2); + return comb.NewString(); +} + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* name##Impl(const T1& v1, const T2& v2, \ + const char* exprtext) { \ + if (GOOGLE_PREDICT_TRUE(v1 op v2)) return NULL; \ + else return MakeCheckOpString(v1, v2, exprtext); \ + } \ + inline std::string* name##Impl(int v1, int v2, const char* exprtext) { \ + return name##Impl(v1, v2, exprtext); \ + } + +// We use the full name Check_EQ, Check_NE, etc. in case the file including +// base/logging.h provides its own #defines for the simpler names EQ, NE, etc. +// This happens if, for example, those are used as token names in a +// yacc grammar. +DEFINE_CHECK_OP_IMPL(Check_EQ, ==) // Compilation error with CHECK_EQ(NULL, x)? +DEFINE_CHECK_OP_IMPL(Check_NE, !=) // Use CHECK(x == NULL) instead. +DEFINE_CHECK_OP_IMPL(Check_LE, <=) +DEFINE_CHECK_OP_IMPL(Check_LT, < ) +DEFINE_CHECK_OP_IMPL(Check_GE, >=) +DEFINE_CHECK_OP_IMPL(Check_GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. + +#if defined(STATIC_ANALYSIS) +// Only for static analysis tool to know that it is equivalent to assert +#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2)) +#elif DCHECK_IS_ON() +// In debug mode, avoid constructing CheckOpStrings if possible, +// to reduce the overhead of CHECK statments by 2x. +// Real DCHECK-heavy tests have seen 1.5x speedups. + +// The meaning of "string" might be different between now and +// when this macro gets invoked (e.g., if someone is experimenting +// with other string implementations that get defined after this +// file is included). Save the current meaning now and use it +// in the macro. +typedef std::string _Check_string; +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::_Check_string* _result = \ + google::Check##name##Impl( \ + google::GetReferenceableValue(val1), \ + google::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, \ + google::CheckOpString(_result)).stream() +#else +// In optimized mode, use CheckOpString to hint to compiler that +// the while condition is unlikely. +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::CheckOpString _result = \ + google::Check##name##Impl( \ + google::GetReferenceableValue(val1), \ + google::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, _result).stream() +#endif // STATIC_ANALYSIS, DCHECK_IS_ON() + +#if GOOGLE_STRIP_LOG <= 3 +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) +#else +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal) +#endif // STRIP_LOG <= 3 + +// Equality/Inequality checks - compare two values, and log a FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// CHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// CHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) + +// Check that the input is non NULL. This very useful in constructor +// initializer lists. + +#define CHECK_NOTNULL(val) \ + google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) + +// Helper functions for string comparisons. +// To avoid bloat, the definitions are in logging.cc. +#define DECLARE_CHECK_STROP_IMPL(func, expected) \ + GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \ + const char* s1, const char* s2, const char* names); +DECLARE_CHECK_STROP_IMPL(strcmp, true) +DECLARE_CHECK_STROP_IMPL(strcmp, false) +DECLARE_CHECK_STROP_IMPL(strcasecmp, true) +DECLARE_CHECK_STROP_IMPL(strcasecmp, false) +#undef DECLARE_CHECK_STROP_IMPL + +// Helper macro for string comparisons. +// Don't use this macro directly in your code, use CHECK_STREQ et al below. +#define CHECK_STROP(func, op, expected, s1, s2) \ + while (google::CheckOpString _result = \ + google::Check##func##expected##Impl((s1), (s2), \ + #s1 " " #op " " #s2)) \ + LOG(FATAL) << *_result.str_ + + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) +#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) +#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) +#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) + +#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) +#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) + +#define CHECK_DOUBLE_EQ(val1, val2) \ + do { \ + CHECK_LE((val1), (val2)+0.000000000000001L); \ + CHECK_GE((val1), (val2)-0.000000000000001L); \ + } while (0) + +#define CHECK_NEAR(val1, val2, margin) \ + do { \ + CHECK_LE((val1), (val2)+(margin)); \ + CHECK_GE((val1), (val2)-(margin)); \ + } while (0) + +// perror()..googly style! +// +// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and +// CHECK equivalents with the addition that they postpend a description +// of the current state of errno to their output lines. + +#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() + +#define GOOGLE_PLOG(severity, counter) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, counter, \ + &google::LogMessage::SendToLog) + +#define PLOG_IF(severity, condition) \ + static_cast(0), \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity) + +// A CHECK() macro that postpends errno if the condition is false. E.g. +// +// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } +#define PCHECK(condition) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A CHECK() macro that lets you assert the success of a function that +// returns -1 and sets errno in case of an error. E.g. +// +// CHECK_ERR(mkdir(path, 0700)); +// +// or +// +// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; +#define CHECK_ERR(invocation) \ +PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ + << #invocation + +// Use macro expansion to create, for each use of LOG_EVERY_N(), static +// variables with the __LINE__ expansion as part of the variable name. +#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) +#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line + +#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) +#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) + +#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (condition && \ + ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ + google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0; \ + if (LOG_OCCURRENCES <= n) \ + ++LOG_OCCURRENCES; \ + if (LOG_OCCURRENCES <= n) \ + google::LogMessage( \ + __FILE__, __LINE__, google::GLOG_ ## severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +namespace glog_internal_namespace_ { +template +struct CompileAssert { +}; +struct CrashReason; + +// Returns true if FailureSignalHandler is installed. +// Needs to be exported since it's used by the signalhandler_unittest. +GOOGLE_GLOG_DLL_DECL bool IsFailureSignalHandlerInstalled(); +} // namespace glog_internal_namespace_ + +#define LOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define SYSLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToSyslogAndLog) + +#define PLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_FIRST_N(severity, n) \ + SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_IF_EVERY_N(severity, condition, n) \ + SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), google::LogMessage::SendToLog) + +// We want the special COUNTER value available for LOG_EVERY_X()'ed messages +enum PRIVATE_Counter {COUNTER}; + +#ifdef GLOG_NO_ABBREVIATED_SEVERITIES +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR. +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +#define SYSLOG_0 SYSLOG_ERROR +#define LOG_TO_STRING_0 LOG_TO_STRING_ERROR +// Needed for LOG_IS_ON(ERROR). +const LogSeverity GLOG_0 = GLOG_ERROR; +#else +// Users may include windows.h after logging.h without +// GLOG_NO_ABBREVIATED_SEVERITIES nor WIN32_LEAN_AND_MEAN. +// For this case, we cannot detect if ERROR is defined before users +// actually use ERROR. Let's make an undefined symbol to warn users. +# define GLOG_ERROR_MSG ERROR_macro_is_defined_Define_GLOG_NO_ABBREVIATED_SEVERITIES_before_including_logging_h_See_the_document_for_detail +# define COMPACT_GOOGLE_LOG_0 GLOG_ERROR_MSG +# define SYSLOG_0 GLOG_ERROR_MSG +# define LOG_TO_STRING_0 GLOG_ERROR_MSG +# define GLOG_0 GLOG_ERROR_MSG +#endif + +// Plus some debug-logging macros that get compiled to nothing for production + +#if DCHECK_IS_ON() + +#define DLOG(severity) LOG(severity) +#define DVLOG(verboselevel) VLOG(verboselevel) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) +#define DLOG_IF_EVERY_N(severity, condition, n) \ + LOG_IF_EVERY_N(severity, condition, n) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. executed if DCHECK_IS_ON(). +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) +#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) +#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) +#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) +#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) +#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) +#define DCHECK_NOTNULL(val) CHECK_NOTNULL(val) +#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) +#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) +#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) +#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) + +#else // !DCHECK_IS_ON() + +#define DLOG(severity) \ + static_cast(0), \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DVLOG(verboselevel) \ + static_cast(0), \ + (true || !VLOG_IS_ON(verboselevel)) ? \ + (void) 0 : google::LogMessageVoidify() & LOG(INFO) + +#define DLOG_IF(severity, condition) \ + static_cast(0), \ + (true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_EVERY_N(severity, n) \ + static_cast(0), \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF_EVERY_N(severity, condition, n) \ + static_cast(0), \ + (true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + static_cast(0), \ + true ? (void) 0 : LOG_ASSERT(condition) + +// MSVC warning C4127: conditional expression is constant +#define DCHECK(condition) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK(condition) + +#define DCHECK_EQ(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_EQ(val1, val2) + +#define DCHECK_NE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_NE(val1, val2) + +#define DCHECK_LE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_LE(val1, val2) + +#define DCHECK_LT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_LT(val1, val2) + +#define DCHECK_GE(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_GE(val1, val2) + +#define DCHECK_GT(val1, val2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_GT(val1, val2) + +// You may see warnings in release mode if you don't use the return +// value of DCHECK_NOTNULL. Please just use DCHECK for such cases. +#define DCHECK_NOTNULL(val) (val) + +#define DCHECK_STREQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STREQ(str1, str2) + +#define DCHECK_STRCASEEQ(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRCASEEQ(str1, str2) + +#define DCHECK_STRNE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRNE(str1, str2) + +#define DCHECK_STRCASENE(str1, str2) \ + GLOG_MSVC_PUSH_DISABLE_WARNING(4127) \ + while (false) \ + GLOG_MSVC_POP_WARNING() CHECK_STRCASENE(str1, str2) + +#endif // DCHECK_IS_ON() + +// Log only in verbose mode. + +#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define VLOG_IF(verboselevel, condition) \ + LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) + +#define VLOG_EVERY_N(verboselevel, n) \ + LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) + +#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ + LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) + +namespace base_logging { + +// LogMessage::LogStream is a std::ostream backed by this streambuf. +// This class ignores overflow and leaves two bytes at the end of the +// buffer to allow for a '\n' and '\0'. +class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf { + public: + // REQUIREMENTS: "len" must be >= 2 to account for the '\n' and '\0'. + LogStreamBuf(char *buf, int len) { + setp(buf, buf + len - 2); + } + + // This effectively ignores overflow. + virtual int_type overflow(int_type ch) { + return ch; + } + + // Legacy public ostrstream method. + size_t pcount() const { return pptr() - pbase(); } + char* pbase() const { return std::streambuf::pbase(); } +}; + +} // namespace base_logging + +// +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class GOOGLE_GLOG_DLL_DECL LogMessage { +public: + enum { + // Passing kNoLogPrefix for the line number disables the + // log-message prefix. Useful for using the LogMessage + // infrastructure as a printing utility. See also the --log_prefix + // flag for controlling the log-message prefix on an + // application-wide basis. + kNoLogPrefix = -1 + }; + + // LogStream inherit from non-DLL-exported class (std::ostrstream) + // and VC++ produces a warning for this situation. + // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ + // 2005 if you are deriving from a type in the Standard C++ Library" + // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx + // Let's just ignore the warning. +GLOG_MSVC_PUSH_DISABLE_WARNING(4275) + class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostream { +GLOG_MSVC_POP_WARNING() + public: + LogStream(char *buf, int len, int ctr) + : std::ostream(NULL), + streambuf_(buf, len), + ctr_(ctr), + self_(this) { + rdbuf(&streambuf_); + } + + int ctr() const { return ctr_; } + void set_ctr(int ctr) { ctr_ = ctr; } + LogStream* self() const { return self_; } + + // Legacy std::streambuf methods. + size_t pcount() const { return streambuf_.pcount(); } + char* pbase() const { return streambuf_.pbase(); } + char* str() const { return pbase(); } + + private: + LogStream(const LogStream&); + LogStream& operator=(const LogStream&); + base_logging::LogStreamBuf streambuf_; + int ctr_; // Counter hack (for the LOG_EVERY_X() macro) + LogStream *self_; // Consistency check hack + }; + +public: + // icc 8 requires this typedef to avoid an internal compiler error. + typedef void (LogMessage::*SendMethod)(); + + LogMessage(const char* file, int line, LogSeverity severity, int ctr, + SendMethod send_method); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + + // Used for LOG(INFO): Implied are: + // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. + // + // Using this constructor instead of the more complex constructor above + // saves 19 bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0, send_method = &LogMessage::SendToLog + // + // Using this constructor instead of the more complex constructor above + // saves 17 bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // Constructor to log this message to a specified sink (if not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if + // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. + LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, + bool also_send_to_log); + + // Constructor where we also give a vector pointer + // for storing the messages (if the pointer is not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::vector* outvec); + + // Constructor where we also give a string pointer for storing the + // message (if the pointer is not NULL). Implied are: ctr = 0, + // send_method = &LogMessage::WriteToStringAndLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* message); + + // A special constructor used for check failures + LogMessage(const char* file, int line, const CheckOpString& result); + + ~LogMessage(); + + // Flush a buffered message to the sink set in the constructor. Always + // called by the destructor, it may also be called from elsewhere if + // needed. Only the first call is actioned; any later ones are ignored. + void Flush(); + + // An arbitrary limit on the length of a single log message. This + // is so that streaming can be done more efficiently. + static const size_t kMaxLogMessageLen; + + // Theses should not be called directly outside of logging.*, + // only passed as SendMethod arguments to other LogMessage methods: + void SendToLog(); // Actually dispatch to the logs + void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs + + // Call abort() or similar to perform LOG(FATAL) crash. + static void __attribute__((noreturn)) Fail(); + + std::ostream& stream(); + + int preserved_errno() const; + + // Must be called without the log_mutex held. (L < log_mutex) + static int64 num_messages(int severity); + + struct LogMessageData; + +private: + // Fully internal SendMethod cases: + void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs + void SendToSink(); // Send to sink if provided, do nothing otherwise. + + // Write to string if provided and dispatch to the logs. + void WriteToStringAndLog(); + + void SaveOrSendToLog(); // Save to stringvec if provided, else to logs + + void Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()); + + // Used to fill in crash information during LOG(FATAL) failures. + void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); + + // Counts of messages sent at each priority: + static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex + + // We keep the data in a separate struct so that each instance of + // LogMessage uses less stack space. + LogMessageData* allocated_; + LogMessageData* data_; + + friend class LogDestination; + + LogMessage(const LogMessage&); + void operator=(const LogMessage&); +}; + +// This class happens to be thread-hostile because all instances share +// a single data buffer, but since it can only be created just before +// the process dies, we don't worry so much. +class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage { + public: + LogMessageFatal(const char* file, int line); + LogMessageFatal(const char* file, int line, const CheckOpString& result); + __attribute__((noreturn)) ~LogMessageFatal(); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const severity, std::string const &msg) { + LogMessage(__FILE__, __LINE__, severity).stream() << msg; +} + +// A macro alternative of LogAtLevel. New code may want to use this +// version since there are two advantages: 1. this version outputs the +// file name and the line number where this macro is put like other +// LOG macros, 2. this macro can be used as C++ stream. +#define LOG_AT_LEVEL(severity) google::LogMessage(__FILE__, __LINE__, severity).stream() + +// Check if it's compiled in C++11 mode. +// +// GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least +// gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1 +// in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is +// defined according to the language version in effect thereafter. +// Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite +// reasonably good C++11 support, so we set LANG_CXX for it and +// newer versions (_MSC_VER >= 1900). +#if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ + (defined(_MSC_VER) && _MSC_VER >= 1900)) +// Helper for CHECK_NOTNULL(). +// +// In C++11, all cases can be handled by a single function. Since the value +// category of the argument is preserved (also for rvalue references), +// member initializer lists like the one below will compile correctly: +// +// Foo() +// : x_(CHECK_NOTNULL(MethodReturningUniquePtr())) {} +template +T CheckNotNull(const char* file, int line, const char* names, T&& t) { + if (t == nullptr) { + LogMessageFatal(file, line, new std::string(names)); + } + return std::forward(t); +} + +#else + +// A small helper for CHECK_NOTNULL(). +template +T* CheckNotNull(const char *file, int line, const char *names, T* t) { + if (t == NULL) { + LogMessageFatal(file, line, new std::string(names)); + } + return t; +} +#endif + +// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This +// only works if ostream is a LogStream. If the ostream is not a +// LogStream you'll get an assert saying as much at runtime. +GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os, + const PRIVATE_Counter&); + + +// Derived class for PLOG*() above. +class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage { + public: + + ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr, + void (LogMessage::*send_method)()); + + // Postpends ": strerror(errno) [errno]". + ~ErrnoLogMessage(); + + private: + ErrnoLogMessage(const ErrnoLogMessage&); + void operator=(const ErrnoLogMessage&); +}; + + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class GOOGLE_GLOG_DLL_DECL LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-safe. +GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity); + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-hostile because it ignores +// locking -- used for catastrophic failures. +GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity); + +// +// Set the destination to which a particular severity level of log +// messages is sent. If base_filename is "", it means "don't log this +// severity". Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity, + const char* base_filename); + +// +// Set the basename of the symlink to the latest log file at a given +// severity. If symlink_basename is empty, do not make a symlink. If +// you don't call this function, the symlink basename is the +// invocation name of the program. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + +// +// Used to send logs to some other kind of destination +// Users should subclass LogSink and override send to do whatever they want. +// Implementations must be thread-safe because a shared instance will +// be called from whichever thread ran the LOG(XXX) line. +class GOOGLE_GLOG_DLL_DECL LogSink { + public: + virtual ~LogSink(); + + // Sink's logging logic (message_len is such as to exclude '\n' at the end). + // This method can't use LOG() or CHECK() as logging system mutex(s) are held + // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) = 0; + + // Redefine this to implement waiting for + // the sink's logging logic to complete. + // It will be called after each send() returns, + // but before that LogMessage exits or crashes. + // By default this function does nothing. + // Using this function one can implement complex logic for send() + // that itself involves logging; and do all this w/o causing deadlocks and + // inconsistent rearrangement of log messages. + // E.g. if a LogSink has thread-specific actions, the send() method + // can simply add the message to a queue and wake up another thread that + // handles real logging while itself making some LOG() calls; + // WaitTillSent() can be implemented to wait for that logic to complete. + // See our unittest for an example. + virtual void WaitTillSent(); + + // Returns the normal text output of the log message. + // Can be useful to implement send(). + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); +}; + +// Add or remove a LogSink as a consumer of logging data. Thread-safe. +GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); +GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); + +// +// Specify an "extension" added to the filename specified via +// SetLogDestination. This applies to all severity levels. It's +// often used to append the port we're listening on to the logfile +// name. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( + const char* filename_extension); + +// +// Make it so that all log messages of at least a particular severity +// are logged to stderr (in addition to logging to the usual log +// file(s)). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); + +// +// Make it so that all log messages go only to stderr. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void LogToStderr(); + +// +// Make it so that all log messages of at least a particular severity are +// logged via email to a list of addresses (in addition to logging to the +// usual log file(s)). The list of addresses is just a string containing +// the email addresses to send to (separated by spaces, say). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, + const char* addresses); + +// A simple function that sends email. dest is a commma-separated +// list of addressess. Thread-safe. +GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, + const char *subject, const char *body); + +GOOGLE_GLOG_DLL_DECL const std::vector& GetLoggingDirectories(); + +// For tests only: Clear the internal [cached] list of logging directories to +// force a refresh the next time GetLoggingDirectories is called. +// Thread-hostile. +void TestOnly_ClearLoggingDirectoriesList(); + +// Returns a set of existing temporary directories, which will be a +// subset of the directories returned by GetLogginDirectories(). +// Thread-safe. +GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( + std::vector* list); + +// Print any fatal message again -- useful to call from signal handler +// so that the last thing in the output is the fatal message. +// Thread-hostile, but a race is unlikely. +GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); + +// Truncate a log file that may be the append-only output of multiple +// processes and hence can't simply be renamed/reopened (typically a +// stdout/stderr). If the file "path" is > "limit" bytes, copy the +// last "keep" bytes to offset 0 and truncate the rest. Since we could +// be racing with other writers, this approach has the potential to +// lose very small amounts of data. For security, only follow symlinks +// if the path is /proc/self/fd/* +GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, + int64 limit, int64 keep); + +// Truncate stdout and stderr if they are over the value specified by +// --max_log_size; keep the final 1MB. This function has the same +// race condition as TruncateLogFile. +GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); + +// Return the string representation of the provided LogSeverity level. +// Thread-safe. +GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity); + +// --------------------------------------------------------------------- +// Implementation details that are not useful to most clients +// --------------------------------------------------------------------- + +// A Logger is the interface used by logging modules to emit entries +// to a log. A typical implementation will dump formatted data to a +// sequence of files. We also provide interfaces that will forward +// the data to another thread so that the invoker never blocks. +// Implementations should be thread-safe since the logging system +// will write to them from multiple threads. + +namespace base { + +class GOOGLE_GLOG_DLL_DECL Logger { + public: + virtual ~Logger(); + + // Writes "message[0,message_len-1]" corresponding to an event that + // occurred at "timestamp". If "force_flush" is true, the log file + // is flushed immediately. + // + // The input message has already been formatted as deemed + // appropriate by the higher level logging facility. For example, + // textual log messages already contain timestamps, and the + // file:linenumber header. + virtual void Write(bool force_flush, + time_t timestamp, + const char* message, + int message_len) = 0; + + // Flush any buffered messages + virtual void Flush() = 0; + + // Get the current LOG file size. + // The returned value is approximate since some + // logged data may not have been flushed to disk yet. + virtual uint32 LogSize() = 0; +}; + +// Get the logger for the specified severity level. The logger +// remains the property of the logging module and should not be +// deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level); + +// Set the logger for the specified severity level. The logger +// becomes the property of the logging module and should not +// be deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger); + +} + +// glibc has traditionally implemented two incompatible versions of +// strerror_r(). There is a poorly defined convention for picking the +// version that we want, but it is not clear whether it even works with +// all versions of glibc. +// So, instead, we provide this wrapper that automatically detects the +// version that is in use, and then implements POSIX semantics. +// N.B. In addition to what POSIX says, we also guarantee that "buf" will +// be set to an empty string, if this function failed. This means, in most +// cases, you do not need to check the error code and you can directly +// use the value of "buf". It will never have an undefined value. +// DEPRECATED: Use StrError(int) instead. +GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len); + +// A thread-safe replacement for strerror(). Returns a string describing the +// given POSIX error code. +GOOGLE_GLOG_DLL_DECL std::string StrError(int err); + +// A class for which we define operator<<, which does nothing. +class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream { + public: + // Initialize the LogStream so the messages can be written somewhere + // (they'll never be actually displayed). This will be needed if a + // NullStream& is implicitly converted to LogStream&, in which case + // the overloaded NullStream::operator<< will not be invoked. + NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream(const char* /*file*/, int /*line*/, + const CheckOpString& /*result*/) : + LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream &stream() { return *this; } + private: + // A very short buffer for messages (which we discard anyway). This + // will be needed if NullStream& converted to LogStream& (e.g. as a + // result of a conditional expression). + char message_buffer_[2]; +}; + +// Do nothing. This operator is inline, allowing the message to be +// compiled away. The message will not be compiled away if we do +// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when +// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly +// converted to LogStream and the message will be computed and then +// quietly discarded. +template +inline NullStream& operator<<(NullStream &str, const T &) { return str; } + +// Similar to NullStream, but aborts the program (without stack +// trace), like LogMessageFatal. +class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream { + public: + NullStreamFatal() { } + NullStreamFatal(const char* file, int line, const CheckOpString& result) : + NullStream(file, line, result) { } + __attribute__((noreturn)) ~NullStreamFatal() throw () { _exit(1); } +}; + +// Install a signal handler that will dump signal information and a stack +// trace when the program crashes on certain signals. We'll install the +// signal handler for the following signals. +// +// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. +// +// By default, the signal handler will write the failure dump to the +// standard error. You can customize the destination by installing your +// own writer function by InstallFailureWriter() below. +// +// Note on threading: +// +// The function should be called before threads are created, if you want +// to use the failure signal handler for all threads. The stack trace +// will be shown only for the thread that receives the signal. In other +// words, stack traces of other threads won't be shown. +GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler(); + +// Installs a function that is used for writing the failure dump. "data" +// is the pointer to the beginning of a message to be written, and "size" +// is the size of the message. You should not expect the data is +// terminated with '\0'. +GOOGLE_GLOG_DLL_DECL void InstallFailureWriter( + void (*writer)(const char* data, int size)); + +} + +#endif // _LOGGING_H_ diff --git a/libs/common/drm/include/demo.h b/libs/common/drm/include/demo.h new file mode 100644 index 0000000..0aafc91 --- /dev/null +++ b/libs/common/drm/include/demo.h @@ -0,0 +1,136 @@ +/* + + */ + +#ifndef DEMO_H_ +#define DEMO_H_ +//ڴ˴ͷļ +#include "stdio.h" +#include "string.h" +#include "stdlib.h" +#include "math.h" + +#include "demo_define.h" +#include "tool.h" +#include "inital_alg_params_ynr.h" +#include "inital_alg_params_gic.h" +#include "inital_alg_params_lsc.h" +#include "inital_alg_params_lsc2.h" +#include "inital_alg_params_rk_shapren_HW.h" +#include "inital_alg_params_rk_edgefilter.h" + +#include "initial_alg_params_bayernr.h" + +#include "inital_alg_params_rkuvnr.h" +#include "inital_alg_params_rk_cnr.h" + +#include "inital_alg_params_mfnr.h" +#include "rk_aiq_awb_algo_v200.h" +#define FILE_RAW_EXT ".raw" +#define FILE_YUV_EXT ".yuv" +#define FILE_DAT_EXT ".dat" + +typedef enum YUV_FILE_FMT +{ + F_YUV_420SP = 0x00, + F_YUV_420P = 0x01, + F_YUV_422I = 0x02, + F_YUV_422SP = 0x03, + F_YUV_422P = 0x04, + F_YUV_444I = 0x05, + + F_YUV_MAX = 0x10, +}YUV_FILE_FMT_t; + +typedef enum INPUT_FILE_FMT +{ + F_IN_FMT_RAW = 0x00, + F_IN_FMT_YUV, + + + F_IN_FMT_MAX = 0x10, +}INPUT_FILE_FMT_t; + + + +//˴ +typedef struct tag_config_com +{ + int exp_info_en ; + int framenum ; + int rawwid ; + int rawhgt ; + int rawbit ; + int bayerfmt ; + int yuvbit ; + int yuvfmt ; +}tag_config_com; + +typedef struct tag_config_txt +{ + tag_config_com config_com; + + int framecnt ; + int iso ; + int exptime[3] ; + int expgain[3] ; + int rgain ; + int bgain ; + int grgain ; + int gbgain ; + int dGain ; + int lux ; +}tag_config_txt; + +typedef struct tag_ST_DEMO_INPUT_PARAMS +{ + int width; //rawͼ + int height; //rawͼ + int bayerPattern; //bayer patternʽ:0--BGGR,1--GBRG,2--GRBG,3--RGGB + int yuvFmt; //yuv file ʽ: YUV_FILE_FMT_t + int bitValue; //rawλ + int hdr_framenum; + float expGain[MAX_HDR_FRM_NUM]; // + float expTime[MAX_HDR_FRM_NUM]; //عʱ + int rGain; //wb rgain + int bGain; //wb bgain + int grGain; //wb grgain + int gbGain; //wb gbgain + int dGain; //wb gbgain + int fileFmt; //input file format:INPUT_FILE_FMT_t + int width_full; //rawͼ + int height_full; //rawͼ + int crop_width; + int crop_height; + int crop_xoffset; + int crop_yoffset; + + char pathFileCfg[256];//configļ· + char pathRawData[256];//rawͼ· + char nameRawData[256];//rawͼ + char pathExpInfo[256];//exp_infoļ· + char pathReslut[256];//ļ· + char suffix[256]; // ļ׺ַ + char pathRtlin[256]; //rtl in path + int skip_num; + int frame_end; + + int hdr_proc_mode; + int out_mode; + + + char dbgFlg[1024]; // must > ISP_CAP_MAX + int config_full; + + int exp_info_en; + int file_info_en; + FILE *fp_exp_info; +}ST_DEMO_INPUT_PARAMS; + + + +//˴ + + + +#endif // DEMO_H_ diff --git a/libs/common/drm/include/demo_define.h b/libs/common/drm/include/demo_define.h new file mode 100644 index 0000000..da7d26e --- /dev/null +++ b/libs/common/drm/include/demo_define.h @@ -0,0 +1,44 @@ +/* + + */ + +#ifndef DEFINE_H_ +#define DEFINE_H_ +//ڴ˴ͷļ + + + +#define S7_EDGE 1 +#define GZ 2 +#define DVR 3 +/* +#define ISP_BAYER_NR 1 +#define ISP_DPC 2 +#define ISP_BLC 3 +#define ISP_STAT_3A 4 +#define ISP_LSC 5 +#define ISP_AWBG 6 +#define ISP_VHDM 7 +#define ISP_GAMMA 8 +#define ISP_CSM 9 +#define ISP_DRC 10 +#define ISP_RGB2YUV 11 +#define ISP_LCE 12 +#define ISP_Y_NR 13 +#define ISP_SHARPEN 14 +#define ISP_SCALING 15 +#define ISP_GIC 16 +#define ISP_UV_NR 17 +#define ISP_HDR_MERGE 18 +#define ISP_HDR_TMO 19 +#define ISP_DPN 20 +#define ISP_CTK 21 +#define ISP_WDR 22 +*/ +#define SAVE_RESULT 1 + + +//#define GET_LSC_CALIBRATION_DATA //ȡlens shadingУ궨 + + +#endif // DEFINE_H_ diff --git a/libs/common/drm/include/libdrm/amdgpu_drm.h b/libs/common/drm/include/libdrm/amdgpu_drm.h new file mode 100644 index 0000000..4fe35d6 --- /dev/null +++ b/libs/common/drm/include/libdrm/amdgpu_drm.h @@ -0,0 +1,1067 @@ +/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2014 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __AMDGPU_DRM_H__ +#define __AMDGPU_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_AMDGPU_GEM_CREATE 0x00 +#define DRM_AMDGPU_GEM_MMAP 0x01 +#define DRM_AMDGPU_CTX 0x02 +#define DRM_AMDGPU_BO_LIST 0x03 +#define DRM_AMDGPU_CS 0x04 +#define DRM_AMDGPU_INFO 0x05 +#define DRM_AMDGPU_GEM_METADATA 0x06 +#define DRM_AMDGPU_GEM_WAIT_IDLE 0x07 +#define DRM_AMDGPU_GEM_VA 0x08 +#define DRM_AMDGPU_WAIT_CS 0x09 +#define DRM_AMDGPU_GEM_OP 0x10 +#define DRM_AMDGPU_GEM_USERPTR 0x11 +#define DRM_AMDGPU_WAIT_FENCES 0x12 +#define DRM_AMDGPU_VM 0x13 +#define DRM_AMDGPU_FENCE_TO_HANDLE 0x14 +#define DRM_AMDGPU_SCHED 0x15 + +#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) +#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) +#define DRM_IOCTL_AMDGPU_CTX DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx) +#define DRM_IOCTL_AMDGPU_BO_LIST DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list) +#define DRM_IOCTL_AMDGPU_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs) +#define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info) +#define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata) +#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle) +#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, struct drm_amdgpu_gem_va) +#define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs) +#define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op) +#define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) +#define DRM_IOCTL_AMDGPU_WAIT_FENCES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences) +#define DRM_IOCTL_AMDGPU_VM DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm) +#define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle) +#define DRM_IOCTL_AMDGPU_SCHED DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_SCHED, union drm_amdgpu_sched) + +/** + * DOC: memory domains + * + * %AMDGPU_GEM_DOMAIN_CPU System memory that is not GPU accessible. + * Memory in this pool could be swapped out to disk if there is pressure. + * + * %AMDGPU_GEM_DOMAIN_GTT GPU accessible system memory, mapped into the + * GPU's virtual address space via gart. Gart memory linearizes non-contiguous + * pages of system memory, allows GPU access system memory in a linezrized + * fashion. + * + * %AMDGPU_GEM_DOMAIN_VRAM Local video memory. For APUs, it is memory + * carved out by the BIOS. + * + * %AMDGPU_GEM_DOMAIN_GDS Global on-chip data storage used to share data + * across shader threads. + * + * %AMDGPU_GEM_DOMAIN_GWS Global wave sync, used to synchronize the + * execution of all the waves on a device. + * + * %AMDGPU_GEM_DOMAIN_OA Ordered append, used by 3D or Compute engines + * for appending data. + */ +#define AMDGPU_GEM_DOMAIN_CPU 0x1 +#define AMDGPU_GEM_DOMAIN_GTT 0x2 +#define AMDGPU_GEM_DOMAIN_VRAM 0x4 +#define AMDGPU_GEM_DOMAIN_GDS 0x8 +#define AMDGPU_GEM_DOMAIN_GWS 0x10 +#define AMDGPU_GEM_DOMAIN_OA 0x20 +#define AMDGPU_GEM_DOMAIN_MASK (AMDGPU_GEM_DOMAIN_CPU | \ + AMDGPU_GEM_DOMAIN_GTT | \ + AMDGPU_GEM_DOMAIN_VRAM | \ + AMDGPU_GEM_DOMAIN_GDS | \ + AMDGPU_GEM_DOMAIN_GWS | \ + AMDGPU_GEM_DOMAIN_OA) + +/* Flag that CPU access will be required for the case of VRAM domain */ +#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) +/* Flag that CPU access will not work, this VRAM domain is invisible */ +#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) +/* Flag that USWC attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2) +/* Flag that the memory should be in VRAM and cleared */ +#define AMDGPU_GEM_CREATE_VRAM_CLEARED (1 << 3) +/* Flag that create shadow bo(GTT) while allocating vram bo */ +#define AMDGPU_GEM_CREATE_SHADOW (1 << 4) +/* Flag that allocating the BO should use linear VRAM */ +#define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 5) +/* Flag that BO is always valid in this VM */ +#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 6) +/* Flag that BO sharing will be explicitly synchronized */ +#define AMDGPU_GEM_CREATE_EXPLICIT_SYNC (1 << 7) +/* Flag that indicates allocating MQD gart on GFX9, where the mtype + * for the second page onward should be set to NC. + */ +#define AMDGPU_GEM_CREATE_MQD_GFX9 (1 << 8) +/* Flag that BO may contain sensitive data that must be wiped before + * releasing the memory + */ +#define AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE (1 << 9) + +struct drm_amdgpu_gem_create_in { + /** the requested memory size */ + __u64 bo_size; + /** physical start_addr alignment in bytes for some HW requirements */ + __u64 alignment; + /** the requested memory domains */ + __u64 domains; + /** allocation flags */ + __u64 domain_flags; +}; + +struct drm_amdgpu_gem_create_out { + /** returned GEM object handle */ + __u32 handle; + __u32 _pad; +}; + +union drm_amdgpu_gem_create { + struct drm_amdgpu_gem_create_in in; + struct drm_amdgpu_gem_create_out out; +}; + +/** Opcode to create new residency list. */ +#define AMDGPU_BO_LIST_OP_CREATE 0 +/** Opcode to destroy previously created residency list */ +#define AMDGPU_BO_LIST_OP_DESTROY 1 +/** Opcode to update resource information in the list */ +#define AMDGPU_BO_LIST_OP_UPDATE 2 + +struct drm_amdgpu_bo_list_in { + /** Type of operation */ + __u32 operation; + /** Handle of list or 0 if we want to create one */ + __u32 list_handle; + /** Number of BOs in list */ + __u32 bo_number; + /** Size of each element describing BO */ + __u32 bo_info_size; + /** Pointer to array describing BOs */ + __u64 bo_info_ptr; +}; + +struct drm_amdgpu_bo_list_entry { + /** Handle of BO */ + __u32 bo_handle; + /** New (if specified) BO priority to be used during migration */ + __u32 bo_priority; +}; + +struct drm_amdgpu_bo_list_out { + /** Handle of resource list */ + __u32 list_handle; + __u32 _pad; +}; + +union drm_amdgpu_bo_list { + struct drm_amdgpu_bo_list_in in; + struct drm_amdgpu_bo_list_out out; +}; + +/* context related */ +#define AMDGPU_CTX_OP_ALLOC_CTX 1 +#define AMDGPU_CTX_OP_FREE_CTX 2 +#define AMDGPU_CTX_OP_QUERY_STATE 3 +#define AMDGPU_CTX_OP_QUERY_STATE2 4 + +/* GPU reset status */ +#define AMDGPU_CTX_NO_RESET 0 +/* this the context caused it */ +#define AMDGPU_CTX_GUILTY_RESET 1 +/* some other context caused it */ +#define AMDGPU_CTX_INNOCENT_RESET 2 +/* unknown cause */ +#define AMDGPU_CTX_UNKNOWN_RESET 3 + +/* indicate gpu reset occured after ctx created */ +#define AMDGPU_CTX_QUERY2_FLAGS_RESET (1<<0) +/* indicate vram lost occured after ctx created */ +#define AMDGPU_CTX_QUERY2_FLAGS_VRAMLOST (1<<1) +/* indicate some job from this context once cause gpu hang */ +#define AMDGPU_CTX_QUERY2_FLAGS_GUILTY (1<<2) +/* indicate some errors are detected by RAS */ +#define AMDGPU_CTX_QUERY2_FLAGS_RAS_CE (1<<3) +#define AMDGPU_CTX_QUERY2_FLAGS_RAS_UE (1<<4) + +/* Context priority level */ +#define AMDGPU_CTX_PRIORITY_UNSET -2048 +#define AMDGPU_CTX_PRIORITY_VERY_LOW -1023 +#define AMDGPU_CTX_PRIORITY_LOW -512 +#define AMDGPU_CTX_PRIORITY_NORMAL 0 +/* + * When used in struct drm_amdgpu_ctx_in, a priority above NORMAL requires + * CAP_SYS_NICE or DRM_MASTER +*/ +#define AMDGPU_CTX_PRIORITY_HIGH 512 +#define AMDGPU_CTX_PRIORITY_VERY_HIGH 1023 + +struct drm_amdgpu_ctx_in { + /** AMDGPU_CTX_OP_* */ + __u32 op; + /** For future use, no flags defined so far */ + __u32 flags; + __u32 ctx_id; + /** AMDGPU_CTX_PRIORITY_* */ + __s32 priority; +}; + +union drm_amdgpu_ctx_out { + struct { + __u32 ctx_id; + __u32 _pad; + } alloc; + + struct { + /** For future use, no flags defined so far */ + __u64 flags; + /** Number of resets caused by this context so far. */ + __u32 hangs; + /** Reset status since the last call of the ioctl. */ + __u32 reset_status; + } state; +}; + +union drm_amdgpu_ctx { + struct drm_amdgpu_ctx_in in; + union drm_amdgpu_ctx_out out; +}; + +/* vm ioctl */ +#define AMDGPU_VM_OP_RESERVE_VMID 1 +#define AMDGPU_VM_OP_UNRESERVE_VMID 2 + +struct drm_amdgpu_vm_in { + /** AMDGPU_VM_OP_* */ + __u32 op; + __u32 flags; +}; + +struct drm_amdgpu_vm_out { + /** For future use, no flags defined so far */ + __u64 flags; +}; + +union drm_amdgpu_vm { + struct drm_amdgpu_vm_in in; + struct drm_amdgpu_vm_out out; +}; + +/* sched ioctl */ +#define AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE 1 +#define AMDGPU_SCHED_OP_CONTEXT_PRIORITY_OVERRIDE 2 + +struct drm_amdgpu_sched_in { + /* AMDGPU_SCHED_OP_* */ + __u32 op; + __u32 fd; + /** AMDGPU_CTX_PRIORITY_* */ + __s32 priority; + __u32 ctx_id; +}; + +union drm_amdgpu_sched { + struct drm_amdgpu_sched_in in; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define AMDGPU_GEM_USERPTR_READONLY (1 << 0) +#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1) +#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2) +#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_amdgpu_gem_userptr { + __u64 addr; + __u64 size; + /* AMDGPU_GEM_USERPTR_* */ + __u32 flags; + /* Resulting GEM handle */ + __u32 handle; +}; + +/* SI-CI-VI: */ +/* same meaning as the GB_TILE_MODE and GL_MACRO_TILE_MODE fields */ +#define AMDGPU_TILING_ARRAY_MODE_SHIFT 0 +#define AMDGPU_TILING_ARRAY_MODE_MASK 0xf +#define AMDGPU_TILING_PIPE_CONFIG_SHIFT 4 +#define AMDGPU_TILING_PIPE_CONFIG_MASK 0x1f +#define AMDGPU_TILING_TILE_SPLIT_SHIFT 9 +#define AMDGPU_TILING_TILE_SPLIT_MASK 0x7 +#define AMDGPU_TILING_MICRO_TILE_MODE_SHIFT 12 +#define AMDGPU_TILING_MICRO_TILE_MODE_MASK 0x7 +#define AMDGPU_TILING_BANK_WIDTH_SHIFT 15 +#define AMDGPU_TILING_BANK_WIDTH_MASK 0x3 +#define AMDGPU_TILING_BANK_HEIGHT_SHIFT 17 +#define AMDGPU_TILING_BANK_HEIGHT_MASK 0x3 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_SHIFT 19 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_MASK 0x3 +#define AMDGPU_TILING_NUM_BANKS_SHIFT 21 +#define AMDGPU_TILING_NUM_BANKS_MASK 0x3 + +/* GFX9 and later: */ +#define AMDGPU_TILING_SWIZZLE_MODE_SHIFT 0 +#define AMDGPU_TILING_SWIZZLE_MODE_MASK 0x1f +#define AMDGPU_TILING_DCC_OFFSET_256B_SHIFT 5 +#define AMDGPU_TILING_DCC_OFFSET_256B_MASK 0xFFFFFF +#define AMDGPU_TILING_DCC_PITCH_MAX_SHIFT 29 +#define AMDGPU_TILING_DCC_PITCH_MAX_MASK 0x3FFF +#define AMDGPU_TILING_DCC_INDEPENDENT_64B_SHIFT 43 +#define AMDGPU_TILING_DCC_INDEPENDENT_64B_MASK 0x1 + +/* Set/Get helpers for tiling flags. */ +#define AMDGPU_TILING_SET(field, value) \ + (((__u64)(value) & AMDGPU_TILING_##field##_MASK) << AMDGPU_TILING_##field##_SHIFT) +#define AMDGPU_TILING_GET(value, field) \ + (((__u64)(value) >> AMDGPU_TILING_##field##_SHIFT) & AMDGPU_TILING_##field##_MASK) + +#define AMDGPU_GEM_METADATA_OP_SET_METADATA 1 +#define AMDGPU_GEM_METADATA_OP_GET_METADATA 2 + +/** The same structure is shared for input/output */ +struct drm_amdgpu_gem_metadata { + /** GEM Object handle */ + __u32 handle; + /** Do we want get or set metadata */ + __u32 op; + struct { + /** For future use, no flags defined so far */ + __u64 flags; + /** family specific tiling info */ + __u64 tiling_info; + __u32 data_size_bytes; + __u32 data[64]; + } data; +}; + +struct drm_amdgpu_gem_mmap_in { + /** the GEM object handle */ + __u32 handle; + __u32 _pad; +}; + +struct drm_amdgpu_gem_mmap_out { + /** mmap offset from the vma offset manager */ + __u64 addr_ptr; +}; + +union drm_amdgpu_gem_mmap { + struct drm_amdgpu_gem_mmap_in in; + struct drm_amdgpu_gem_mmap_out out; +}; + +struct drm_amdgpu_gem_wait_idle_in { + /** GEM object handle */ + __u32 handle; + /** For future use, no flags defined so far */ + __u32 flags; + /** Absolute timeout to wait */ + __u64 timeout; +}; + +struct drm_amdgpu_gem_wait_idle_out { + /** BO status: 0 - BO is idle, 1 - BO is busy */ + __u32 status; + /** Returned current memory domain */ + __u32 domain; +}; + +union drm_amdgpu_gem_wait_idle { + struct drm_amdgpu_gem_wait_idle_in in; + struct drm_amdgpu_gem_wait_idle_out out; +}; + +struct drm_amdgpu_wait_cs_in { + /* Command submission handle + * handle equals 0 means none to wait for + * handle equals ~0ull means wait for the latest sequence number + */ + __u64 handle; + /** Absolute timeout to wait */ + __u64 timeout; + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u32 ctx_id; +}; + +struct drm_amdgpu_wait_cs_out { + /** CS status: 0 - CS completed, 1 - CS still busy */ + __u64 status; +}; + +union drm_amdgpu_wait_cs { + struct drm_amdgpu_wait_cs_in in; + struct drm_amdgpu_wait_cs_out out; +}; + +struct drm_amdgpu_fence { + __u32 ctx_id; + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u64 seq_no; +}; + +struct drm_amdgpu_wait_fences_in { + /** This points to uint64_t * which points to fences */ + __u64 fences; + __u32 fence_count; + __u32 wait_all; + __u64 timeout_ns; +}; + +struct drm_amdgpu_wait_fences_out { + __u32 status; + __u32 first_signaled; +}; + +union drm_amdgpu_wait_fences { + struct drm_amdgpu_wait_fences_in in; + struct drm_amdgpu_wait_fences_out out; +}; + +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_PLACEMENT 1 + +/* Sets or returns a value associated with a buffer. */ +struct drm_amdgpu_gem_op { + /** GEM object handle */ + __u32 handle; + /** AMDGPU_GEM_OP_* */ + __u32 op; + /** Input or return value */ + __u64 value; +}; + +#define AMDGPU_VA_OP_MAP 1 +#define AMDGPU_VA_OP_UNMAP 2 +#define AMDGPU_VA_OP_CLEAR 3 +#define AMDGPU_VA_OP_REPLACE 4 + +/* Delay the page table update till the next CS */ +#define AMDGPU_VM_DELAY_UPDATE (1 << 0) + +/* Mapping flags */ +/* readable mapping */ +#define AMDGPU_VM_PAGE_READABLE (1 << 1) +/* writable mapping */ +#define AMDGPU_VM_PAGE_WRITEABLE (1 << 2) +/* executable mapping, new for VI */ +#define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) +/* partially resident texture */ +#define AMDGPU_VM_PAGE_PRT (1 << 4) +/* MTYPE flags use bit 5 to 8 */ +#define AMDGPU_VM_MTYPE_MASK (0xf << 5) +/* Default MTYPE. Pre-AI must use this. Recommended for newer ASICs. */ +#define AMDGPU_VM_MTYPE_DEFAULT (0 << 5) +/* Use NC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_NC (1 << 5) +/* Use WC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_WC (2 << 5) +/* Use CC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_CC (3 << 5) +/* Use UC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_UC (4 << 5) + +struct drm_amdgpu_gem_va { + /** GEM object handle */ + __u32 handle; + __u32 _pad; + /** AMDGPU_VA_OP_* */ + __u32 operation; + /** AMDGPU_VM_PAGE_* */ + __u32 flags; + /** va address to assign . Must be correctly aligned.*/ + __u64 va_address; + /** Specify offset inside of BO to assign. Must be correctly aligned.*/ + __u64 offset_in_bo; + /** Specify mapping size. Must be correctly aligned. */ + __u64 map_size; +}; + +#define AMDGPU_HW_IP_GFX 0 +#define AMDGPU_HW_IP_COMPUTE 1 +#define AMDGPU_HW_IP_DMA 2 +#define AMDGPU_HW_IP_UVD 3 +#define AMDGPU_HW_IP_VCE 4 +#define AMDGPU_HW_IP_UVD_ENC 5 +#define AMDGPU_HW_IP_VCN_DEC 6 +#define AMDGPU_HW_IP_VCN_ENC 7 +#define AMDGPU_HW_IP_VCN_JPEG 8 +#define AMDGPU_HW_IP_NUM 9 + +#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1 + +#define AMDGPU_CHUNK_ID_IB 0x01 +#define AMDGPU_CHUNK_ID_FENCE 0x02 +#define AMDGPU_CHUNK_ID_DEPENDENCIES 0x03 +#define AMDGPU_CHUNK_ID_SYNCOBJ_IN 0x04 +#define AMDGPU_CHUNK_ID_SYNCOBJ_OUT 0x05 +#define AMDGPU_CHUNK_ID_BO_HANDLES 0x06 +#define AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES 0x07 +#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT 0x08 +#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL 0x09 + +struct drm_amdgpu_cs_chunk { + __u32 chunk_id; + __u32 length_dw; + __u64 chunk_data; +}; + +struct drm_amdgpu_cs_in { + /** Rendering context id */ + __u32 ctx_id; + /** Handle of resource list associated with CS */ + __u32 bo_list_handle; + __u32 num_chunks; + __u32 _pad; + /** this points to __u64 * which point to cs chunks */ + __u64 chunks; +}; + +struct drm_amdgpu_cs_out { + __u64 handle; +}; + +union drm_amdgpu_cs { + struct drm_amdgpu_cs_in in; + struct drm_amdgpu_cs_out out; +}; + +/* Specify flags to be used for IB */ + +/* This IB should be submitted to CE */ +#define AMDGPU_IB_FLAG_CE (1<<0) + +/* Preamble flag, which means the IB could be dropped if no context switch */ +#define AMDGPU_IB_FLAG_PREAMBLE (1<<1) + +/* Preempt flag, IB should set Pre_enb bit if PREEMPT flag detected */ +#define AMDGPU_IB_FLAG_PREEMPT (1<<2) + +/* The IB fence should do the L2 writeback but not invalidate any shader + * caches (L2/vL1/sL1/I$). */ +#define AMDGPU_IB_FLAG_TC_WB_NOT_INVALIDATE (1 << 3) + +/* Set GDS_COMPUTE_MAX_WAVE_ID = DEFAULT before PACKET3_INDIRECT_BUFFER. + * This will reset wave ID counters for the IB. + */ +#define AMDGPU_IB_FLAG_RESET_GDS_MAX_WAVE_ID (1 << 4) + +struct drm_amdgpu_cs_chunk_ib { + __u32 _pad; + /** AMDGPU_IB_FLAG_* */ + __u32 flags; + /** Virtual address to begin IB execution */ + __u64 va_start; + /** Size of submission */ + __u32 ib_bytes; + /** HW IP to submit to */ + __u32 ip_type; + /** HW IP index of the same type to submit to */ + __u32 ip_instance; + /** Ring index to submit to */ + __u32 ring; +}; + +struct drm_amdgpu_cs_chunk_dep { + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u32 ctx_id; + __u64 handle; +}; + +struct drm_amdgpu_cs_chunk_fence { + __u32 handle; + __u32 offset; +}; + +struct drm_amdgpu_cs_chunk_sem { + __u32 handle; +}; + +struct drm_amdgpu_cs_chunk_syncobj { + __u32 handle; + __u32 flags; + __u64 point; +}; + +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ 0 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD 1 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNC_FILE_FD 2 + +union drm_amdgpu_fence_to_handle { + struct { + struct drm_amdgpu_fence fence; + __u32 what; + __u32 pad; + } in; + struct { + __u32 handle; + } out; +}; + +struct drm_amdgpu_cs_chunk_data { + union { + struct drm_amdgpu_cs_chunk_ib ib_data; + struct drm_amdgpu_cs_chunk_fence fence_data; + }; +}; + +/** + * Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU + * + */ +#define AMDGPU_IDS_FLAGS_FUSION 0x1 +#define AMDGPU_IDS_FLAGS_PREEMPTION 0x2 + +/* indicate if acceleration can be working */ +#define AMDGPU_INFO_ACCEL_WORKING 0x00 +/* get the crtc_id from the mode object id? */ +#define AMDGPU_INFO_CRTC_FROM_ID 0x01 +/* query hw IP info */ +#define AMDGPU_INFO_HW_IP_INFO 0x02 +/* query hw IP instance count for the specified type */ +#define AMDGPU_INFO_HW_IP_COUNT 0x03 +/* timestamp for GL_ARB_timer_query */ +#define AMDGPU_INFO_TIMESTAMP 0x05 +/* Query the firmware version */ +#define AMDGPU_INFO_FW_VERSION 0x0e + /* Subquery id: Query VCE firmware version */ + #define AMDGPU_INFO_FW_VCE 0x1 + /* Subquery id: Query UVD firmware version */ + #define AMDGPU_INFO_FW_UVD 0x2 + /* Subquery id: Query GMC firmware version */ + #define AMDGPU_INFO_FW_GMC 0x03 + /* Subquery id: Query GFX ME firmware version */ + #define AMDGPU_INFO_FW_GFX_ME 0x04 + /* Subquery id: Query GFX PFP firmware version */ + #define AMDGPU_INFO_FW_GFX_PFP 0x05 + /* Subquery id: Query GFX CE firmware version */ + #define AMDGPU_INFO_FW_GFX_CE 0x06 + /* Subquery id: Query GFX RLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC 0x07 + /* Subquery id: Query GFX MEC firmware version */ + #define AMDGPU_INFO_FW_GFX_MEC 0x08 + /* Subquery id: Query SMC firmware version */ + #define AMDGPU_INFO_FW_SMC 0x0a + /* Subquery id: Query SDMA firmware version */ + #define AMDGPU_INFO_FW_SDMA 0x0b + /* Subquery id: Query PSP SOS firmware version */ + #define AMDGPU_INFO_FW_SOS 0x0c + /* Subquery id: Query PSP ASD firmware version */ + #define AMDGPU_INFO_FW_ASD 0x0d + /* Subquery id: Query VCN firmware version */ + #define AMDGPU_INFO_FW_VCN 0x0e + /* Subquery id: Query GFX RLC SRLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_CNTL 0x0f + /* Subquery id: Query GFX RLC SRLG firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_GPM_MEM 0x10 + /* Subquery id: Query GFX RLC SRLS firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_SRM_MEM 0x11 + /* Subquery id: Query DMCU firmware version */ + #define AMDGPU_INFO_FW_DMCU 0x12 + #define AMDGPU_INFO_FW_TA 0x13 +/* number of bytes moved for TTM migration */ +#define AMDGPU_INFO_NUM_BYTES_MOVED 0x0f +/* the used VRAM size */ +#define AMDGPU_INFO_VRAM_USAGE 0x10 +/* the used GTT size */ +#define AMDGPU_INFO_GTT_USAGE 0x11 +/* Information about GDS, etc. resource configuration */ +#define AMDGPU_INFO_GDS_CONFIG 0x13 +/* Query information about VRAM and GTT domains */ +#define AMDGPU_INFO_VRAM_GTT 0x14 +/* Query information about register in MMR address space*/ +#define AMDGPU_INFO_READ_MMR_REG 0x15 +/* Query information about device: rev id, family, etc. */ +#define AMDGPU_INFO_DEV_INFO 0x16 +/* visible vram usage */ +#define AMDGPU_INFO_VIS_VRAM_USAGE 0x17 +/* number of TTM buffer evictions */ +#define AMDGPU_INFO_NUM_EVICTIONS 0x18 +/* Query memory about VRAM and GTT domains */ +#define AMDGPU_INFO_MEMORY 0x19 +/* Query vce clock table */ +#define AMDGPU_INFO_VCE_CLOCK_TABLE 0x1A +/* Query vbios related information */ +#define AMDGPU_INFO_VBIOS 0x1B + /* Subquery id: Query vbios size */ + #define AMDGPU_INFO_VBIOS_SIZE 0x1 + /* Subquery id: Query vbios image */ + #define AMDGPU_INFO_VBIOS_IMAGE 0x2 +/* Query UVD handles */ +#define AMDGPU_INFO_NUM_HANDLES 0x1C +/* Query sensor related information */ +#define AMDGPU_INFO_SENSOR 0x1D + /* Subquery id: Query GPU shader clock */ + #define AMDGPU_INFO_SENSOR_GFX_SCLK 0x1 + /* Subquery id: Query GPU memory clock */ + #define AMDGPU_INFO_SENSOR_GFX_MCLK 0x2 + /* Subquery id: Query GPU temperature */ + #define AMDGPU_INFO_SENSOR_GPU_TEMP 0x3 + /* Subquery id: Query GPU load */ + #define AMDGPU_INFO_SENSOR_GPU_LOAD 0x4 + /* Subquery id: Query average GPU power */ + #define AMDGPU_INFO_SENSOR_GPU_AVG_POWER 0x5 + /* Subquery id: Query northbridge voltage */ + #define AMDGPU_INFO_SENSOR_VDDNB 0x6 + /* Subquery id: Query graphics voltage */ + #define AMDGPU_INFO_SENSOR_VDDGFX 0x7 + /* Subquery id: Query GPU stable pstate shader clock */ + #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_SCLK 0x8 + /* Subquery id: Query GPU stable pstate memory clock */ + #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_MCLK 0x9 +/* Number of VRAM page faults on CPU access. */ +#define AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS 0x1E +#define AMDGPU_INFO_VRAM_LOST_COUNTER 0x1F +/* query ras mask of enabled features*/ +#define AMDGPU_INFO_RAS_ENABLED_FEATURES 0x20 + +/* RAS MASK: UMC (VRAM) */ +#define AMDGPU_INFO_RAS_ENABLED_UMC (1 << 0) +/* RAS MASK: SDMA */ +#define AMDGPU_INFO_RAS_ENABLED_SDMA (1 << 1) +/* RAS MASK: GFX */ +#define AMDGPU_INFO_RAS_ENABLED_GFX (1 << 2) +/* RAS MASK: MMHUB */ +#define AMDGPU_INFO_RAS_ENABLED_MMHUB (1 << 3) +/* RAS MASK: ATHUB */ +#define AMDGPU_INFO_RAS_ENABLED_ATHUB (1 << 4) +/* RAS MASK: PCIE */ +#define AMDGPU_INFO_RAS_ENABLED_PCIE (1 << 5) +/* RAS MASK: HDP */ +#define AMDGPU_INFO_RAS_ENABLED_HDP (1 << 6) +/* RAS MASK: XGMI */ +#define AMDGPU_INFO_RAS_ENABLED_XGMI (1 << 7) +/* RAS MASK: DF */ +#define AMDGPU_INFO_RAS_ENABLED_DF (1 << 8) +/* RAS MASK: SMN */ +#define AMDGPU_INFO_RAS_ENABLED_SMN (1 << 9) +/* RAS MASK: SEM */ +#define AMDGPU_INFO_RAS_ENABLED_SEM (1 << 10) +/* RAS MASK: MP0 */ +#define AMDGPU_INFO_RAS_ENABLED_MP0 (1 << 11) +/* RAS MASK: MP1 */ +#define AMDGPU_INFO_RAS_ENABLED_MP1 (1 << 12) +/* RAS MASK: FUSE */ +#define AMDGPU_INFO_RAS_ENABLED_FUSE (1 << 13) + +#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0 +#define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff +#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT 8 +#define AMDGPU_INFO_MMR_SH_INDEX_MASK 0xff + +struct drm_amdgpu_query_fw { + /** AMDGPU_INFO_FW_* */ + __u32 fw_type; + /** + * Index of the IP if there are more IPs of + * the same type. + */ + __u32 ip_instance; + /** + * Index of the engine. Whether this is used depends + * on the firmware type. (e.g. MEC, SDMA) + */ + __u32 index; + __u32 _pad; +}; + +/* Input structure for the INFO ioctl */ +struct drm_amdgpu_info { + /* Where the return value will be stored */ + __u64 return_pointer; + /* The size of the return value. Just like "size" in "snprintf", + * it limits how many bytes the kernel can write. */ + __u32 return_size; + /* The query request id. */ + __u32 query; + + union { + struct { + __u32 id; + __u32 _pad; + } mode_crtc; + + struct { + /** AMDGPU_HW_IP_* */ + __u32 type; + /** + * Index of the IP if there are more IPs of the same + * type. Ignored by AMDGPU_INFO_HW_IP_COUNT. + */ + __u32 ip_instance; + } query_hw_ip; + + struct { + __u32 dword_offset; + /** number of registers to read */ + __u32 count; + __u32 instance; + /** For future use, no flags defined so far */ + __u32 flags; + } read_mmr_reg; + + struct drm_amdgpu_query_fw query_fw; + + struct { + __u32 type; + __u32 offset; + } vbios_info; + + struct { + __u32 type; + } sensor_info; + }; +}; + +struct drm_amdgpu_info_gds { + /** GDS GFX partition size */ + __u32 gds_gfx_partition_size; + /** GDS compute partition size */ + __u32 compute_partition_size; + /** total GDS memory size */ + __u32 gds_total_size; + /** GWS size per GFX partition */ + __u32 gws_per_gfx_partition; + /** GSW size per compute partition */ + __u32 gws_per_compute_partition; + /** OA size per GFX partition */ + __u32 oa_per_gfx_partition; + /** OA size per compute partition */ + __u32 oa_per_compute_partition; + __u32 _pad; +}; + +struct drm_amdgpu_info_vram_gtt { + __u64 vram_size; + __u64 vram_cpu_accessible_size; + __u64 gtt_size; +}; + +struct drm_amdgpu_heap_info { + /** max. physical memory */ + __u64 total_heap_size; + + /** Theoretical max. available memory in the given heap */ + __u64 usable_heap_size; + + /** + * Number of bytes allocated in the heap. This includes all processes + * and private allocations in the kernel. It changes when new buffers + * are allocated, freed, and moved. It cannot be larger than + * heap_size. + */ + __u64 heap_usage; + + /** + * Theoretical possible max. size of buffer which + * could be allocated in the given heap + */ + __u64 max_allocation; +}; + +struct drm_amdgpu_memory_info { + struct drm_amdgpu_heap_info vram; + struct drm_amdgpu_heap_info cpu_accessible_vram; + struct drm_amdgpu_heap_info gtt; +}; + +struct drm_amdgpu_info_firmware { + __u32 ver; + __u32 feature; +}; + +#define AMDGPU_VRAM_TYPE_UNKNOWN 0 +#define AMDGPU_VRAM_TYPE_GDDR1 1 +#define AMDGPU_VRAM_TYPE_DDR2 2 +#define AMDGPU_VRAM_TYPE_GDDR3 3 +#define AMDGPU_VRAM_TYPE_GDDR4 4 +#define AMDGPU_VRAM_TYPE_GDDR5 5 +#define AMDGPU_VRAM_TYPE_HBM 6 +#define AMDGPU_VRAM_TYPE_DDR3 7 +#define AMDGPU_VRAM_TYPE_DDR4 8 +#define AMDGPU_VRAM_TYPE_GDDR6 9 + +struct drm_amdgpu_info_device { + /** PCI Device ID */ + __u32 device_id; + /** Internal chip revision: A0, A1, etc.) */ + __u32 chip_rev; + __u32 external_rev; + /** Revision id in PCI Config space */ + __u32 pci_rev; + __u32 family; + __u32 num_shader_engines; + __u32 num_shader_arrays_per_engine; + /* in KHz */ + __u32 gpu_counter_freq; + __u64 max_engine_clock; + __u64 max_memory_clock; + /* cu information */ + __u32 cu_active_number; + /* NOTE: cu_ao_mask is INVALID, DON'T use it */ + __u32 cu_ao_mask; + __u32 cu_bitmap[4][4]; + /** Render backend pipe mask. One render backend is CB+DB. */ + __u32 enabled_rb_pipes_mask; + __u32 num_rb_pipes; + __u32 num_hw_gfx_contexts; + __u32 _pad; + __u64 ids_flags; + /** Starting virtual address for UMDs. */ + __u64 virtual_address_offset; + /** The maximum virtual address */ + __u64 virtual_address_max; + /** Required alignment of virtual addresses. */ + __u32 virtual_address_alignment; + /** Page table entry - fragment size */ + __u32 pte_fragment_size; + __u32 gart_page_size; + /** constant engine ram size*/ + __u32 ce_ram_size; + /** video memory type info*/ + __u32 vram_type; + /** video memory bit width*/ + __u32 vram_bit_width; + /* vce harvesting instance */ + __u32 vce_harvest_config; + /* gfx double offchip LDS buffers */ + __u32 gc_double_offchip_lds_buf; + /* NGG Primitive Buffer */ + __u64 prim_buf_gpu_addr; + /* NGG Position Buffer */ + __u64 pos_buf_gpu_addr; + /* NGG Control Sideband */ + __u64 cntl_sb_buf_gpu_addr; + /* NGG Parameter Cache */ + __u64 param_buf_gpu_addr; + __u32 prim_buf_size; + __u32 pos_buf_size; + __u32 cntl_sb_buf_size; + __u32 param_buf_size; + /* wavefront size*/ + __u32 wave_front_size; + /* shader visible vgprs*/ + __u32 num_shader_visible_vgprs; + /* CU per shader array*/ + __u32 num_cu_per_sh; + /* number of tcc blocks*/ + __u32 num_tcc_blocks; + /* gs vgt table depth*/ + __u32 gs_vgt_table_depth; + /* gs primitive buffer depth*/ + __u32 gs_prim_buffer_depth; + /* max gs wavefront per vgt*/ + __u32 max_gs_waves_per_vgt; + __u32 _pad1; + /* always on cu bitmap */ + __u32 cu_ao_bitmap[4][4]; + /** Starting high virtual address for UMDs. */ + __u64 high_va_offset; + /** The maximum high virtual address */ + __u64 high_va_max; + /* gfx10 pa_sc_tile_steering_override */ + __u32 pa_sc_tile_steering_override; + /* disabled TCCs */ + __u64 tcc_disabled_mask; +}; + +struct drm_amdgpu_info_hw_ip { + /** Version of h/w IP */ + __u32 hw_ip_version_major; + __u32 hw_ip_version_minor; + /** Capabilities */ + __u64 capabilities_flags; + /** command buffer address start alignment*/ + __u32 ib_start_alignment; + /** command buffer size alignment*/ + __u32 ib_size_alignment; + /** Bitmask of available rings. Bit 0 means ring 0, etc. */ + __u32 available_rings; + __u32 _pad; +}; + +struct drm_amdgpu_info_num_handles { + /** Max handles as supported by firmware for UVD */ + __u32 uvd_max_handles; + /** Handles currently in use for UVD */ + __u32 uvd_used_handles; +}; + +#define AMDGPU_VCE_CLOCK_TABLE_ENTRIES 6 + +struct drm_amdgpu_info_vce_clock_table_entry { + /** System clock */ + __u32 sclk; + /** Memory clock */ + __u32 mclk; + /** VCE clock */ + __u32 eclk; + __u32 pad; +}; + +struct drm_amdgpu_info_vce_clock_table { + struct drm_amdgpu_info_vce_clock_table_entry entries[AMDGPU_VCE_CLOCK_TABLE_ENTRIES]; + __u32 num_valid_entries; + __u32 pad; +}; + +/* + * Supported GPU families + */ +#define AMDGPU_FAMILY_UNKNOWN 0 +#define AMDGPU_FAMILY_SI 110 /* Hainan, Oland, Verde, Pitcairn, Tahiti */ +#define AMDGPU_FAMILY_CI 120 /* Bonaire, Hawaii */ +#define AMDGPU_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */ +#define AMDGPU_FAMILY_VI 130 /* Iceland, Tonga */ +#define AMDGPU_FAMILY_CZ 135 /* Carrizo, Stoney */ +#define AMDGPU_FAMILY_AI 141 /* Vega10 */ +#define AMDGPU_FAMILY_RV 142 /* Raven */ +#define AMDGPU_FAMILY_NV 143 /* Navi10 */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/drm.h b/libs/common/drm/include/libdrm/drm.h new file mode 100644 index 0000000..438abde --- /dev/null +++ b/libs/common/drm/include/libdrm/drm.h @@ -0,0 +1,1042 @@ +/** + * \file drm.h + * Header for the Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith + * + * \par Acknowledgments: + * Dec 1999, Richard Henderson , move to generic \c cmpxchg. + */ + +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#if defined(__linux__) + +#include +#include +typedef unsigned int drm_handle_t; + +#else /* One of the BSDs */ + +#include +#include +#include +typedef int8_t __s8; +typedef uint8_t __u8; +typedef int16_t __s16; +typedef uint16_t __u16; +typedef int32_t __s32; +typedef uint32_t __u32; +typedef int64_t __s64; +typedef uint64_t __u64; +typedef size_t __kernel_size_t; +typedef unsigned long drm_handle_t; + +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +typedef unsigned int drm_context_t; +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; + +/** + * Cliprect. + * + * \warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well + * + * \note KW: Actually it's illegal to change either for + * backwards-compatibility reasons. + */ +struct drm_clip_rect { + unsigned short x1; + unsigned short y1; + unsigned short x2; + unsigned short y2; +}; + +/** + * Drawable information. + */ +struct drm_drawable_info { + unsigned int num_rects; + struct drm_clip_rect *rects; +}; + +/** + * Texture region, + */ +struct drm_tex_region { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; + unsigned int age; +}; + +/** + * Hardware lock. + * + * The lock structure is a simple cache-line aligned integer. To avoid + * processor bus contention on a multiprocessor system, there should not be any + * other data stored in the same cache line. + */ +struct drm_hw_lock { + __volatile__ unsigned int lock; /**< lock variable */ + char padding[60]; /**< Pad to cache line */ +}; + +/** + * DRM_IOCTL_VERSION ioctl argument type. + * + * \sa drmGetVersion(). + */ +struct drm_version { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + __kernel_size_t name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + __kernel_size_t date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + __kernel_size_t desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ +}; + +/** + * DRM_IOCTL_GET_UNIQUE ioctl argument type. + * + * \sa drmGetBusid() and drmSetBusId(). + */ +struct drm_unique { + __kernel_size_t unique_len; /**< Length of unique */ + char *unique; /**< Unique name for driver instantiation */ +}; + +struct drm_list { + int count; /**< Length of user-space structures */ + struct drm_version *version; +}; + +struct drm_block { + int unused; +}; + +/** + * DRM_IOCTL_CONTROL ioctl argument type. + * + * \sa drmCtlInstHandler() and drmCtlUninstHandler(). + */ +struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +}; + +/** + * Type of memory to map. + */ +enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /**< no caching, no core dump */ + _DRM_SHM = 2, /**< shared, cached */ + _DRM_AGP = 3, /**< AGP/GART */ + _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ + _DRM_CONSISTENT = 5 /**< Consistent memory for PCI DMA */ +}; + +/** + * Memory mapping flags. + */ +enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /**< shared, cached, locked */ + _DRM_KERNEL = 0x08, /**< kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ + _DRM_REMOVABLE = 0x40, /**< Removable mapping */ + _DRM_DRIVER = 0x80 /**< Managed by driver */ +}; + +struct drm_ctx_priv_map { + unsigned int ctx_id; /**< Context requesting private mapping */ + void *handle; /**< Handle of map */ +}; + +/** + * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls + * argument type. + * + * \sa drmAddMap(). + */ +struct drm_map { + unsigned long offset; /**< Requested physical address (0 for SAREA)*/ + unsigned long size; /**< Requested physical size (bytes) */ + enum drm_map_type type; /**< Type of memory to map */ + enum drm_map_flags flags; /**< Flags */ + void *handle; /**< User-space: "Handle" to pass to mmap() */ + /**< Kernel-space: kernel-virtual address */ + int mtrr; /**< MTRR slot used */ + /* Private data */ +}; + +/** + * DRM_IOCTL_GET_CLIENT ioctl argument type. + */ +struct drm_client { + int idx; /**< Which client desired? */ + int auth; /**< Is client authenticated? */ + unsigned long pid; /**< Process ID */ + unsigned long uid; /**< User ID */ + unsigned long magic; /**< Magic */ + unsigned long iocs; /**< Ioctl count */ +}; + +enum drm_stat_type { + _DRM_STAT_LOCK, + _DRM_STAT_OPENS, + _DRM_STAT_CLOSES, + _DRM_STAT_IOCTLS, + _DRM_STAT_LOCKS, + _DRM_STAT_UNLOCKS, + _DRM_STAT_VALUE, /**< Generic value */ + _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ + _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ + + _DRM_STAT_IRQ, /**< IRQ */ + _DRM_STAT_PRIMARY, /**< Primary DMA bytes */ + _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ + _DRM_STAT_DMA, /**< DMA */ + _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ + _DRM_STAT_MISSED /**< Missed DMA opportunity */ + /* Add to the *END* of the list */ +}; + +/** + * DRM_IOCTL_GET_STATS ioctl argument type. + */ +struct drm_stats { + unsigned long count; + struct { + unsigned long value; + enum drm_stat_type type; + } data[15]; +}; + +/** + * Hardware locking flags. + */ +enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +}; + +/** + * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. + * + * \sa drmGetLock() and drmUnlock(). + */ +struct drm_lock { + int context; + enum drm_lock_flags flags; +}; + +/** + * DMA flags + * + * \warning + * These values \e must match xf86drm.h. + * + * \sa drm_dma. + */ +enum drm_dma_flags { + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note The buffer may not yet have + * been processed by the hardware -- + * getting a hardware lock with the + * hardware quiescent will ensure + * that the buffer has been + * processed. + */ + _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ +}; + +/** + * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. + * + * \sa drmAddBufs(). + */ +struct drm_buf_desc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ + enum { + _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ + _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ + _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ + _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ + } flags; + unsigned long agp_start; /**< + * Start address of where the AGP buffers are + * in the AGP aperture + */ +}; + +/** + * DRM_IOCTL_INFO_BUFS ioctl argument type. + */ +struct drm_buf_info { + int count; /**< Entries in list */ + struct drm_buf_desc *list; +}; + +/** + * DRM_IOCTL_FREE_BUFS ioctl argument type. + */ +struct drm_buf_free { + int count; + int *list; +}; + +/** + * Buffer information + * + * \sa drm_buf_map. + */ +struct drm_buf_pub { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + void *address; /**< Address of buffer */ +}; + +/** + * DRM_IOCTL_MAP_BUFS ioctl argument type. + */ +struct drm_buf_map { + int count; /**< Length of the buffer list */ +#ifdef __cplusplus + void *virt; +#else + void *virtual; /**< Mmap'd area in user-virtual */ +#endif + struct drm_buf_pub *list; /**< Buffer information */ +}; + +/** + * DRM_IOCTL_DMA ioctl argument type. + * + * Indices here refer to the offset into the buffer list in drm_buf_get. + * + * \sa drmDMA(). + */ +struct drm_dma { + int context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_indices; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send */ + enum drm_dma_flags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size for buffers */ + int *request_indices; /**< Buffer information */ + int *request_sizes; + int granted_count; /**< Number of buffers granted */ +}; + +enum drm_ctx_flags { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +}; + +/** + * DRM_IOCTL_ADD_CTX ioctl argument type. + * + * \sa drmCreateContext() and drmDestroyContext(). + */ +struct drm_ctx { + drm_context_t handle; + enum drm_ctx_flags flags; +}; + +/** + * DRM_IOCTL_RES_CTX ioctl argument type. + */ +struct drm_ctx_res { + int count; + struct drm_ctx *contexts; +}; + +/** + * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. + */ +struct drm_draw { + drm_drawable_t handle; +}; + +/** + * DRM_IOCTL_UPDATE_DRAW ioctl argument type. + */ +typedef enum { + DRM_DRAWABLE_CLIPRECTS +} drm_drawable_info_type_t; + +struct drm_update_draw { + drm_drawable_t handle; + unsigned int type; + unsigned int num; + unsigned long long data; +}; + +/** + * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. + */ +struct drm_auth { + drm_magic_t magic; +}; + +/** + * DRM_IOCTL_IRQ_BUSID ioctl argument type. + * + * \sa drmGetInterruptFromBusID(). + */ +struct drm_irq_busid { + int irq; /**< IRQ number */ + int busnum; /**< bus number */ + int devnum; /**< device number */ + int funcnum; /**< function number */ +}; + +enum drm_vblank_seq_type { + _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + /* bits 1-6 are reserved for high crtcs */ + _DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, + _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking, unsupported */ +}; +#define _DRM_VBLANK_HIGH_CRTC_SHIFT 1 + +#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \ + _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS) + +struct drm_wait_vblank_request { + enum drm_vblank_seq_type type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { + enum drm_vblank_seq_type type; + unsigned int sequence; + long tval_sec; + long tval_usec; +}; + +/** + * DRM_IOCTL_WAIT_VBLANK ioctl argument type. + * + * \sa drmWaitVBlank(). + */ +union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +}; + +#define _DRM_PRE_MODESET 1 +#define _DRM_POST_MODESET 2 + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +struct drm_modeset_ctl { + __u32 crtc; + __u32 cmd; +}; + +/** + * DRM_IOCTL_AGP_ENABLE ioctl argument type. + * + * \sa drmAgpEnable(). + */ +struct drm_agp_mode { + unsigned long mode; /**< AGP mode */ +}; + +/** + * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. + * + * \sa drmAgpAlloc() and drmAgpFree(). + */ +struct drm_agp_buffer { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for binding / unbinding */ + unsigned long type; /**< Type of memory to allocate */ + unsigned long physical; /**< Physical used by i810 */ +}; + +/** + * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. + * + * \sa drmAgpBind() and drmAgpUnbind(). + */ +struct drm_agp_binding { + unsigned long handle; /**< From drm_agp_buffer */ + unsigned long offset; /**< In bytes -- will round to page boundary */ +}; + +/** + * DRM_IOCTL_AGP_INFO ioctl argument type. + * + * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), + * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), + * drmAgpVendorId() and drmAgpDeviceId(). + */ +struct drm_agp_info { + int agp_version_major; + int agp_version_minor; + unsigned long mode; + unsigned long aperture_base; /* physical address */ + unsigned long aperture_size; /* bytes */ + unsigned long memory_allowed; /* bytes */ + unsigned long memory_used; + + /* PCI information */ + unsigned short id_vendor; + unsigned short id_device; +}; + +/** + * DRM_IOCTL_SG_ALLOC ioctl argument type. + */ +struct drm_scatter_gather { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for mapping / unmapping */ +}; + +/** + * DRM_IOCTL_SET_VERSION ioctl argument type. + */ +struct drm_set_version { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +}; + +/** DRM_IOCTL_GEM_CLOSE ioctl argument type */ +struct drm_gem_close { + /** Handle of the object to be closed. */ + __u32 handle; + __u32 pad; +}; + +/** DRM_IOCTL_GEM_FLINK ioctl argument type */ +struct drm_gem_flink { + /** Handle for the object being named */ + __u32 handle; + + /** Returned global name */ + __u32 name; +}; + +/** DRM_IOCTL_GEM_OPEN ioctl argument type */ +struct drm_gem_open { + /** Name of object being opened */ + __u32 name; + + /** Returned handle for the object */ + __u32 handle; + + /** Returned size of the object */ + __u64 size; +}; + +#define DRM_CAP_DUMB_BUFFER 0x1 +#define DRM_CAP_VBLANK_HIGH_CRTC 0x2 +#define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 +#define DRM_CAP_DUMB_PREFER_SHADOW 0x4 +#define DRM_CAP_PRIME 0x5 +#define DRM_PRIME_CAP_IMPORT 0x1 +#define DRM_PRIME_CAP_EXPORT 0x2 +#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 +#define DRM_CAP_ASYNC_PAGE_FLIP 0x7 +/* + * The CURSOR_WIDTH and CURSOR_HEIGHT capabilities return a valid widthxheight + * combination for the hardware cursor. The intention is that a hardware + * agnostic userspace can query a cursor plane size to use. + * + * Note that the cross-driver contract is to merely return a valid size; + * drivers are free to attach another meaning on top, eg. i915 returns the + * maximum plane size. + */ +#define DRM_CAP_CURSOR_WIDTH 0x8 +#define DRM_CAP_CURSOR_HEIGHT 0x9 +#define DRM_CAP_ADDFB2_MODIFIERS 0x10 +#define DRM_CAP_PAGE_FLIP_TARGET 0x11 +#define DRM_CAP_CRTC_IN_VBLANK_EVENT 0x12 +#define DRM_CAP_SYNCOBJ 0x13 +#define DRM_CAP_SYNCOBJ_TIMELINE 0x14 + +/** DRM_IOCTL_GET_CAP ioctl argument type */ +struct drm_get_cap { + __u64 capability; + __u64 value; +}; + +/** + * DRM_CLIENT_CAP_STEREO_3D + * + * if set to 1, the DRM core will expose the stereo 3D capabilities of the + * monitor by advertising the supported 3D layouts in the flags of struct + * drm_mode_modeinfo. + */ +#define DRM_CLIENT_CAP_STEREO_3D 1 + +/** + * DRM_CLIENT_CAP_UNIVERSAL_PLANES + * + * If set to 1, the DRM core will expose all planes (overlay, primary, and + * cursor) to userspace. + */ +#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 + +/** + * DRM_CLIENT_CAP_ATOMIC + * + * If set to 1, the DRM core will expose atomic properties to userspace + */ +#define DRM_CLIENT_CAP_ATOMIC 3 + +/** + * DRM_CLIENT_CAP_ASPECT_RATIO + * + * If set to 1, the DRM core will provide aspect ratio information in modes. + */ +#define DRM_CLIENT_CAP_ASPECT_RATIO 4 + +/** + * DRM_CLIENT_CAP_WRITEBACK_CONNECTORS + * + * If set to 1, the DRM core will expose special connectors to be used for + * writing back to memory the scene setup in the commit. Depends on client + * also supporting DRM_CLIENT_CAP_ATOMIC + */ +#define DRM_CLIENT_CAP_WRITEBACK_CONNECTORS 5 + +/** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ +struct drm_set_client_cap { + __u64 capability; + __u64 value; +}; + +#define DRM_RDWR O_RDWR +#define DRM_CLOEXEC O_CLOEXEC +struct drm_prime_handle { + __u32 handle; + + /** Flags.. only applicable for handle->fd */ + __u32 flags; + + /** Returned dmabuf file descriptor */ + __s32 fd; +}; + +struct drm_syncobj_create { + __u32 handle; +#define DRM_SYNCOBJ_CREATE_SIGNALED (1 << 0) + __u32 flags; +}; + +struct drm_syncobj_destroy { + __u32 handle; + __u32 pad; +}; + +#define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE (1 << 0) +struct drm_syncobj_handle { + __u32 handle; + __u32 flags; + + __s32 fd; + __u32 pad; +}; + +struct drm_syncobj_transfer { + __u32 src_handle; + __u32 dst_handle; + __u64 src_point; + __u64 dst_point; + __u32 flags; + __u32 pad; +}; + +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0) +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1) +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2) /* wait for time point to become available */ +struct drm_syncobj_wait { + __u64 handles; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + +struct drm_syncobj_timeline_wait { + __u64 handles; + /* wait on specific timeline point for every handles*/ + __u64 points; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + + +struct drm_syncobj_array { + __u64 handles; + __u32 count_handles; + __u32 pad; +}; + +struct drm_syncobj_timeline_array { + __u64 handles; + __u64 points; + __u32 count_handles; + __u32 pad; +}; + + +/* Query current scanout sequence number */ +struct drm_crtc_get_sequence { + __u32 crtc_id; /* requested crtc_id */ + __u32 active; /* return: crtc output is active */ + __u64 sequence; /* return: most recent vblank sequence */ + __s64 sequence_ns; /* return: most recent time of first pixel out */ +}; + +/* Queue event to be delivered at specified sequence. Time stamp marks + * when the first pixel of the refresh cycle leaves the display engine + * for the display + */ +#define DRM_CRTC_SEQUENCE_RELATIVE 0x00000001 /* sequence is relative to current */ +#define DRM_CRTC_SEQUENCE_NEXT_ON_MISS 0x00000002 /* Use next sequence if we've missed */ + +struct drm_crtc_queue_sequence { + __u32 crtc_id; + __u32 flags; + __u64 sequence; /* on input, target sequence. on output, actual sequence */ + __u64 user_data; /* user data passed to event */ +}; + +#if defined(__cplusplus) +} +#endif + +#include "drm_mode.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_IOCTL_BASE 'd' +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) +#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) +#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, struct drm_unique) +#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, struct drm_auth) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, struct drm_irq_busid) +#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, struct drm_map) +#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) +#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) +#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) +#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) +#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) +#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) +#define DRM_IOCTL_GET_CAP DRM_IOWR(0x0c, struct drm_get_cap) +#define DRM_IOCTL_SET_CLIENT_CAP DRM_IOW( 0x0d, struct drm_set_client_cap) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, struct drm_block) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, struct drm_block) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, struct drm_control) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, struct drm_map) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, struct drm_buf_desc) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, struct drm_buf_desc) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, struct drm_buf_info) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, struct drm_buf_map) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, struct drm_buf_free) + +#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, struct drm_map) + +#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) +#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) + +#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) +#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, struct drm_ctx) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, struct drm_ctx) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, struct drm_ctx) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, struct drm_ctx_res) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, struct drm_draw) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, struct drm_draw) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, struct drm_dma) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, struct drm_lock) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) + +#define DRM_IOCTL_PRIME_HANDLE_TO_FD DRM_IOWR(0x2d, struct drm_prime_handle) +#define DRM_IOCTL_PRIME_FD_TO_HANDLE DRM_IOWR(0x2e, struct drm_prime_handle) + +#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) +#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) +#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, struct drm_agp_mode) +#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, struct drm_agp_info) +#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) +#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) + +#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) + +#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) + +#define DRM_IOCTL_CRTC_GET_SEQUENCE DRM_IOWR(0x3b, struct drm_crtc_get_sequence) +#define DRM_IOCTL_CRTC_QUEUE_SEQUENCE DRM_IOWR(0x3c, struct drm_crtc_queue_sequence) + +#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) + +#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) +#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) +#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) +#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) +#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) /* deprecated (never worked) */ +#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) /* deprecated (never worked) */ + +#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) +#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) +#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) +#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) +#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip) +#define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd) + +#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb) +#define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb) +#define DRM_IOCTL_MODE_DESTROY_DUMB DRM_IOWR(0xB4, struct drm_mode_destroy_dumb) +#define DRM_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xB5, struct drm_mode_get_plane_res) +#define DRM_IOCTL_MODE_GETPLANE DRM_IOWR(0xB6, struct drm_mode_get_plane) +#define DRM_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct drm_mode_set_plane) +#define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2) +#define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) +#define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) +#define DRM_IOCTL_MODE_CURSOR2 DRM_IOWR(0xBB, struct drm_mode_cursor2) +#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic) +#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob) +#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob) + +#define DRM_IOCTL_SYNCOBJ_CREATE DRM_IOWR(0xBF, struct drm_syncobj_create) +#define DRM_IOCTL_SYNCOBJ_DESTROY DRM_IOWR(0xC0, struct drm_syncobj_destroy) +#define DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD DRM_IOWR(0xC1, struct drm_syncobj_handle) +#define DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE DRM_IOWR(0xC2, struct drm_syncobj_handle) +#define DRM_IOCTL_SYNCOBJ_WAIT DRM_IOWR(0xC3, struct drm_syncobj_wait) +#define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array) +#define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array) + +#define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease) +#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees) +#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease) +#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease) + +#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait) +#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array) +#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) +#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) + +/** + * Device specific ioctls should only be in their respective headers + * The device specific ioctl range is from 0x40 to 0x9f. + * Generic IOCTLS restart at 0xA0. + * + * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and + * drmCommandReadWrite(). + */ +#define DRM_COMMAND_BASE 0x40 +#define DRM_COMMAND_END 0xA0 + +/** + * Header for events written back to userspace on the drm fd. The + * type defines the type of event, the length specifies the total + * length of the event (including the header), and user_data is + * typically a 64 bit value passed with the ioctl that triggered the + * event. A read on the drm fd will always only return complete + * events, that is, if for example the read buffer is 100 bytes, and + * there are two 64 byte events pending, only one will be returned. + * + * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and + * up are chipset specific. + */ +struct drm_event { + __u32 type; + __u32 length; +}; + +#define DRM_EVENT_VBLANK 0x01 +#define DRM_EVENT_FLIP_COMPLETE 0x02 +#define DRM_EVENT_CRTC_SEQUENCE 0x03 + +struct drm_event_vblank { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 crtc_id; /* 0 on older kernels that do not support this */ +}; + +/* Event delivered at sequence. Time stamp marks when the first pixel + * of the refresh cycle leaves the display engine for the display + */ +struct drm_event_crtc_sequence { + struct drm_event base; + __u64 user_data; + __s64 time_ns; + __u64 sequence; +}; + +/* typedef area */ +typedef struct drm_clip_rect drm_clip_rect_t; +typedef struct drm_drawable_info drm_drawable_info_t; +typedef struct drm_tex_region drm_tex_region_t; +typedef struct drm_hw_lock drm_hw_lock_t; +typedef struct drm_version drm_version_t; +typedef struct drm_unique drm_unique_t; +typedef struct drm_list drm_list_t; +typedef struct drm_block drm_block_t; +typedef struct drm_control drm_control_t; +typedef enum drm_map_type drm_map_type_t; +typedef enum drm_map_flags drm_map_flags_t; +typedef struct drm_ctx_priv_map drm_ctx_priv_map_t; +typedef struct drm_map drm_map_t; +typedef struct drm_client drm_client_t; +typedef enum drm_stat_type drm_stat_type_t; +typedef struct drm_stats drm_stats_t; +typedef enum drm_lock_flags drm_lock_flags_t; +typedef struct drm_lock drm_lock_t; +typedef enum drm_dma_flags drm_dma_flags_t; +typedef struct drm_buf_desc drm_buf_desc_t; +typedef struct drm_buf_info drm_buf_info_t; +typedef struct drm_buf_free drm_buf_free_t; +typedef struct drm_buf_pub drm_buf_pub_t; +typedef struct drm_buf_map drm_buf_map_t; +typedef struct drm_dma drm_dma_t; +typedef union drm_wait_vblank drm_wait_vblank_t; +typedef struct drm_agp_mode drm_agp_mode_t; +typedef enum drm_ctx_flags drm_ctx_flags_t; +typedef struct drm_ctx drm_ctx_t; +typedef struct drm_ctx_res drm_ctx_res_t; +typedef struct drm_draw drm_draw_t; +typedef struct drm_update_draw drm_update_draw_t; +typedef struct drm_auth drm_auth_t; +typedef struct drm_irq_busid drm_irq_busid_t; +typedef enum drm_vblank_seq_type drm_vblank_seq_type_t; + +typedef struct drm_agp_buffer drm_agp_buffer_t; +typedef struct drm_agp_binding drm_agp_binding_t; +typedef struct drm_agp_info drm_agp_info_t; +typedef struct drm_scatter_gather drm_scatter_gather_t; +typedef struct drm_set_version drm_set_version_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/drm_fourcc.h b/libs/common/drm/include/libdrm/drm_fourcc.h new file mode 100644 index 0000000..5c69090 --- /dev/null +++ b/libs/common/drm/include/libdrm/drm_fourcc.h @@ -0,0 +1,763 @@ +/* + * Copyright 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_FOURCC_H +#define DRM_FOURCC_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * DOC: overview + * + * In the DRM subsystem, framebuffer pixel formats are described using the + * fourcc codes defined in `include/uapi/drm/drm_fourcc.h`. In addition to the + * fourcc code, a Format Modifier may optionally be provided, in order to + * further describe the buffer's format - for example tiling or compression. + * + * Format Modifiers + * ---------------- + * + * Format modifiers are used in conjunction with a fourcc code, forming a + * unique fourcc:modifier pair. This format:modifier pair must fully define the + * format and data layout of the buffer, and should be the only way to describe + * that particular buffer. + * + * Having multiple fourcc:modifier pairs which describe the same layout should + * be avoided, as such aliases run the risk of different drivers exposing + * different names for the same data format, forcing userspace to understand + * that they are aliases. + * + * Format modifiers may change any property of the buffer, including the number + * of planes and/or the required allocation size. Format modifiers are + * vendor-namespaced, and as such the relationship between a fourcc code and a + * modifier is specific to the modifer being used. For example, some modifiers + * may preserve meaning - such as number of planes - from the fourcc code, + * whereas others may not. + * + * Vendors should document their modifier usage in as much detail as + * possible, to ensure maximum compatibility across devices, drivers and + * applications. + * + * The authoritative list of format modifier codes is found in + * `include/uapi/drm/drm_fourcc.h` + */ + +#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ + ((__u32)(c) << 16) | ((__u32)(d) << 24)) + +#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ + +/* Reserve 0 for the invalid format specifier */ +#define DRM_FORMAT_INVALID 0 + +/* color index */ +#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ + +/* 8 bpp Red */ +#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ + +/* 16 bpp Red */ +#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ + +/* 16 bpp RG */ +#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ +#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ + +/* 32 bpp RG */ +#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ +#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ + +/* 8 bpp RGB */ +#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ +#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ + +/* 16 bpp RGB */ +#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ + +#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ + +#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ + +#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ + +#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ +#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ + +/* 24 bpp RGB */ +#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ +#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ + +/* 32 bpp RGB */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ + +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ + +#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ + +#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ + +/* + * Floating point 64bpp RGB + * IEEE 754-2008 binary16 half-precision float + * [15:0] sign:exponent:mantissa 1:5:10 + */ +#define DRM_FORMAT_XRGB16161616F fourcc_code('X', 'R', '4', 'H') /* [63:0] x:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_XBGR16161616F fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */ + +#define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ + +/* packed YCbCr */ +#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ +#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ + +#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_VUY888 fourcc_code('V', 'U', '2', '4') /* [23:0] Cr:Cb:Y 8:8:8 little endian */ +#define DRM_FORMAT_VUY101010 fourcc_code('V', 'U', '3', '0') /* Y followed by U then V, 10:10:10. Non-linear modifier only */ + +/* + * packed Y2xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb + */ +#define DRM_FORMAT_Y210 fourcc_code('Y', '2', '1', '0') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels */ +#define DRM_FORMAT_Y212 fourcc_code('Y', '2', '1', '2') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels */ +#define DRM_FORMAT_Y216 fourcc_code('Y', '2', '1', '6') /* [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels */ + +/* + * packed Y4xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb except Y410 + */ +#define DRM_FORMAT_Y410 fourcc_code('Y', '4', '1', '0') /* [31:0] A:Cr:Y:Cb 2:10:10:10 little endian */ +#define DRM_FORMAT_Y412 fourcc_code('Y', '4', '1', '2') /* [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ +#define DRM_FORMAT_Y416 fourcc_code('Y', '4', '1', '6') /* [63:0] A:Cr:Y:Cb 16:16:16:16 little endian */ + +#define DRM_FORMAT_XVYU2101010 fourcc_code('X', 'V', '3', '0') /* [31:0] X:Cr:Y:Cb 2:10:10:10 little endian */ +#define DRM_FORMAT_XVYU12_16161616 fourcc_code('X', 'V', '3', '6') /* [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ +#define DRM_FORMAT_XVYU16161616 fourcc_code('X', 'V', '4', '8') /* [63:0] X:Cr:Y:Cb 16:16:16:16 little endian */ + +/* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ +/* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_Y0L0 fourcc_code('Y', '0', 'L', '0') +/* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_X0L0 fourcc_code('X', '0', 'L', '0') + +/* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_Y0L2 fourcc_code('Y', '0', 'L', '2') +/* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_X0L2 fourcc_code('X', '0', 'L', '2') + +/* + * 1-plane YUV 4:2:0 + * In these formats, the component ordering is specified (Y, followed by U + * then V), but the exact Linear layout is undefined. + * These formats can only be used with a non-Linear modifier. + */ +#define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8') +#define DRM_FORMAT_YUV420_10BIT fourcc_code('Y', 'U', '1', '0') + +/* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ +#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') +#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') +#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') +#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') +#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') +#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') +#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') +#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') + +/* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ +#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ +#define DRM_FORMAT_P210 fourcc_code('P', '2', '1', '0') /* 2x1 subsampled Cr:Cb plane, 10 bit per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [12:4] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [12:4:12:4] little endian + */ +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane 12 bits per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr:Cb plane, [31:0] Cr:Cb [16:16] little endian + */ +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */ + +/* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ +#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ + + +/* + * Format Modifiers: + * + * Format modifiers describe, typically, a re-ordering or modification + * of the data in a plane of an FB. This can be used to express tiled/ + * swizzled formats, or compression, or a combination of the two. + * + * The upper 8 bits of the format modifier are a vendor-id as assigned + * below. The lower 56 bits are assigned as vendor sees fit. + */ + +/* Vendor Ids: */ +#define DRM_FORMAT_MOD_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 +#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 +#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03 +#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 +#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 +#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 +#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 +#define DRM_FORMAT_MOD_VENDOR_ARM 0x08 +#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 + +/* add more to the end as needed */ + +#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) + +#define fourcc_mod_code(vendor, val) \ + ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) + +/* + * Format Modifier tokens: + * + * When adding a new token please document the layout with a code comment, + * similar to the fourcc codes above. drm_fourcc.h is considered the + * authoritative source for all of these. + */ + +/* + * Invalid Modifier + * + * This modifier can be used as a sentinel to terminate the format modifiers + * list, or to initialize a variable with an invalid modifier. It might also be + * used to report an error back to userspace for certain APIs. + */ +#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) + +/* + * Linear Layout + * + * Just plain linear layout. Note that this is different from no specifying any + * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), + * which tells the driver to also take driver-internal information into account + * and so might actually result in a tiled framebuffer. + */ +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) + +/* Intel framebuffer modifiers */ + +/* + * Intel X-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out row-major, with + * a platform-dependent stride. On top of that the memory can apply + * platform-depending swizzling of some higher address bits into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) + +/* + * Intel Y-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) + * chunks column-major, with a platform-dependent height. On top of that the + * memory can apply platform-depending swizzling of some higher address bits + * into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) + +/* + * Intel Yf-tiling layout + * + * This is a tiled layout using 4Kb tiles in row-major layout. + * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which + * are arranged in four groups (two wide, two high) with column-major layout. + * Each group therefore consists out of four 256 byte units, which are also laid + * out as 2x2 column-major. + * 256 byte units are made out of four 64 byte blocks of pixels, producing + * either a square block or a 2:1 unit. + * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width + * in pixel depends on the pixel depth. + */ +#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) + +/* + * Intel color control surface (CCS) for render compression + * + * The framebuffer format must be one of the 8:8:8:8 RGB formats. + * The main surface will be plane index 0 and must be Y/Yf-tiled, + * the CCS will be plane index 1. + * + * Each CCS tile matches a 1024x512 pixel area of the main surface. + * To match certain aspects of the 3D hardware the CCS is + * considered to be made up of normal 128Bx32 Y tiles, Thus + * the CCS pitch must be specified in multiples of 128 bytes. + * + * In reality the CCS tile appears to be a 64Bx64 Y tile, composed + * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. + * But that fact is not relevant unless the memory is accessed + * directly. + */ +#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) +#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) + +/* + * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks + * + * Macroblocks are laid in a Z-shape, and each pixel data is following the + * standard NV12 style. + * As for NV12, an image is the result of two frame buffers: one for Y, + * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). + * Alignment requirements are (for each buffer): + * - multiple of 128 pixels for the width + * - multiple of 32 pixels for the height + * + * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html + */ +#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) + +/* + * Tiled, 16 (pixels) x 16 (lines) - sized macroblocks + * + * This is a simple tiled layout using tiles of 16x16 pixels in a row-major + * layout. For YCbCr formats Cb/Cr components are taken in such a way that + * they correspond to their 16x16 luma block. + */ +#define DRM_FORMAT_MOD_SAMSUNG_16_16_TILE fourcc_mod_code(SAMSUNG, 2) + +/* + * Qualcomm Compressed Format + * + * Refers to a compressed variant of the base format that is compressed. + * Implementation may be platform and base-format specific. + * + * Each macrotile consists of m x n (mostly 4 x 4) tiles. + * Pixel data pitch/stride is aligned with macrotile width. + * Pixel data height is aligned with macrotile height. + * Entire pixel data buffer is aligned with 4k(bytes). + */ +#define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1) + +/* Vivante framebuffer modifiers */ + +/* + * Vivante 4x4 tiling layout + * + * This is a simple tiled layout using tiles of 4x4 pixels in a row-major + * layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) + +/* + * Vivante 64x64 super-tiling layout + * + * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile + * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- + * major layout. + * + * For more information: see + * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling + */ +#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) + +/* + * Vivante 4x4 tiling layout for dual-pipe + * + * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a + * different base address. Offsets from the base addresses are therefore halved + * compared to the non-split tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) + +/* + * Vivante 64x64 super-tiling layout for dual-pipe + * + * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile + * starts at a different base address. Offsets from the base addresses are + * therefore halved compared to the non-split super-tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) + +/* NVIDIA frame buffer modifiers */ + +/* + * Tegra Tiled Layout, used by Tegra 2, 3 and 4. + * + * Pixels are arranged in simple tiles of 16 x 16 bytes. + */ +#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) + +/* + * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later + * + * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked + * vertically by a power of 2 (1 to 32 GOBs) to form a block. + * + * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. + * + * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. + * Valid values are: + * + * 0 == ONE_GOB + * 1 == TWO_GOBS + * 2 == FOUR_GOBS + * 3 == EIGHT_GOBS + * 4 == SIXTEEN_GOBS + * 5 == THIRTYTWO_GOBS + * + * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format + * in full detail. + */ +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ + fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf)) + +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ + fourcc_mod_code(NVIDIA, 0x10) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ + fourcc_mod_code(NVIDIA, 0x11) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ + fourcc_mod_code(NVIDIA, 0x12) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ + fourcc_mod_code(NVIDIA, 0x13) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ + fourcc_mod_code(NVIDIA, 0x14) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ + fourcc_mod_code(NVIDIA, 0x15) + +/* + * Some Broadcom modifiers take parameters, for example the number of + * vertical lines in the image. Reserve the lower 32 bits for modifier + * type, and the next 24 bits for parameters. Top 8 bits are the + * vendor code. + */ +#define __fourcc_mod_broadcom_param_shift 8 +#define __fourcc_mod_broadcom_param_bits 48 +#define fourcc_mod_broadcom_code(val, params) \ + fourcc_mod_code(BROADCOM, ((((__u64)params) << __fourcc_mod_broadcom_param_shift) | val)) +#define fourcc_mod_broadcom_param(m) \ + ((int)(((m) >> __fourcc_mod_broadcom_param_shift) & \ + ((1ULL << __fourcc_mod_broadcom_param_bits) - 1))) +#define fourcc_mod_broadcom_mod(m) \ + ((m) & ~(((1ULL << __fourcc_mod_broadcom_param_bits) - 1) << \ + __fourcc_mod_broadcom_param_shift)) + +/* + * Broadcom VC4 "T" format + * + * This is the primary layout that the V3D GPU can texture from (it + * can't do linear). The T format has: + * + * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 + * pixels at 32 bit depth. + * + * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually + * 16x16 pixels). + * + * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On + * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows + * they're (TR, BR, BL, TL), where bottom left is start of memory. + * + * - an image made of 4k tiles in rows either left-to-right (even rows of 4k + * tiles) or right-to-left (odd rows of 4k tiles). + */ +#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) + +/* + * Broadcom SAND format + * + * This is the native format that the H.264 codec block uses. For VC4 + * HVS, it is only valid for H.264 (NV12/21) and RGBA modes. + * + * The image can be considered to be split into columns, and the + * columns are placed consecutively into memory. The width of those + * columns can be either 32, 64, 128, or 256 pixels, but in practice + * only 128 pixel columns are used. + * + * The pitch between the start of each column is set to optimally + * switch between SDRAM banks. This is passed as the number of lines + * of column width in the modifier (we can't use the stride value due + * to various core checks that look at it , so you should set the + * stride to width*cpp). + * + * Note that the column height for this format modifier is the same + * for all of the planes, assuming that each column contains both Y + * and UV. Some SAND-using hardware stores UV in a separate tiled + * image from Y to reduce the column height, which is not supported + * with these modifiers. + */ + +#define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(2, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(3, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(4, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(5, v) + +#define DRM_FORMAT_MOD_BROADCOM_SAND32 \ + DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND64 \ + DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND128 \ + DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND256 \ + DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(0) + +/* Broadcom UIF format + * + * This is the common format for the current Broadcom multimedia + * blocks, including V3D 3.x and newer, newer video codecs, and + * displays. + * + * The image consists of utiles (64b blocks), UIF blocks (2x2 utiles), + * and macroblocks (4x4 UIF blocks). Those 4x4 UIF block groups are + * stored in columns, with padding between the columns to ensure that + * moving from one column to the next doesn't hit the same SDRAM page + * bank. + * + * To calculate the padding, it is assumed that each hardware block + * and the software driving it knows the platform's SDRAM page size, + * number of banks, and XOR address, and that it's identical between + * all blocks using the format. This tiling modifier will use XOR as + * necessary to reduce the padding. If a hardware block can't do XOR, + * the assumption is that a no-XOR tiling modifier will be created. + */ +#define DRM_FORMAT_MOD_BROADCOM_UIF fourcc_mod_code(BROADCOM, 6) + +/* + * Arm Framebuffer Compression (AFBC) modifiers + * + * AFBC is a proprietary lossless image compression protocol and format. + * It provides fine-grained random access and minimizes the amount of data + * transferred between IP blocks. + * + * AFBC has several features which may be supported and/or used, which are + * represented using bits in the modifier. Not all combinations are valid, + * and different devices or use-cases may support different combinations. + * + * Further information on the use of AFBC modifiers can be found in + * Documentation/gpu/afbc.rst + */ +#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) + +/* + * AFBC superblock size + * + * Indicates the superblock size(s) used for the AFBC buffer. The buffer + * size (in pixels) must be aligned to a multiple of the superblock size. + * Four lowest significant bits(LSBs) are reserved for block size. + * + * Where one superblock size is specified, it applies to all planes of the + * buffer (e.g. 16x16, 32x8). When multiple superblock sizes are specified, + * the first applies to the Luma plane and the second applies to the Chroma + * plane(s). e.g. (32x8_64x4 means 32x8 Luma, with 64x4 Chroma). + * Multiple superblock sizes are only valid for multi-plane YCbCr formats. + */ +#define AFBC_FORMAT_MOD_BLOCK_SIZE_MASK 0xf +#define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 (2ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 (3ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4 (4ULL) + +/* + * AFBC lossless colorspace transform + * + * Indicates that the buffer makes use of the AFBC lossless colorspace + * transform. + */ +#define AFBC_FORMAT_MOD_YTR (1ULL << 4) + +/* + * AFBC block-split + * + * Indicates that the payload of each superblock is split. The second + * half of the payload is positioned at a predefined offset from the start + * of the superblock payload. + */ +#define AFBC_FORMAT_MOD_SPLIT (1ULL << 5) + +/* + * AFBC sparse layout + * + * This flag indicates that the payload of each superblock must be stored at a + * predefined position relative to the other superblocks in the same AFBC + * buffer. This order is the same order used by the header buffer. In this mode + * each superblock is given the same amount of space as an uncompressed + * superblock of the particular format would require, rounding up to the next + * multiple of 128 bytes in size. + */ +#define AFBC_FORMAT_MOD_SPARSE (1ULL << 6) + +/* + * AFBC copy-block restrict + * + * Buffers with this flag must obey the copy-block restriction. The restriction + * is such that there are no copy-blocks referring across the border of 8x8 + * blocks. For the subsampled data the 8x8 limitation is also subsampled. + */ +#define AFBC_FORMAT_MOD_CBR (1ULL << 7) + +/* + * AFBC tiled layout + * + * The tiled layout groups superblocks in 8x8 or 4x4 tiles, where all + * superblocks inside a tile are stored together in memory. 8x8 tiles are used + * for pixel formats up to and including 32 bpp while 4x4 tiles are used for + * larger bpp formats. The order between the tiles is scan line. + * When the tiled layout is used, the buffer size (in pixels) must be aligned + * to the tile size. + */ +#define AFBC_FORMAT_MOD_TILED (1ULL << 8) + +/* + * AFBC solid color blocks + * + * Indicates that the buffer makes use of solid-color blocks, whereby bandwidth + * can be reduced if a whole superblock is a single color. + */ +#define AFBC_FORMAT_MOD_SC (1ULL << 9) + +/* + * AFBC double-buffer + * + * Indicates that the buffer is allocated in a layout safe for front-buffer + * rendering. + */ +#define AFBC_FORMAT_MOD_DB (1ULL << 10) + +/* + * AFBC buffer content hints + * + * Indicates that the buffer includes per-superblock content hints. + */ +#define AFBC_FORMAT_MOD_BCH (1ULL << 11) + +/* + * Allwinner tiled modifier + * + * This tiling mode is implemented by the VPU found on all Allwinner platforms, + * codenamed sunxi. It is associated with a YUV format that uses either 2 or 3 + * planes. + * + * With this tiling, the luminance samples are disposed in tiles representing + * 32x32 pixels and the chrominance samples in tiles representing 32x64 pixels. + * The pixel order in each tile is linear and the tiles are disposed linearly, + * both in row-major order. + */ +#define DRM_FORMAT_MOD_ALLWINNER_TILED fourcc_mod_code(ALLWINNER, 1) + +#if defined(__cplusplus) +} +#endif + +#endif /* DRM_FOURCC_H */ diff --git a/libs/common/drm/include/libdrm/drm_mode.h b/libs/common/drm/include/libdrm/drm_mode.h new file mode 100644 index 0000000..5fe6c64 --- /dev/null +++ b/libs/common/drm/include/libdrm/drm_mode.h @@ -0,0 +1,914 @@ +/* + * Copyright (c) 2007 Dave Airlie + * Copyright (c) 2007 Jakob Bornecrantz + * Copyright (c) 2008 Red Hat Inc. + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * Copyright (c) 2007-2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _DRM_MODE_H +#define _DRM_MODE_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) /* deprecated */ +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) /* deprecated */ +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +#define DRM_MODE_TYPE_ALL (DRM_MODE_TYPE_PREFERRED | \ + DRM_MODE_TYPE_USERDEF | \ + DRM_MODE_TYPE_DRIVER) + +/* Video mode flags */ +/* bit compatible with the xrandr RR_ definitions (bits 0-13) + * + * ABI warning: Existing userspace really expects + * the mode flags to match the xrandr definitions. Any + * changes that don't match the xrandr definitions will + * likely need a new client cap or some other mechanism + * to avoid breaking existing userspace. This includes + * allocating new flags in the previously unused bits! + */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) /* deprecated */ +#define DRM_MODE_FLAG_PIXMUX (1<<11) /* deprecated */ +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) + /* + * When adding a new stereo mode don't forget to adjust DRM_MODE_FLAGS_3D_MAX + * (define not exposed to user space). + */ +#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) +#define DRM_MODE_FLAG_3D_NONE (0<<14) +#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) +#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) +#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) +#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + +/* Picture aspect ratio options */ +#define DRM_MODE_PICTURE_ASPECT_NONE 0 +#define DRM_MODE_PICTURE_ASPECT_4_3 1 +#define DRM_MODE_PICTURE_ASPECT_16_9 2 +#define DRM_MODE_PICTURE_ASPECT_64_27 3 +#define DRM_MODE_PICTURE_ASPECT_256_135 4 + +/* Content type options */ +#define DRM_MODE_CONTENT_TYPE_NO_DATA 0 +#define DRM_MODE_CONTENT_TYPE_GRAPHICS 1 +#define DRM_MODE_CONTENT_TYPE_PHOTO 2 +#define DRM_MODE_CONTENT_TYPE_CINEMA 3 +#define DRM_MODE_CONTENT_TYPE_GAME 4 + +/* Aspect ratio flag bitmask (4 bits 22:19) */ +#define DRM_MODE_FLAG_PIC_AR_MASK (0x0F<<19) +#define DRM_MODE_FLAG_PIC_AR_NONE \ + (DRM_MODE_PICTURE_ASPECT_NONE<<19) +#define DRM_MODE_FLAG_PIC_AR_4_3 \ + (DRM_MODE_PICTURE_ASPECT_4_3<<19) +#define DRM_MODE_FLAG_PIC_AR_16_9 \ + (DRM_MODE_PICTURE_ASPECT_16_9<<19) +#define DRM_MODE_FLAG_PIC_AR_64_27 \ + (DRM_MODE_PICTURE_ASPECT_64_27<<19) +#define DRM_MODE_FLAG_PIC_AR_256_135 \ + (DRM_MODE_PICTURE_ASPECT_256_135<<19) + +#define DRM_MODE_FLAG_ALL (DRM_MODE_FLAG_PHSYNC | \ + DRM_MODE_FLAG_NHSYNC | \ + DRM_MODE_FLAG_PVSYNC | \ + DRM_MODE_FLAG_NVSYNC | \ + DRM_MODE_FLAG_INTERLACE | \ + DRM_MODE_FLAG_DBLSCAN | \ + DRM_MODE_FLAG_CSYNC | \ + DRM_MODE_FLAG_PCSYNC | \ + DRM_MODE_FLAG_NCSYNC | \ + DRM_MODE_FLAG_HSKEW | \ + DRM_MODE_FLAG_DBLCLK | \ + DRM_MODE_FLAG_CLKDIV2 | \ + DRM_MODE_FLAG_3D_MASK) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NONE 0 /* Unmodified timing (display or + software can still scale) */ +#define DRM_MODE_SCALE_FULLSCREEN 1 /* Full screen, ignore aspect */ +#define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ +#define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 +#define DRM_MODE_DITHERING_AUTO 2 + +/* Dirty info options */ +#define DRM_MODE_DIRTY_OFF 0 +#define DRM_MODE_DIRTY_ON 1 +#define DRM_MODE_DIRTY_ANNOTATE 2 + +/* Link Status options */ +#define DRM_MODE_LINK_STATUS_GOOD 0 +#define DRM_MODE_LINK_STATUS_BAD 1 + +/* + * DRM_MODE_ROTATE_ + * + * Signals that a drm plane is been rotated degrees in counter + * clockwise direction. + * + * This define is provided as a convenience, looking up the property id + * using the name->prop id lookup is the preferred method. + */ +#define DRM_MODE_ROTATE_0 (1<<0) +#define DRM_MODE_ROTATE_90 (1<<1) +#define DRM_MODE_ROTATE_180 (1<<2) +#define DRM_MODE_ROTATE_270 (1<<3) + +/* + * DRM_MODE_ROTATE_MASK + * + * Bitmask used to look for drm plane rotations. + */ +#define DRM_MODE_ROTATE_MASK (\ + DRM_MODE_ROTATE_0 | \ + DRM_MODE_ROTATE_90 | \ + DRM_MODE_ROTATE_180 | \ + DRM_MODE_ROTATE_270) + +/* + * DRM_MODE_REFLECT_ + * + * Signals that the contents of a drm plane is reflected along the axis, + * in the same way as mirroring. + * See kerneldoc chapter "Plane Composition Properties" for more details. + * + * This define is provided as a convenience, looking up the property id + * using the name->prop id lookup is the preferred method. + */ +#define DRM_MODE_REFLECT_X (1<<4) +#define DRM_MODE_REFLECT_Y (1<<5) + +/* + * DRM_MODE_REFLECT_MASK + * + * Bitmask used to look for drm plane reflections. + */ +#define DRM_MODE_REFLECT_MASK (\ + DRM_MODE_REFLECT_X | \ + DRM_MODE_REFLECT_Y) + +/* Content Protection Flags */ +#define DRM_MODE_CONTENT_PROTECTION_UNDESIRED 0 +#define DRM_MODE_CONTENT_PROTECTION_DESIRED 1 +#define DRM_MODE_CONTENT_PROTECTION_ENABLED 2 + +struct drm_mode_modeinfo { + __u32 clock; + __u16 hdisplay; + __u16 hsync_start; + __u16 hsync_end; + __u16 htotal; + __u16 hskew; + __u16 vdisplay; + __u16 vsync_start; + __u16 vsync_end; + __u16 vtotal; + __u16 vscan; + + __u32 vrefresh; + + __u32 flags; + __u32 type; + char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + __u64 fb_id_ptr; + __u64 crtc_id_ptr; + __u64 connector_id_ptr; + __u64 encoder_id_ptr; + __u32 count_fbs; + __u32 count_crtcs; + __u32 count_connectors; + __u32 count_encoders; + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; +}; + +struct drm_mode_crtc { + __u64 set_connectors_ptr; + __u32 count_connectors; + + __u32 crtc_id; /**< Id */ + __u32 fb_id; /**< Id of framebuffer */ + + __u32 x; /**< x Position on the framebuffer */ + __u32 y; /**< y Position on the framebuffer */ + + __u32 gamma_size; + __u32 mode_valid; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_PRESENT_TOP_FIELD (1<<0) +#define DRM_MODE_PRESENT_BOTTOM_FIELD (1<<1) + +/* Planes blend with or override other bits on the CRTC */ +struct drm_mode_set_plane { + __u32 plane_id; + __u32 crtc_id; + __u32 fb_id; /* fb object contains surface format type */ + __u32 flags; /* see above flags */ + + /* Signed dest location allows it to be partially off screen */ + __s32 crtc_x; + __s32 crtc_y; + __u32 crtc_w; + __u32 crtc_h; + + /* Source values are 16.16 fixed point */ + __u32 src_x; + __u32 src_y; + __u32 src_h; + __u32 src_w; +}; + +struct drm_mode_get_plane { + __u32 plane_id; + + __u32 crtc_id; + __u32 fb_id; + + __u32 possible_crtcs; + __u32 gamma_size; + + __u32 count_format_types; + __u64 format_type_ptr; +}; + +struct drm_mode_get_plane_res { + __u64 plane_id_ptr; + __u32 count_planes; +}; + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 +#define DRM_MODE_ENCODER_VIRTUAL 5 +#define DRM_MODE_ENCODER_DSI 6 +#define DRM_MODE_ENCODER_DPMST 7 +#define DRM_MODE_ENCODER_DPI 8 + +struct drm_mode_get_encoder { + __u32 encoder_id; + __u32 encoder_type; + + __u32 crtc_id; /**< Id of crtc */ + + __u32 possible_crtcs; + __u32 possible_clones; +}; + +/* This is for connectors with multiple signal types. */ +/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ +enum drm_mode_subconnector { + DRM_MODE_SUBCONNECTOR_Automatic = 0, + DRM_MODE_SUBCONNECTOR_Unknown = 0, + DRM_MODE_SUBCONNECTOR_DVID = 3, + DRM_MODE_SUBCONNECTOR_DVIA = 4, + DRM_MODE_SUBCONNECTOR_Composite = 5, + DRM_MODE_SUBCONNECTOR_SVIDEO = 6, + DRM_MODE_SUBCONNECTOR_Component = 8, + DRM_MODE_SUBCONNECTOR_SCART = 9, +}; + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 +#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_eDP 14 +#define DRM_MODE_CONNECTOR_VIRTUAL 15 +#define DRM_MODE_CONNECTOR_DSI 16 +#define DRM_MODE_CONNECTOR_DPI 17 +#define DRM_MODE_CONNECTOR_WRITEBACK 18 + +struct drm_mode_get_connector { + + __u64 encoders_ptr; + __u64 modes_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + + __u32 count_modes; + __u32 count_props; + __u32 count_encoders; + + __u32 encoder_id; /**< Current Encoder */ + __u32 connector_id; /**< Id */ + __u32 connector_type; + __u32 connector_type_id; + + __u32 connection; + __u32 mm_width; /**< width in millimeters */ + __u32 mm_height; /**< height in millimeters */ + __u32 subpixel; + + __u32 pad; +}; + +#define DRM_MODE_PROP_PENDING (1<<0) /* deprecated, do not use */ +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) +#define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */ + +/* non-extended types: legacy bitmask, one bit per type: */ +#define DRM_MODE_PROP_LEGACY_TYPE ( \ + DRM_MODE_PROP_RANGE | \ + DRM_MODE_PROP_ENUM | \ + DRM_MODE_PROP_BLOB | \ + DRM_MODE_PROP_BITMASK) + +/* extended-types: rather than continue to consume a bit per type, + * grab a chunk of the bits to use as integer type id. + */ +#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) +#define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) + +/* the PROP_ATOMIC flag is used to hide properties from userspace that + * is not aware of atomic properties. This is mostly to work around + * older userspace (DDX drivers) that read/write each prop they find, + * without being aware that this could be triggering a lengthy modeset. + */ +#define DRM_MODE_PROP_ATOMIC 0x80000000 + +struct drm_mode_property_enum { + __u64 value; + char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_mode_get_property { + __u64 values_ptr; /* values and blob lengths */ + __u64 enum_blob_ptr; /* enum and blob id ptrs */ + + __u32 prop_id; + __u32 flags; + char name[DRM_PROP_NAME_LEN]; + + __u32 count_values; + /* This is only used to count enum values, not blobs. The _blobs is + * simply because of a historical reason, i.e. backwards compat. */ + __u32 count_enum_blobs; +}; + +struct drm_mode_connector_set_property { + __u64 value; + __u32 prop_id; + __u32 connector_id; +}; + +#define DRM_MODE_OBJECT_CRTC 0xcccccccc +#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 +#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 +#define DRM_MODE_OBJECT_MODE 0xdededede +#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 +#define DRM_MODE_OBJECT_FB 0xfbfbfbfb +#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb +#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee +#define DRM_MODE_OBJECT_ANY 0 + +struct drm_mode_obj_get_properties { + __u64 props_ptr; + __u64 prop_values_ptr; + __u32 count_props; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_obj_set_property { + __u64 value; + __u32 prop_id; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_get_blob { + __u32 blob_id; + __u32 length; + __u64 data; +}; + +struct drm_mode_fb_cmd { + __u32 fb_id; + __u32 width; + __u32 height; + __u32 pitch; + __u32 bpp; + __u32 depth; + /* driver specific handle */ + __u32 handle; +}; + +#define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ +#define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[] */ + +struct drm_mode_fb_cmd2 { + __u32 fb_id; + __u32 width; + __u32 height; + __u32 pixel_format; /* fourcc code from drm_fourcc.h */ + __u32 flags; /* see above flags */ + + /* + * In case of planar formats, this ioctl allows up to 4 + * buffer objects with offsets and pitches per plane. + * The pitch and offset order is dictated by the fourcc, + * e.g. NV12 (http://fourcc.org/yuv.php#NV12) is described as: + * + * YUV 4:2:0 image with a plane of 8 bit Y samples + * followed by an interleaved U/V plane containing + * 8 bit 2x2 subsampled colour difference samples. + * + * So it would consist of Y as offsets[0] and UV as + * offsets[1]. Note that offsets[0] will generally + * be 0 (but this is not required). + * + * To accommodate tiled, compressed, etc formats, a + * modifier can be specified. The default value of zero + * indicates "native" format as specified by the fourcc. + * Vendor specific modifier token. Note that even though + * it looks like we have a modifier per-plane, we in fact + * do not. The modifier for each plane must be identical. + * Thus all combinations of different data layouts for + * multi plane formats must be enumerated as separate + * modifiers. + */ + __u32 handles[4]; + __u32 pitches[4]; /* pitch for each plane */ + __u32 offsets[4]; /* offset of each plane */ + __u64 modifier[4]; /* ie, tiling, compress */ +}; + +#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 +#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02 +#define DRM_MODE_FB_DIRTY_FLAGS 0x03 + +#define DRM_MODE_FB_DIRTY_MAX_CLIPS 256 + +/* + * Mark a region of a framebuffer as dirty. + * + * Some hardware does not automatically update display contents + * as a hardware or software draw to a framebuffer. This ioctl + * allows userspace to tell the kernel and the hardware what + * regions of the framebuffer have changed. + * + * The kernel or hardware is free to update more then just the + * region specified by the clip rects. The kernel or hardware + * may also delay and/or coalesce several calls to dirty into a + * single update. + * + * Userspace may annotate the updates, the annotates are a + * promise made by the caller that the change is either a copy + * of pixels or a fill of a single color in the region specified. + * + * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then + * the number of updated regions are half of num_clips given, + * where the clip rects are paired in src and dst. The width and + * height of each one of the pairs must match. + * + * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller + * promises that the region specified of the clip rects is filled + * completely with a single color as given in the color argument. + */ + +struct drm_mode_fb_dirty_cmd { + __u32 fb_id; + __u32 flags; + __u32 color; + __u32 num_clips; + __u64 clips_ptr; +}; + +struct drm_mode_mode_cmd { + __u32 connector_id; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_CURSOR_BO 0x01 +#define DRM_MODE_CURSOR_MOVE 0x02 +#define DRM_MODE_CURSOR_FLAGS 0x03 + +/* + * depending on the value in flags different members are used. + * + * CURSOR_BO uses + * crtc_id + * width + * height + * handle - if 0 turns the cursor off + * + * CURSOR_MOVE uses + * crtc_id + * x + * y + */ +struct drm_mode_cursor { + __u32 flags; + __u32 crtc_id; + __s32 x; + __s32 y; + __u32 width; + __u32 height; + /* driver specific handle */ + __u32 handle; +}; + +struct drm_mode_cursor2 { + __u32 flags; + __u32 crtc_id; + __s32 x; + __s32 y; + __u32 width; + __u32 height; + /* driver specific handle */ + __u32 handle; + __s32 hot_x; + __s32 hot_y; +}; + +struct drm_mode_crtc_lut { + __u32 crtc_id; + __u32 gamma_size; + + /* pointers to arrays */ + __u64 red; + __u64 green; + __u64 blue; +}; + +struct drm_color_ctm { + /* + * Conversion matrix in S31.32 sign-magnitude + * (not two's complement!) format. + */ + __u64 matrix[9]; +}; + +struct drm_color_lut { + /* + * Values are mapped linearly to 0.0 - 1.0 range, with 0x0 == 0.0 and + * 0xffff == 1.0. + */ + __u16 red; + __u16 green; + __u16 blue; + __u16 reserved; +}; + +#define DRM_MODE_PAGE_FLIP_EVENT 0x01 +#define DRM_MODE_PAGE_FLIP_ASYNC 0x02 +#define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4 +#define DRM_MODE_PAGE_FLIP_TARGET_RELATIVE 0x8 +#define DRM_MODE_PAGE_FLIP_TARGET (DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE | \ + DRM_MODE_PAGE_FLIP_TARGET_RELATIVE) +#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT | \ + DRM_MODE_PAGE_FLIP_ASYNC | \ + DRM_MODE_PAGE_FLIP_TARGET) + +/* + * Request a page flip on the specified crtc. + * + * This ioctl will ask KMS to schedule a page flip for the specified + * crtc. Once any pending rendering targeting the specified fb (as of + * ioctl time) has completed, the crtc will be reprogrammed to display + * that fb after the next vertical refresh. The ioctl returns + * immediately, but subsequent rendering to the current fb will block + * in the execbuffer ioctl until the page flip happens. If a page + * flip is already pending as the ioctl is called, EBUSY will be + * returned. + * + * Flag DRM_MODE_PAGE_FLIP_EVENT requests that drm sends back a vblank + * event (see drm.h: struct drm_event_vblank) when the page flip is + * done. The user_data field passed in with this ioctl will be + * returned as the user_data field in the vblank event struct. + * + * Flag DRM_MODE_PAGE_FLIP_ASYNC requests that the flip happen + * 'as soon as possible', meaning that it not delay waiting for vblank. + * This may cause tearing on the screen. + * + * The reserved field must be zero. + */ + +struct drm_mode_crtc_page_flip { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 reserved; + __u64 user_data; +}; + +/* + * Request a page flip on the specified crtc. + * + * Same as struct drm_mode_crtc_page_flip, but supports new flags and + * re-purposes the reserved field: + * + * The sequence field must be zero unless either of the + * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is specified. When + * the ABSOLUTE flag is specified, the sequence field denotes the absolute + * vblank sequence when the flip should take effect. When the RELATIVE + * flag is specified, the sequence field denotes the relative (to the + * current one when the ioctl is called) vblank sequence when the flip + * should take effect. NOTE: DRM_IOCTL_WAIT_VBLANK must still be used to + * make sure the vblank sequence before the target one has passed before + * calling this ioctl. The purpose of the + * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is merely to clarify + * the target for when code dealing with a page flip runs during a + * vertical blank period. + */ + +struct drm_mode_crtc_page_flip_target { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 sequence; + __u64 user_data; +}; + +/* create a dumb scanout buffer */ +struct drm_mode_create_dumb { + __u32 height; + __u32 width; + __u32 bpp; + __u32 flags; + /* handle, pitch, size will be returned */ + __u32 handle; + __u32 pitch; + __u64 size; +}; + +/* set up for mmap of a dumb scanout buffer */ +struct drm_mode_map_dumb { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_mode_destroy_dumb { + __u32 handle; +}; + +/* page-flip flags are valid, plus: */ +#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 +#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 + +#define DRM_MODE_ATOMIC_FLAGS (\ + DRM_MODE_PAGE_FLIP_EVENT |\ + DRM_MODE_PAGE_FLIP_ASYNC |\ + DRM_MODE_ATOMIC_TEST_ONLY |\ + DRM_MODE_ATOMIC_NONBLOCK |\ + DRM_MODE_ATOMIC_ALLOW_MODESET) + +struct drm_mode_atomic { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 reserved; + __u64 user_data; +}; + +struct drm_format_modifier_blob { +#define FORMAT_BLOB_CURRENT 1 + /* Version of this blob format */ + __u32 version; + + /* Flags */ + __u32 flags; + + /* Number of fourcc formats supported */ + __u32 count_formats; + + /* Where in this blob the formats exist (in bytes) */ + __u32 formats_offset; + + /* Number of drm_format_modifiers */ + __u32 count_modifiers; + + /* Where in this blob the modifiers exist (in bytes) */ + __u32 modifiers_offset; + + /* __u32 formats[] */ + /* struct drm_format_modifier modifiers[] */ +}; + +struct drm_format_modifier { + /* Bitmask of formats in get_plane format list this info applies to. The + * offset allows a sliding window of which 64 formats (bits). + * + * Some examples: + * In today's world with < 65 formats, and formats 0, and 2 are + * supported + * 0x0000000000000005 + * ^-offset = 0, formats = 5 + * + * If the number formats grew to 128, and formats 98-102 are + * supported with the modifier: + * + * 0x0000007c00000000 0000000000000000 + * ^ + * |__offset = 64, formats = 0x7c00000000 + * + */ + __u64 formats; + __u32 offset; + __u32 pad; + + /* The modifier that applies to the >get_plane format list bitmask. */ + __u64 modifier; +}; + +/** + * Create a new 'blob' data property, copying length bytes from data pointer, + * and returning new blob ID. + */ +struct drm_mode_create_blob { + /** Pointer to data to copy. */ + __u64 data; + /** Length of data to copy. */ + __u32 length; + /** Return: new property ID. */ + __u32 blob_id; +}; + +/** + * Destroy a user-created blob property. + */ +struct drm_mode_destroy_blob { + __u32 blob_id; +}; + +/** + * Lease mode resources, creating another drm_master. + */ +struct drm_mode_create_lease { + /** Pointer to array of object ids (__u32) */ + __u64 object_ids; + /** Number of object ids */ + __u32 object_count; + /** flags for new FD (O_CLOEXEC, etc) */ + __u32 flags; + + /** Return: unique identifier for lessee. */ + __u32 lessee_id; + /** Return: file descriptor to new drm_master file */ + __u32 fd; +}; + +/** + * List lesses from a drm_master + */ +struct drm_mode_list_lessees { + /** Number of lessees. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_lessees; + __u32 pad; + + /** Pointer to lessees. + * pointer to __u64 array of lessee ids + */ + __u64 lessees_ptr; +}; + +/** + * Get leased objects + */ +struct drm_mode_get_lease { + /** Number of leased objects. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_objects; + __u32 pad; + + /** Pointer to objects. + * pointer to __u32 array of object ids + */ + __u64 objects_ptr; +}; + +/** + * Revoke lease + */ +struct drm_mode_revoke_lease { + /** Unique ID of lessee + */ + __u32 lessee_id; +}; + +/** + * struct drm_mode_rect - Two dimensional rectangle. + * @x1: Horizontal starting coordinate (inclusive). + * @y1: Vertical starting coordinate (inclusive). + * @x2: Horizontal ending coordinate (exclusive). + * @y2: Vertical ending coordinate (exclusive). + * + * With drm subsystem using struct drm_rect to manage rectangular area this + * export it to user-space. + * + * Currently used by drm_mode_atomic blob property FB_DAMAGE_CLIPS. + */ +struct drm_mode_rect { + __s32 x1; + __s32 y1; + __s32 x2; + __s32 y2; +}; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/drm_sarea.h b/libs/common/drm/include/libdrm/drm_sarea.h new file mode 100644 index 0000000..93025be --- /dev/null +++ b/libs/common/drm/include/libdrm/drm_sarea.h @@ -0,0 +1,92 @@ +/** + * \file drm_sarea.h + * \brief SAREA definitions + * + * \author Michel Dänzer + */ + +/* + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_SAREA_H_ +#define _DRM_SAREA_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* SAREA area needs to be at least a page */ +#if defined(__alpha__) +#define SAREA_MAX 0x2000U +#elif defined(__mips__) +#define SAREA_MAX 0x4000U +#elif defined(__ia64__) +#define SAREA_MAX 0x10000U /* 64kB */ +#else +/* Intel 830M driver needs at least 8k SAREA */ +#define SAREA_MAX 0x2000U +#endif + +/** Maximum number of drawables in the SAREA */ +#define SAREA_MAX_DRAWABLES 256 + +#define SAREA_DRAWABLE_CLAIMED_ENTRY 0x80000000 + +/** SAREA drawable */ +struct drm_sarea_drawable { + unsigned int stamp; + unsigned int flags; +}; + +/** SAREA frame */ +struct drm_sarea_frame { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int fullscreen; +}; + +/** SAREA */ +struct drm_sarea { + /** first thing is always the DRM locking structure */ + struct drm_hw_lock lock; + /** \todo Use readers/writer lock for drm_sarea::drawable_lock */ + struct drm_hw_lock drawable_lock; + struct drm_sarea_drawable drawableTable[SAREA_MAX_DRAWABLES]; /**< drawables */ + struct drm_sarea_frame frame; /**< frame */ + drm_context_t dummy_context; +}; + +typedef struct drm_sarea_drawable drm_sarea_drawable_t; +typedef struct drm_sarea_frame drm_sarea_frame_t; +typedef struct drm_sarea drm_sarea_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* _DRM_SAREA_H_ */ diff --git a/libs/common/drm/include/libdrm/i915_drm.h b/libs/common/drm/include/libdrm/i915_drm.h new file mode 100644 index 0000000..72afd94 --- /dev/null +++ b/libs/common/drm/include/libdrm/i915_drm.h @@ -0,0 +1,1915 @@ +/* + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _I915_DRM_H_ +#define _I915_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + */ + +/** + * DOC: uevents generated by i915 on it's device node + * + * I915_L3_PARITY_UEVENT - Generated when the driver receives a parity mismatch + * event from the gpu l3 cache. Additional information supplied is ROW, + * BANK, SUBBANK, SLICE of the affected cacheline. Userspace should keep + * track of these events and if a specific cache-line seems to have a + * persistent error remap it with the l3 remapping tool supplied in + * intel-gpu-tools. The value supplied with the event is always 1. + * + * I915_ERROR_UEVENT - Generated upon error detection, currently only via + * hangcheck. The error detection event is a good indicator of when things + * began to go badly. The value supplied with the event is a 1 upon error + * detection, and a 0 upon reset completion, signifying no more error + * exists. NOTE: Disabling hangcheck or reset via module parameter will + * cause the related events to not be seen. + * + * I915_RESET_UEVENT - Event is generated just before an attempt to reset the + * the GPU. The value supplied with the event is always 1. NOTE: Disable + * reset via module parameter will cause this event to not be seen. + */ +#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR" +#define I915_ERROR_UEVENT "ERROR" +#define I915_RESET_UEVENT "RESET" + +/* + * i915_user_extension: Base class for defining a chain of extensions + * + * Many interfaces need to grow over time. In most cases we can simply + * extend the struct and have userspace pass in more data. Another option, + * as demonstrated by Vulkan's approach to providing extensions for forward + * and backward compatibility, is to use a list of optional structs to + * provide those extra details. + * + * The key advantage to using an extension chain is that it allows us to + * redefine the interface more easily than an ever growing struct of + * increasing complexity, and for large parts of that interface to be + * entirely optional. The downside is more pointer chasing; chasing across + * the boundary with pointers encapsulated inside u64. + */ +struct i915_user_extension { + __u64 next_extension; + __u32 name; + __u32 flags; /* All undefined bits must be zero. */ + __u32 rsvd[4]; /* Reserved for future use; must be zero. */ +}; + +/* + * MOCS indexes used for GPU surfaces, defining the cacheability of the + * surface data and the coherency for this data wrt. CPU vs. GPU accesses. + */ +enum i915_mocs_table_index { + /* + * Not cached anywhere, coherency between CPU and GPU accesses is + * guaranteed. + */ + I915_MOCS_UNCACHED, + /* + * Cacheability and coherency controlled by the kernel automatically + * based on the DRM_I915_GEM_SET_CACHING IOCTL setting and the current + * usage of the surface (used for display scanout or not). + */ + I915_MOCS_PTE, + /* + * Cached in all GPU caches available on the platform. + * Coherency between CPU and GPU accesses to the surface is not + * guaranteed without extra synchronization. + */ + I915_MOCS_CACHED, +}; + +/* + * Different engines serve different roles, and there may be more than one + * engine serving each role. enum drm_i915_gem_engine_class provides a + * classification of the role of the engine, which may be used when requesting + * operations to be performed on a certain subset of engines, or for providing + * information about that group. + */ +enum drm_i915_gem_engine_class { + I915_ENGINE_CLASS_RENDER = 0, + I915_ENGINE_CLASS_COPY = 1, + I915_ENGINE_CLASS_VIDEO = 2, + I915_ENGINE_CLASS_VIDEO_ENHANCE = 3, + + /* should be kept compact */ + + I915_ENGINE_CLASS_INVALID = -1 +}; + +/** + * DOC: perf_events exposed by i915 through /sys/bus/event_sources/drivers/i915 + * + */ + +enum drm_i915_pmu_engine_sample { + I915_SAMPLE_BUSY = 0, + I915_SAMPLE_WAIT = 1, + I915_SAMPLE_SEMA = 2 +}; + +#define I915_PMU_SAMPLE_BITS (4) +#define I915_PMU_SAMPLE_MASK (0xf) +#define I915_PMU_SAMPLE_INSTANCE_BITS (8) +#define I915_PMU_CLASS_SHIFT \ + (I915_PMU_SAMPLE_BITS + I915_PMU_SAMPLE_INSTANCE_BITS) + +#define __I915_PMU_ENGINE(class, instance, sample) \ + ((class) << I915_PMU_CLASS_SHIFT | \ + (instance) << I915_PMU_SAMPLE_BITS | \ + (sample)) + +#define I915_PMU_ENGINE_BUSY(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_BUSY) + +#define I915_PMU_ENGINE_WAIT(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_WAIT) + +#define I915_PMU_ENGINE_SEMA(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_SEMA) + +#define __I915_PMU_OTHER(x) (__I915_PMU_ENGINE(0xff, 0xff, 0xf) + 1 + (x)) + +#define I915_PMU_ACTUAL_FREQUENCY __I915_PMU_OTHER(0) +#define I915_PMU_REQUESTED_FREQUENCY __I915_PMU_OTHER(1) +#define I915_PMU_INTERRUPTS __I915_PMU_OTHER(2) +#define I915_PMU_RC6_RESIDENCY __I915_PMU_OTHER(3) + +#define I915_PMU_LAST I915_PMU_RC6_RESIDENCY + +/* Each region is a minimum of 16k, and there are at most 255 of them. + */ +#define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use + * of chars for next/prev indices */ +#define I915_LOG_MIN_TEX_REGION_SIZE 14 + +typedef struct _drm_i915_init { + enum { + I915_INIT_DMA = 0x01, + I915_CLEANUP_DMA = 0x02, + I915_RESUME_DMA = 0x03 + } func; + unsigned int mmio_offset; + int sarea_priv_offset; + unsigned int ring_start; + unsigned int ring_end; + unsigned int ring_size; + unsigned int front_offset; + unsigned int back_offset; + unsigned int depth_offset; + unsigned int w; + unsigned int h; + unsigned int pitch; + unsigned int pitch_bits; + unsigned int back_pitch; + unsigned int depth_pitch; + unsigned int cpp; + unsigned int chipset; +} drm_i915_init_t; + +typedef struct _drm_i915_sarea { + struct drm_tex_region texList[I915_NR_TEX_REGIONS + 1]; + int last_upload; /* last time texture was uploaded */ + int last_enqueue; /* last time a buffer was enqueued */ + int last_dispatch; /* age of the most recently dispatched buffer */ + int ctxOwner; /* last context to upload state */ + int texAge; + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ + int perf_boxes; /* performance boxes to be displayed */ + int width, height; /* screen size in pixels */ + + drm_handle_t front_handle; + int front_offset; + int front_size; + + drm_handle_t back_handle; + int back_offset; + int back_size; + + drm_handle_t depth_handle; + int depth_offset; + int depth_size; + + drm_handle_t tex_handle; + int tex_offset; + int tex_size; + int log_tex_granularity; + int pitch; + int rotation; /* 0, 90, 180 or 270 */ + int rotated_offset; + int rotated_size; + int rotated_pitch; + int virtualX, virtualY; + + unsigned int front_tiled; + unsigned int back_tiled; + unsigned int depth_tiled; + unsigned int rotated_tiled; + unsigned int rotated2_tiled; + + int pipeA_x; + int pipeA_y; + int pipeA_w; + int pipeA_h; + int pipeB_x; + int pipeB_y; + int pipeB_w; + int pipeB_h; + + /* fill out some space for old userspace triple buffer */ + drm_handle_t unused_handle; + __u32 unused1, unused2, unused3; + + /* buffer object handles for static buffers. May change + * over the lifetime of the client. + */ + __u32 front_bo_handle; + __u32 back_bo_handle; + __u32 unused_bo_handle; + __u32 depth_bo_handle; + +} drm_i915_sarea_t; + +/* due to userspace building against these headers we need some compat here */ +#define planeA_x pipeA_x +#define planeA_y pipeA_y +#define planeA_w pipeA_w +#define planeA_h pipeA_h +#define planeB_x pipeB_x +#define planeB_y pipeB_y +#define planeB_w pipeB_w +#define planeB_h pipeB_h + +/* Flags for perf_boxes + */ +#define I915_BOX_RING_EMPTY 0x1 +#define I915_BOX_FLIP 0x2 +#define I915_BOX_WAIT 0x4 +#define I915_BOX_TEXTURE_LOAD 0x8 +#define I915_BOX_LOST_CONTEXT 0x10 + +/* + * i915 specific ioctls. + * + * The device specific ioctl range is [DRM_COMMAND_BASE, DRM_COMMAND_END) ie + * [0x40, 0xa0) (a0 is excluded). The numbers below are defined as offset + * against DRM_COMMAND_BASE and should be between [0x0, 0x60). + */ +#define DRM_I915_INIT 0x00 +#define DRM_I915_FLUSH 0x01 +#define DRM_I915_FLIP 0x02 +#define DRM_I915_BATCHBUFFER 0x03 +#define DRM_I915_IRQ_EMIT 0x04 +#define DRM_I915_IRQ_WAIT 0x05 +#define DRM_I915_GETPARAM 0x06 +#define DRM_I915_SETPARAM 0x07 +#define DRM_I915_ALLOC 0x08 +#define DRM_I915_FREE 0x09 +#define DRM_I915_INIT_HEAP 0x0a +#define DRM_I915_CMDBUFFER 0x0b +#define DRM_I915_DESTROY_HEAP 0x0c +#define DRM_I915_SET_VBLANK_PIPE 0x0d +#define DRM_I915_GET_VBLANK_PIPE 0x0e +#define DRM_I915_VBLANK_SWAP 0x0f +#define DRM_I915_HWS_ADDR 0x11 +#define DRM_I915_GEM_INIT 0x13 +#define DRM_I915_GEM_EXECBUFFER 0x14 +#define DRM_I915_GEM_PIN 0x15 +#define DRM_I915_GEM_UNPIN 0x16 +#define DRM_I915_GEM_BUSY 0x17 +#define DRM_I915_GEM_THROTTLE 0x18 +#define DRM_I915_GEM_ENTERVT 0x19 +#define DRM_I915_GEM_LEAVEVT 0x1a +#define DRM_I915_GEM_CREATE 0x1b +#define DRM_I915_GEM_PREAD 0x1c +#define DRM_I915_GEM_PWRITE 0x1d +#define DRM_I915_GEM_MMAP 0x1e +#define DRM_I915_GEM_SET_DOMAIN 0x1f +#define DRM_I915_GEM_SW_FINISH 0x20 +#define DRM_I915_GEM_SET_TILING 0x21 +#define DRM_I915_GEM_GET_TILING 0x22 +#define DRM_I915_GEM_GET_APERTURE 0x23 +#define DRM_I915_GEM_MMAP_GTT 0x24 +#define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25 +#define DRM_I915_GEM_MADVISE 0x26 +#define DRM_I915_OVERLAY_PUT_IMAGE 0x27 +#define DRM_I915_OVERLAY_ATTRS 0x28 +#define DRM_I915_GEM_EXECBUFFER2 0x29 +#define DRM_I915_GEM_EXECBUFFER2_WR DRM_I915_GEM_EXECBUFFER2 +#define DRM_I915_GET_SPRITE_COLORKEY 0x2a +#define DRM_I915_SET_SPRITE_COLORKEY 0x2b +#define DRM_I915_GEM_WAIT 0x2c +#define DRM_I915_GEM_CONTEXT_CREATE 0x2d +#define DRM_I915_GEM_CONTEXT_DESTROY 0x2e +#define DRM_I915_GEM_SET_CACHING 0x2f +#define DRM_I915_GEM_GET_CACHING 0x30 +#define DRM_I915_REG_READ 0x31 +#define DRM_I915_GET_RESET_STATS 0x32 +#define DRM_I915_GEM_USERPTR 0x33 +#define DRM_I915_GEM_CONTEXT_GETPARAM 0x34 +#define DRM_I915_GEM_CONTEXT_SETPARAM 0x35 +#define DRM_I915_PERF_OPEN 0x36 +#define DRM_I915_PERF_ADD_CONFIG 0x37 +#define DRM_I915_PERF_REMOVE_CONFIG 0x38 +#define DRM_I915_QUERY 0x39 +/* Must be kept compact -- no holes */ + +#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) +#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) +#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP) +#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t) +#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t) +#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t) +#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, drm_i915_getparam_t) +#define DRM_IOCTL_I915_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SETPARAM, drm_i915_setparam_t) +#define DRM_IOCTL_I915_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_ALLOC, drm_i915_mem_alloc_t) +#define DRM_IOCTL_I915_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FREE, drm_i915_mem_free_t) +#define DRM_IOCTL_I915_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT_HEAP, drm_i915_mem_init_heap_t) +#define DRM_IOCTL_I915_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_CMDBUFFER, drm_i915_cmdbuffer_t) +#define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) +#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) +#define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2_WR DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2_WR, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) +#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) +#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) +#define DRM_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_CACHING, struct drm_i915_gem_caching) +#define DRM_IOCTL_I915_GEM_GET_CACHING DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GET_CACHING, struct drm_i915_gem_caching) +#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) +#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT) +#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT) +#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) +#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) +#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) +#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) +#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt) +#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) +#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish) +#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) +#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) +#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) +#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id) +#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise) +#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image) +#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) +#define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create_ext) +#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) +#define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read) +#define DRM_IOCTL_I915_GET_RESET_STATS DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats) +#define DRM_IOCTL_I915_GEM_USERPTR DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr) +#define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_PERF_OPEN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_OPEN, struct drm_i915_perf_open_param) +#define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) +#define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) +#define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) + +/* Allow drivers to submit batchbuffers directly to hardware, relying + * on the security mechanisms provided by hardware. + */ +typedef struct drm_i915_batchbuffer { + int start; /* agp offset */ + int used; /* nr bytes in use */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect *cliprects; /* pointer to userspace cliprects */ +} drm_i915_batchbuffer_t; + +/* As above, but pass a pointer to userspace buffer which can be + * validated by the kernel prior to sending to hardware. + */ +typedef struct _drm_i915_cmdbuffer { + char *buf; /* pointer to userspace command buffer */ + int sz; /* nr bytes in buf */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect *cliprects; /* pointer to userspace cliprects */ +} drm_i915_cmdbuffer_t; + +/* Userspace can request & wait on irq's: + */ +typedef struct drm_i915_irq_emit { + int *irq_seq; +} drm_i915_irq_emit_t; + +typedef struct drm_i915_irq_wait { + int irq_seq; +} drm_i915_irq_wait_t; + +/* + * Different modes of per-process Graphics Translation Table, + * see I915_PARAM_HAS_ALIASING_PPGTT + */ +#define I915_GEM_PPGTT_NONE 0 +#define I915_GEM_PPGTT_ALIASING 1 +#define I915_GEM_PPGTT_FULL 2 + +/* Ioctl to query kernel params: + */ +#define I915_PARAM_IRQ_ACTIVE 1 +#define I915_PARAM_ALLOW_BATCHBUFFER 2 +#define I915_PARAM_LAST_DISPATCH 3 +#define I915_PARAM_CHIPSET_ID 4 +#define I915_PARAM_HAS_GEM 5 +#define I915_PARAM_NUM_FENCES_AVAIL 6 +#define I915_PARAM_HAS_OVERLAY 7 +#define I915_PARAM_HAS_PAGEFLIPPING 8 +#define I915_PARAM_HAS_EXECBUF2 9 +#define I915_PARAM_HAS_BSD 10 +#define I915_PARAM_HAS_BLT 11 +#define I915_PARAM_HAS_RELAXED_FENCING 12 +#define I915_PARAM_HAS_COHERENT_RINGS 13 +#define I915_PARAM_HAS_EXEC_CONSTANTS 14 +#define I915_PARAM_HAS_RELAXED_DELTA 15 +#define I915_PARAM_HAS_GEN7_SOL_RESET 16 +#define I915_PARAM_HAS_LLC 17 +#define I915_PARAM_HAS_ALIASING_PPGTT 18 +#define I915_PARAM_HAS_WAIT_TIMEOUT 19 +#define I915_PARAM_HAS_SEMAPHORES 20 +#define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21 +#define I915_PARAM_HAS_VEBOX 22 +#define I915_PARAM_HAS_SECURE_BATCHES 23 +#define I915_PARAM_HAS_PINNED_BATCHES 24 +#define I915_PARAM_HAS_EXEC_NO_RELOC 25 +#define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 +#define I915_PARAM_HAS_WT 27 +#define I915_PARAM_CMD_PARSER_VERSION 28 +#define I915_PARAM_HAS_COHERENT_PHYS_GTT 29 +#define I915_PARAM_MMAP_VERSION 30 +#define I915_PARAM_HAS_BSD2 31 +#define I915_PARAM_REVISION 32 +#define I915_PARAM_SUBSLICE_TOTAL 33 +#define I915_PARAM_EU_TOTAL 34 +#define I915_PARAM_HAS_GPU_RESET 35 +#define I915_PARAM_HAS_RESOURCE_STREAMER 36 +#define I915_PARAM_HAS_EXEC_SOFTPIN 37 +#define I915_PARAM_HAS_POOLED_EU 38 +#define I915_PARAM_MIN_EU_IN_POOL 39 +#define I915_PARAM_MMAP_GTT_VERSION 40 + +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports user defined execution + * priorities and the driver will attempt to execute batches in priority order. + * The param returns a capability bitmask, nonzero implies that the scheduler + * is enabled, with different features present according to the mask. + * + * The initial priority for each batch is supplied by the context and is + * controlled via I915_CONTEXT_PARAM_PRIORITY. + */ +#define I915_PARAM_HAS_SCHEDULER 41 +#define I915_SCHEDULER_CAP_ENABLED (1ul << 0) +#define I915_SCHEDULER_CAP_PRIORITY (1ul << 1) +#define I915_SCHEDULER_CAP_PREEMPTION (1ul << 2) +#define I915_SCHEDULER_CAP_SEMAPHORES (1ul << 3) + +#define I915_PARAM_HUC_STATUS 42 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to opt-out of + * synchronisation with implicit fencing on individual objects. + * See EXEC_OBJECT_ASYNC. + */ +#define I915_PARAM_HAS_EXEC_ASYNC 43 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports explicit fence support - + * both being able to pass in a sync_file fd to wait upon before executing, + * and being able to return a new sync_file fd that is signaled when the + * current request is complete. See I915_EXEC_FENCE_IN and I915_EXEC_FENCE_OUT. + */ +#define I915_PARAM_HAS_EXEC_FENCE 44 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to capture + * user specified buffers for post-mortem debugging of GPU hangs. See + * EXEC_OBJECT_CAPTURE. + */ +#define I915_PARAM_HAS_EXEC_CAPTURE 45 + +#define I915_PARAM_SLICE_MASK 46 + +/* Assuming it's uniform for each slice, this queries the mask of subslices + * per-slice for this system. + */ +#define I915_PARAM_SUBSLICE_MASK 47 + +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying the batch buffer + * as the first execobject as opposed to the last. See I915_EXEC_BATCH_FIRST. + */ +#define I915_PARAM_HAS_EXEC_BATCH_FIRST 48 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying an array of + * drm_i915_gem_exec_fence structures. See I915_EXEC_FENCE_ARRAY. + */ +#define I915_PARAM_HAS_EXEC_FENCE_ARRAY 49 + +/* + * Query whether every context (both per-file default and user created) is + * isolated (insofar as HW supports). If this parameter is not true, then + * freshly created contexts may inherit values from an existing context, + * rather than default HW values. If true, it also ensures (insofar as HW + * supports) that all state set by this context will not leak to any other + * context. + * + * As not every engine across every gen support contexts, the returned + * value reports the support of context isolation for individual engines by + * returning a bitmask of each engine class set to true if that class supports + * isolation. + */ +#define I915_PARAM_HAS_CONTEXT_ISOLATION 50 + +/* Frequency of the command streamer timestamps given by the *_TIMESTAMP + * registers. This used to be fixed per platform but from CNL onwards, this + * might vary depending on the parts. + */ +#define I915_PARAM_CS_TIMESTAMP_FREQUENCY 51 + +/* + * Once upon a time we supposed that writes through the GGTT would be + * immediately in physical memory (once flushed out of the CPU path). However, + * on a few different processors and chipsets, this is not necessarily the case + * as the writes appear to be buffered internally. Thus a read of the backing + * storage (physical memory) via a different path (with different physical tags + * to the indirect write via the GGTT) will see stale values from before + * the GGTT write. Inside the kernel, we can for the most part keep track of + * the different read/write domains in use (e.g. set-domain), but the assumption + * of coherency is baked into the ABI, hence reporting its true state in this + * parameter. + * + * Reports true when writes via mmap_gtt are immediately visible following an + * lfence to flush the WCB. + * + * Reports false when writes via mmap_gtt are indeterminately delayed in an in + * internal buffer and are _not_ immediately visible to third parties accessing + * directly via mmap_cpu/mmap_wc. Use of mmap_gtt as part of an IPC + * communications channel when reporting false is strongly disadvised. + */ +#define I915_PARAM_MMAP_GTT_COHERENT 52 + +/* Must be kept compact -- no holes and well documented */ + +typedef struct drm_i915_getparam { + __s32 param; + /* + * WARNING: Using pointers instead of fixed-size u64 means we need to write + * compat32 code. Don't repeat this mistake. + */ + int *value; +} drm_i915_getparam_t; + +/* Ioctl to set kernel params: + */ +#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1 +#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2 +#define I915_SETPARAM_ALLOW_BATCHBUFFER 3 +#define I915_SETPARAM_NUM_USED_FENCES 4 +/* Must be kept compact -- no holes */ + +typedef struct drm_i915_setparam { + int param; + int value; +} drm_i915_setparam_t; + +/* A memory manager for regions of shared memory: + */ +#define I915_MEM_REGION_AGP 1 + +typedef struct drm_i915_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or agp */ +} drm_i915_mem_alloc_t; + +typedef struct drm_i915_mem_free { + int region; + int region_offset; +} drm_i915_mem_free_t; + +typedef struct drm_i915_mem_init_heap { + int region; + int size; + int start; +} drm_i915_mem_init_heap_t; + +/* Allow memory manager to be torn down and re-initialized (eg on + * rotate): + */ +typedef struct drm_i915_mem_destroy_heap { + int region; +} drm_i915_mem_destroy_heap_t; + +/* Allow X server to configure which pipes to monitor for vblank signals + */ +#define DRM_I915_VBLANK_PIPE_A 1 +#define DRM_I915_VBLANK_PIPE_B 2 + +typedef struct drm_i915_vblank_pipe { + int pipe; +} drm_i915_vblank_pipe_t; + +/* Schedule buffer swap at given vertical blank: + */ +typedef struct drm_i915_vblank_swap { + drm_drawable_t drawable; + enum drm_vblank_seq_type seqtype; + unsigned int sequence; +} drm_i915_vblank_swap_t; + +typedef struct drm_i915_hws_addr { + __u64 addr; +} drm_i915_hws_addr_t; + +struct drm_i915_gem_init { + /** + * Beginning offset in the GTT to be managed by the DRM memory + * manager. + */ + __u64 gtt_start; + /** + * Ending offset in the GTT to be managed by the DRM memory + * manager. + */ + __u64 gtt_end; +}; + +struct drm_i915_gem_create { + /** + * Requested size for the object. + * + * The (page-aligned) allocated size for the object will be returned. + */ + __u64 size; + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; + __u32 pad; +}; + +struct drm_i915_gem_pread { + /** Handle for the object being read. */ + __u32 handle; + __u32 pad; + /** Offset into the object to read from */ + __u64 offset; + /** Length of data to read */ + __u64 size; + /** + * Pointer to write the data into. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 data_ptr; +}; + +struct drm_i915_gem_pwrite { + /** Handle for the object being written to. */ + __u32 handle; + __u32 pad; + /** Offset into the object to write to */ + __u64 offset; + /** Length of data to write */ + __u64 size; + /** + * Pointer to read the data from. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 data_ptr; +}; + +struct drm_i915_gem_mmap { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** Offset in the object to map. */ + __u64 offset; + /** + * Length of data to map. + * + * The value will be page-aligned. + */ + __u64 size; + /** + * Returned pointer the data was mapped at. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 addr_ptr; + + /** + * Flags for extended behaviour. + * + * Added in version 2. + */ + __u64 flags; +#define I915_MMAP_WC 0x1 +}; + +struct drm_i915_gem_mmap_gtt { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_i915_gem_set_domain { + /** Handle for the object */ + __u32 handle; + + /** New read domains */ + __u32 read_domains; + + /** New write domain */ + __u32 write_domain; +}; + +struct drm_i915_gem_sw_finish { + /** Handle for the object */ + __u32 handle; +}; + +struct drm_i915_gem_relocation_entry { + /** + * Handle of the buffer being pointed to by this relocation entry. + * + * It's appealing to make this be an index into the mm_validate_entry + * list to refer to the buffer, but this allows the driver to create + * a relocation list for state buffers and not re-write it per + * exec using the buffer. + */ + __u32 target_handle; + + /** + * Value to be added to the offset of the target buffer to make up + * the relocation entry. + */ + __u32 delta; + + /** Offset in the buffer the relocation entry will be written into */ + __u64 offset; + + /** + * Offset value of the target buffer that the relocation entry was last + * written as. + * + * If the buffer has the same offset as last time, we can skip syncing + * and writing the relocation. This value is written back out by + * the execbuffer ioctl when the relocation is written. + */ + __u64 presumed_offset; + + /** + * Target memory domains read by this operation. + */ + __u32 read_domains; + + /** + * Target memory domains written by this operation. + * + * Note that only one domain may be written by the whole + * execbuffer operation, so that where there are conflicts, + * the application will get -EINVAL back. + */ + __u32 write_domain; +}; + +/** @{ + * Intel memory domains + * + * Most of these just align with the various caches in + * the system and are used to flush and invalidate as + * objects end up cached in different domains. + */ +/** CPU cache */ +#define I915_GEM_DOMAIN_CPU 0x00000001 +/** Render cache, used by 2D and 3D drawing */ +#define I915_GEM_DOMAIN_RENDER 0x00000002 +/** Sampler cache, used by texture engine */ +#define I915_GEM_DOMAIN_SAMPLER 0x00000004 +/** Command queue, used to load batch buffers */ +#define I915_GEM_DOMAIN_COMMAND 0x00000008 +/** Instruction cache, used by shader programs */ +#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010 +/** Vertex address cache */ +#define I915_GEM_DOMAIN_VERTEX 0x00000020 +/** GTT domain - aperture and scanout */ +#define I915_GEM_DOMAIN_GTT 0x00000040 +/** WC domain - uncached access */ +#define I915_GEM_DOMAIN_WC 0x00000080 +/** @} */ + +struct drm_i915_gem_exec_object { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + __u32 handle; + + /** Number of relocations to be performed on this buffer */ + __u32 relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + __u64 relocs_ptr; + + /** Required alignment in graphics aperture */ + __u64 alignment; + + /** + * Returned value of the updated offset of the object, for future + * presumed_offset writes. + */ + __u64 offset; +}; + +struct drm_i915_gem_execbuffer { + /** + * List of buffers to be validated with their relocations to be + * performend on them. + * + * This is a pointer to an array of struct drm_i915_gem_validate_entry. + * + * These buffers must be listed in an order such that all relocations + * a buffer is performing refer to buffers that have already appeared + * in the validate list. + */ + __u64 buffers_ptr; + __u32 buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + __u32 batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + __u32 batch_len; + __u32 DR1; + __u32 DR4; + __u32 num_cliprects; + /** This is a struct drm_clip_rect *cliprects */ + __u64 cliprects_ptr; +}; + +struct drm_i915_gem_exec_object2 { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + __u32 handle; + + /** Number of relocations to be performed on this buffer */ + __u32 relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + __u64 relocs_ptr; + + /** Required alignment in graphics aperture */ + __u64 alignment; + + /** + * When the EXEC_OBJECT_PINNED flag is specified this is populated by + * the user with the GTT offset at which this object will be pinned. + * When the I915_EXEC_NO_RELOC flag is specified this must contain the + * presumed_offset of the object. + * During execbuffer2 the kernel populates it with the value of the + * current GTT offset of the object, for future presumed_offset writes. + */ + __u64 offset; + +#define EXEC_OBJECT_NEEDS_FENCE (1<<0) +#define EXEC_OBJECT_NEEDS_GTT (1<<1) +#define EXEC_OBJECT_WRITE (1<<2) +#define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3) +#define EXEC_OBJECT_PINNED (1<<4) +#define EXEC_OBJECT_PAD_TO_SIZE (1<<5) +/* The kernel implicitly tracks GPU activity on all GEM objects, and + * synchronises operations with outstanding rendering. This includes + * rendering on other devices if exported via dma-buf. However, sometimes + * this tracking is too coarse and the user knows better. For example, + * if the object is split into non-overlapping ranges shared between different + * clients or engines (i.e. suballocating objects), the implicit tracking + * by kernel assumes that each operation affects the whole object rather + * than an individual range, causing needless synchronisation between clients. + * The kernel will also forgo any CPU cache flushes prior to rendering from + * the object as the client is expected to be also handling such domain + * tracking. + * + * The kernel maintains the implicit tracking in order to manage resources + * used by the GPU - this flag only disables the synchronisation prior to + * rendering with this object in this execbuf. + * + * Opting out of implicit synhronisation requires the user to do its own + * explicit tracking to avoid rendering corruption. See, for example, + * I915_PARAM_HAS_EXEC_FENCE to order execbufs and execute them asynchronously. + */ +#define EXEC_OBJECT_ASYNC (1<<6) +/* Request that the contents of this execobject be copied into the error + * state upon a GPU hang involving this batch for post-mortem debugging. + * These buffers are recorded in no particular order as "user" in + * /sys/class/drm/cardN/error. Query I915_PARAM_HAS_EXEC_CAPTURE to see + * if the kernel supports this flag. + */ +#define EXEC_OBJECT_CAPTURE (1<<7) +/* All remaining bits are MBZ and RESERVED FOR FUTURE USE */ +#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_CAPTURE<<1) + __u64 flags; + + union { + __u64 rsvd1; + __u64 pad_to_size; + }; + __u64 rsvd2; +}; + +struct drm_i915_gem_exec_fence { + /** + * User's handle for a drm_syncobj to wait on or signal. + */ + __u32 handle; + +#define I915_EXEC_FENCE_WAIT (1<<0) +#define I915_EXEC_FENCE_SIGNAL (1<<1) +#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SIGNAL << 1)) + __u32 flags; +}; + +struct drm_i915_gem_execbuffer2 { + /** + * List of gem_exec_object2 structs + */ + __u64 buffers_ptr; + __u32 buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + __u32 batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + __u32 batch_len; + __u32 DR1; + __u32 DR4; + __u32 num_cliprects; + /** + * This is a struct drm_clip_rect *cliprects if I915_EXEC_FENCE_ARRAY + * is not set. If I915_EXEC_FENCE_ARRAY is set, then this is a + * struct drm_i915_gem_exec_fence *fences. + */ + __u64 cliprects_ptr; +#define I915_EXEC_RING_MASK (0x3f) +#define I915_EXEC_DEFAULT (0<<0) +#define I915_EXEC_RENDER (1<<0) +#define I915_EXEC_BSD (2<<0) +#define I915_EXEC_BLT (3<<0) +#define I915_EXEC_VEBOX (4<<0) + +/* Used for switching the constants addressing mode on gen4+ RENDER ring. + * Gen6+ only supports relative addressing to dynamic state (default) and + * absolute addressing. + * + * These flags are ignored for the BSD and BLT rings. + */ +#define I915_EXEC_CONSTANTS_MASK (3<<6) +#define I915_EXEC_CONSTANTS_REL_GENERAL (0<<6) /* default */ +#define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6) +#define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */ + __u64 flags; + __u64 rsvd1; /* now used for context info */ + __u64 rsvd2; +}; + +/** Resets the SO write offset registers for transform feedback on gen7. */ +#define I915_EXEC_GEN7_SOL_RESET (1<<8) + +/** Request a privileged ("secure") batch buffer. Note only available for + * DRM_ROOT_ONLY | DRM_MASTER processes. + */ +#define I915_EXEC_SECURE (1<<9) + +/** Inform the kernel that the batch is and will always be pinned. This + * negates the requirement for a workaround to be performed to avoid + * an incoherent CS (such as can be found on 830/845). If this flag is + * not passed, the kernel will endeavour to make sure the batch is + * coherent with the CS before execution. If this flag is passed, + * userspace assumes the responsibility for ensuring the same. + */ +#define I915_EXEC_IS_PINNED (1<<10) + +/** Provide a hint to the kernel that the command stream and auxiliary + * state buffers already holds the correct presumed addresses and so the + * relocation process may be skipped if no buffers need to be moved in + * preparation for the execbuffer. + */ +#define I915_EXEC_NO_RELOC (1<<11) + +/** Use the reloc.handle as an index into the exec object array rather + * than as the per-file handle. + */ +#define I915_EXEC_HANDLE_LUT (1<<12) + +/** Used for switching BSD rings on the platforms with two BSD rings */ +#define I915_EXEC_BSD_SHIFT (13) +#define I915_EXEC_BSD_MASK (3 << I915_EXEC_BSD_SHIFT) +/* default ping-pong mode */ +#define I915_EXEC_BSD_DEFAULT (0 << I915_EXEC_BSD_SHIFT) +#define I915_EXEC_BSD_RING1 (1 << I915_EXEC_BSD_SHIFT) +#define I915_EXEC_BSD_RING2 (2 << I915_EXEC_BSD_SHIFT) + +/** Tell the kernel that the batchbuffer is processed by + * the resource streamer. + */ +#define I915_EXEC_RESOURCE_STREAMER (1<<15) + +/* Setting I915_EXEC_FENCE_IN implies that lower_32_bits(rsvd2) represent + * a sync_file fd to wait upon (in a nonblocking manner) prior to executing + * the batch. + * + * Returns -EINVAL if the sync_file fd cannot be found. + */ +#define I915_EXEC_FENCE_IN (1<<16) + +/* Setting I915_EXEC_FENCE_OUT causes the ioctl to return a sync_file fd + * in the upper_32_bits(rsvd2) upon success. Ownership of the fd is given + * to the caller, and it should be close() after use. (The fd is a regular + * file descriptor and will be cleaned up on process termination. It holds + * a reference to the request, but nothing else.) + * + * The sync_file fd can be combined with other sync_file and passed either + * to execbuf using I915_EXEC_FENCE_IN, to atomic KMS ioctls (so that a flip + * will only occur after this request completes), or to other devices. + * + * Using I915_EXEC_FENCE_OUT requires use of + * DRM_IOCTL_I915_GEM_EXECBUFFER2_WR ioctl so that the result is written + * back to userspace. Failure to do so will cause the out-fence to always + * be reported as zero, and the real fence fd to be leaked. + */ +#define I915_EXEC_FENCE_OUT (1<<17) + +/* + * Traditionally the execbuf ioctl has only considered the final element in + * the execobject[] to be the executable batch. Often though, the client + * will known the batch object prior to construction and being able to place + * it into the execobject[] array first can simplify the relocation tracking. + * Setting I915_EXEC_BATCH_FIRST tells execbuf to use element 0 of the + * execobject[] as the * batch instead (the default is to use the last + * element). + */ +#define I915_EXEC_BATCH_FIRST (1<<18) + +/* Setting I915_FENCE_ARRAY implies that num_cliprects and cliprects_ptr + * define an array of i915_gem_exec_fence structures which specify a set of + * dma fences to wait upon or signal. + */ +#define I915_EXEC_FENCE_ARRAY (1<<19) + +#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1)) + +#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) +#define i915_execbuffer2_set_context_id(eb2, context) \ + (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK +#define i915_execbuffer2_get_context_id(eb2) \ + ((eb2).rsvd1 & I915_EXEC_CONTEXT_ID_MASK) + +struct drm_i915_gem_pin { + /** Handle of the buffer to be pinned. */ + __u32 handle; + __u32 pad; + + /** alignment required within the aperture */ + __u64 alignment; + + /** Returned GTT offset of the buffer. */ + __u64 offset; +}; + +struct drm_i915_gem_unpin { + /** Handle of the buffer to be unpinned. */ + __u32 handle; + __u32 pad; +}; + +struct drm_i915_gem_busy { + /** Handle of the buffer to check for busy */ + __u32 handle; + + /** Return busy status + * + * A return of 0 implies that the object is idle (after + * having flushed any pending activity), and a non-zero return that + * the object is still in-flight on the GPU. (The GPU has not yet + * signaled completion for all pending requests that reference the + * object.) An object is guaranteed to become idle eventually (so + * long as no new GPU commands are executed upon it). Due to the + * asynchronous nature of the hardware, an object reported + * as busy may become idle before the ioctl is completed. + * + * Furthermore, if the object is busy, which engine is busy is only + * provided as a guide and only indirectly by reporting its class + * (there may be more than one engine in each class). There are race + * conditions which prevent the report of which engines are busy from + * being always accurate. However, the converse is not true. If the + * object is idle, the result of the ioctl, that all engines are idle, + * is accurate. + * + * The returned dword is split into two fields to indicate both + * the engine classess on which the object is being read, and the + * engine class on which it is currently being written (if any). + * + * The low word (bits 0:15) indicate if the object is being written + * to by any engine (there can only be one, as the GEM implicit + * synchronisation rules force writes to be serialised). Only the + * engine class (offset by 1, I915_ENGINE_CLASS_RENDER is reported as + * 1 not 0 etc) for the last write is reported. + * + * The high word (bits 16:31) are a bitmask of which engines classes + * are currently reading from the object. Multiple engines may be + * reading from the object simultaneously. + * + * The value of each engine class is the same as specified in the + * I915_CONTEXT_SET_ENGINES parameter and via perf, i.e. + * I915_ENGINE_CLASS_RENDER, I915_ENGINE_CLASS_COPY, etc. + * reported as active itself. Some hardware may have parallel + * execution engines, e.g. multiple media engines, which are + * mapped to the same class identifier and so are not separately + * reported for busyness. + * + * Caveat emptor: + * Only the boolean result of this query is reliable; that is whether + * the object is idle or busy. The report of which engines are busy + * should be only used as a heuristic. + */ + __u32 busy; +}; + +/** + * I915_CACHING_NONE + * + * GPU access is not coherent with cpu caches. Default for machines without an + * LLC. + */ +#define I915_CACHING_NONE 0 +/** + * I915_CACHING_CACHED + * + * GPU access is coherent with cpu caches and furthermore the data is cached in + * last-level caches shared between cpu cores and the gpu GT. Default on + * machines with HAS_LLC. + */ +#define I915_CACHING_CACHED 1 +/** + * I915_CACHING_DISPLAY + * + * Special GPU caching mode which is coherent with the scanout engines. + * Transparently falls back to I915_CACHING_NONE on platforms where no special + * cache mode (like write-through or gfdt flushing) is available. The kernel + * automatically sets this mode when using a buffer as a scanout target. + * Userspace can manually set this mode to avoid a costly stall and clflush in + * the hotpath of drawing the first frame. + */ +#define I915_CACHING_DISPLAY 2 + +struct drm_i915_gem_caching { + /** + * Handle of the buffer to set/get the caching level of. */ + __u32 handle; + + /** + * Caching level to apply or return value + * + * bits0-15 are for generic caching control (i.e. the above defined + * values). bits16-31 are reserved for platform-specific variations + * (e.g. l3$ caching on gen7). */ + __u32 caching; +}; + +#define I915_TILING_NONE 0 +#define I915_TILING_X 1 +#define I915_TILING_Y 2 +#define I915_TILING_LAST I915_TILING_Y + +#define I915_BIT_6_SWIZZLE_NONE 0 +#define I915_BIT_6_SWIZZLE_9 1 +#define I915_BIT_6_SWIZZLE_9_10 2 +#define I915_BIT_6_SWIZZLE_9_11 3 +#define I915_BIT_6_SWIZZLE_9_10_11 4 +/* Not seen by userland */ +#define I915_BIT_6_SWIZZLE_UNKNOWN 5 +/* Seen by userland. */ +#define I915_BIT_6_SWIZZLE_9_17 6 +#define I915_BIT_6_SWIZZLE_9_10_17 7 + +struct drm_i915_gem_set_tiling { + /** Handle of the buffer to have its tiling state updated */ + __u32 handle; + + /** + * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + * + * This value is to be set on request, and will be updated by the + * kernel on successful return with the actual chosen tiling layout. + * + * The tiling mode may be demoted to I915_TILING_NONE when the system + * has bit 6 swizzling that can't be managed correctly by GEM. + * + * Buffer contents become undefined when changing tiling_mode. + */ + __u32 tiling_mode; + + /** + * Stride in bytes for the object when in I915_TILING_X or + * I915_TILING_Y. + */ + __u32 stride; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + __u32 swizzle_mode; +}; + +struct drm_i915_gem_get_tiling { + /** Handle of the buffer to get tiling state for. */ + __u32 handle; + + /** + * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + */ + __u32 tiling_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + __u32 swizzle_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping whilst bound. + */ + __u32 phys_swizzle_mode; +}; + +struct drm_i915_gem_get_aperture { + /** Total size of the aperture used by i915_gem_execbuffer, in bytes */ + __u64 aper_size; + + /** + * Available space in the aperture used by i915_gem_execbuffer, in + * bytes + */ + __u64 aper_available_size; +}; + +struct drm_i915_get_pipe_from_crtc_id { + /** ID of CRTC being requested **/ + __u32 crtc_id; + + /** pipe of requested CRTC **/ + __u32 pipe; +}; + +#define I915_MADV_WILLNEED 0 +#define I915_MADV_DONTNEED 1 +#define __I915_MADV_PURGED 2 /* internal state */ + +struct drm_i915_gem_madvise { + /** Handle of the buffer to change the backing store advice */ + __u32 handle; + + /* Advice: either the buffer will be needed again in the near future, + * or wont be and could be discarded under memory pressure. + */ + __u32 madv; + + /** Whether the backing store still exists. */ + __u32 retained; +}; + +/* flags */ +#define I915_OVERLAY_TYPE_MASK 0xff +#define I915_OVERLAY_YUV_PLANAR 0x01 +#define I915_OVERLAY_YUV_PACKED 0x02 +#define I915_OVERLAY_RGB 0x03 + +#define I915_OVERLAY_DEPTH_MASK 0xff00 +#define I915_OVERLAY_RGB24 0x1000 +#define I915_OVERLAY_RGB16 0x2000 +#define I915_OVERLAY_RGB15 0x3000 +#define I915_OVERLAY_YUV422 0x0100 +#define I915_OVERLAY_YUV411 0x0200 +#define I915_OVERLAY_YUV420 0x0300 +#define I915_OVERLAY_YUV410 0x0400 + +#define I915_OVERLAY_SWAP_MASK 0xff0000 +#define I915_OVERLAY_NO_SWAP 0x000000 +#define I915_OVERLAY_UV_SWAP 0x010000 +#define I915_OVERLAY_Y_SWAP 0x020000 +#define I915_OVERLAY_Y_AND_UV_SWAP 0x030000 + +#define I915_OVERLAY_FLAGS_MASK 0xff000000 +#define I915_OVERLAY_ENABLE 0x01000000 + +struct drm_intel_overlay_put_image { + /* various flags and src format description */ + __u32 flags; + /* source picture description */ + __u32 bo_handle; + /* stride values and offsets are in bytes, buffer relative */ + __u16 stride_Y; /* stride for packed formats */ + __u16 stride_UV; + __u32 offset_Y; /* offset for packet formats */ + __u32 offset_U; + __u32 offset_V; + /* in pixels */ + __u16 src_width; + __u16 src_height; + /* to compensate the scaling factors for partially covered surfaces */ + __u16 src_scan_width; + __u16 src_scan_height; + /* output crtc description */ + __u32 crtc_id; + __u16 dst_x; + __u16 dst_y; + __u16 dst_width; + __u16 dst_height; +}; + +/* flags */ +#define I915_OVERLAY_UPDATE_ATTRS (1<<0) +#define I915_OVERLAY_UPDATE_GAMMA (1<<1) +#define I915_OVERLAY_DISABLE_DEST_COLORKEY (1<<2) +struct drm_intel_overlay_attrs { + __u32 flags; + __u32 color_key; + __s32 brightness; + __u32 contrast; + __u32 saturation; + __u32 gamma0; + __u32 gamma1; + __u32 gamma2; + __u32 gamma3; + __u32 gamma4; + __u32 gamma5; +}; + +/* + * Intel sprite handling + * + * Color keying works with a min/mask/max tuple. Both source and destination + * color keying is allowed. + * + * Source keying: + * Sprite pixels within the min & max values, masked against the color channels + * specified in the mask field, will be transparent. All other pixels will + * be displayed on top of the primary plane. For RGB surfaces, only the min + * and mask fields will be used; ranged compares are not allowed. + * + * Destination keying: + * Primary plane pixels that match the min value, masked against the color + * channels specified in the mask field, will be replaced by corresponding + * pixels from the sprite plane. + * + * Note that source & destination keying are exclusive; only one can be + * active on a given plane. + */ + +#define I915_SET_COLORKEY_NONE (1<<0) /* Deprecated. Instead set + * flags==0 to disable colorkeying. + */ +#define I915_SET_COLORKEY_DESTINATION (1<<1) +#define I915_SET_COLORKEY_SOURCE (1<<2) +struct drm_intel_sprite_colorkey { + __u32 plane_id; + __u32 min_value; + __u32 channel_mask; + __u32 max_value; + __u32 flags; +}; + +struct drm_i915_gem_wait { + /** Handle of BO we shall wait on */ + __u32 bo_handle; + __u32 flags; + /** Number of nanoseconds to wait, Returns time remaining. */ + __s64 timeout_ns; +}; + +struct drm_i915_gem_context_create { + __u32 ctx_id; /* output: id of new context*/ + __u32 pad; +}; + +struct drm_i915_gem_context_create_ext { + __u32 ctx_id; /* output: id of new context*/ + __u32 flags; +#define I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS (1u << 0) +#define I915_CONTEXT_CREATE_FLAGS_UNKNOWN \ + (-(I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS << 1)) + __u64 extensions; +}; + +struct drm_i915_gem_context_param { + __u32 ctx_id; + __u32 size; + __u64 param; +#define I915_CONTEXT_PARAM_BAN_PERIOD 0x1 +#define I915_CONTEXT_PARAM_NO_ZEROMAP 0x2 +#define I915_CONTEXT_PARAM_GTT_SIZE 0x3 +#define I915_CONTEXT_PARAM_NO_ERROR_CAPTURE 0x4 +#define I915_CONTEXT_PARAM_BANNABLE 0x5 +#define I915_CONTEXT_PARAM_PRIORITY 0x6 +#define I915_CONTEXT_MAX_USER_PRIORITY 1023 /* inclusive */ +#define I915_CONTEXT_DEFAULT_PRIORITY 0 +#define I915_CONTEXT_MIN_USER_PRIORITY -1023 /* inclusive */ + /* + * When using the following param, value should be a pointer to + * drm_i915_gem_context_param_sseu. + */ +#define I915_CONTEXT_PARAM_SSEU 0x7 + +/* + * Not all clients may want to attempt automatic recover of a context after + * a hang (for example, some clients may only submit very small incremental + * batches relying on known logical state of previous batches which will never + * recover correctly and each attempt will hang), and so would prefer that + * the context is forever banned instead. + * + * If set to false (0), after a reset, subsequent (and in flight) rendering + * from this context is discarded, and the client will need to create a new + * context to use instead. + * + * If set to true (1), the kernel will automatically attempt to recover the + * context by skipping the hanging batch and executing the next batch starting + * from the default context state (discarding the incomplete logical context + * state lost due to the reset). + * + * On creation, all new contexts are marked as recoverable. + */ +#define I915_CONTEXT_PARAM_RECOVERABLE 0x8 +/* Must be kept compact -- no holes and well documented */ + + __u64 value; +}; + +/** + * Context SSEU programming + * + * It may be necessary for either functional or performance reason to configure + * a context to run with a reduced number of SSEU (where SSEU stands for Slice/ + * Sub-slice/EU). + * + * This is done by configuring SSEU configuration using the below + * @struct drm_i915_gem_context_param_sseu for every supported engine which + * userspace intends to use. + * + * Not all GPUs or engines support this functionality in which case an error + * code -ENODEV will be returned. + * + * Also, flexibility of possible SSEU configuration permutations varies between + * GPU generations and software imposed limitations. Requesting such a + * combination will return an error code of -EINVAL. + * + * NOTE: When perf/OA is active the context's SSEU configuration is ignored in + * favour of a single global setting. + */ +struct drm_i915_gem_context_param_sseu { + /* + * Engine class & instance to be configured or queried. + */ + __u16 engine_class; + __u16 engine_instance; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * Mask of slices to enable for the context. Valid values are a subset + * of the bitmask value returned for I915_PARAM_SLICE_MASK. + */ + __u64 slice_mask; + + /* + * Mask of subslices to enable for the context. Valid values are a + * subset of the bitmask value return by I915_PARAM_SUBSLICE_MASK. + */ + __u64 subslice_mask; + + /* + * Minimum/Maximum number of EUs to enable per subslice for the + * context. min_eus_per_subslice must be inferior or equal to + * max_eus_per_subslice. + */ + __u16 min_eus_per_subslice; + __u16 max_eus_per_subslice; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 rsvd; +}; + +struct drm_i915_gem_context_create_ext_setparam { +#define I915_CONTEXT_CREATE_EXT_SETPARAM 0 + struct i915_user_extension base; + struct drm_i915_gem_context_param param; +}; + +struct drm_i915_gem_context_destroy { + __u32 ctx_id; + __u32 pad; +}; + +/* + * DRM_I915_GEM_VM_CREATE - + * + * Create a new virtual memory address space (ppGTT) for use within a context + * on the same file. Extensions can be provided to configure exactly how the + * address space is setup upon creation. + * + * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is + * returned in the outparam @id. + * + * No flags are defined, with all bits reserved and must be zero. + * + * An extension chain maybe provided, starting with @extensions, and terminated + * by the @next_extension being 0. Currently, no extensions are defined. + * + * DRM_I915_GEM_VM_DESTROY - + * + * Destroys a previously created VM id, specified in @id. + * + * No extensions or flags are allowed currently, and so must be zero. + */ +struct drm_i915_gem_vm_control { + __u64 extensions; + __u32 flags; + __u32 vm_id; +}; + +struct drm_i915_reg_read { + /* + * Register offset. + * For 64bit wide registers where the upper 32bits don't immediately + * follow the lower 32bits, the offset of the lower 32bits must + * be specified + */ + __u64 offset; +#define I915_REG_READ_8B_WA (1ul << 0) + + __u64 val; /* Return value */ +}; + +/* Known registers: + * + * Render engine timestamp - 0x2358 + 64bit - gen7+ + * - Note this register returns an invalid value if using the default + * single instruction 8byte read, in order to workaround that pass + * flag I915_REG_READ_8B_WA in offset field. + * + */ + +struct drm_i915_reset_stats { + __u32 ctx_id; + __u32 flags; + + /* All resets since boot/module reload, for all contexts */ + __u32 reset_count; + + /* Number of batches lost when active in GPU, for this context */ + __u32 batch_active; + + /* Number of batches lost pending for execution, for this context */ + __u32 batch_pending; + + __u32 pad; +}; + +struct drm_i915_gem_userptr { + __u64 user_ptr; + __u64 user_size; + __u32 flags; +#define I915_USERPTR_READ_ONLY 0x1 +#define I915_USERPTR_UNSYNCHRONIZED 0x80000000 + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; +}; + +enum drm_i915_oa_format { + I915_OA_FORMAT_A13 = 1, /* HSW only */ + I915_OA_FORMAT_A29, /* HSW only */ + I915_OA_FORMAT_A13_B8_C8, /* HSW only */ + I915_OA_FORMAT_B4_C8, /* HSW only */ + I915_OA_FORMAT_A45_B8_C8, /* HSW only */ + I915_OA_FORMAT_B4_C8_A16, /* HSW only */ + I915_OA_FORMAT_C4_B8, /* HSW+ */ + + /* Gen8+ */ + I915_OA_FORMAT_A12, + I915_OA_FORMAT_A12_B8_C8, + I915_OA_FORMAT_A32u40_A4u32_B8_C8, + + I915_OA_FORMAT_MAX /* non-ABI */ +}; + +enum drm_i915_perf_property_id { + /** + * Open the stream for a specific context handle (as used with + * execbuffer2). A stream opened for a specific context this way + * won't typically require root privileges. + */ + DRM_I915_PERF_PROP_CTX_HANDLE = 1, + + /** + * A value of 1 requests the inclusion of raw OA unit reports as + * part of stream samples. + */ + DRM_I915_PERF_PROP_SAMPLE_OA, + + /** + * The value specifies which set of OA unit metrics should be + * be configured, defining the contents of any OA unit reports. + */ + DRM_I915_PERF_PROP_OA_METRICS_SET, + + /** + * The value specifies the size and layout of OA unit reports. + */ + DRM_I915_PERF_PROP_OA_FORMAT, + + /** + * Specifying this property implicitly requests periodic OA unit + * sampling and (at least on Haswell) the sampling frequency is derived + * from this exponent as follows: + * + * 80ns * 2^(period_exponent + 1) + */ + DRM_I915_PERF_PROP_OA_EXPONENT, + + DRM_I915_PERF_PROP_MAX /* non-ABI */ +}; + +struct drm_i915_perf_open_param { + __u32 flags; +#define I915_PERF_FLAG_FD_CLOEXEC (1<<0) +#define I915_PERF_FLAG_FD_NONBLOCK (1<<1) +#define I915_PERF_FLAG_DISABLED (1<<2) + + /** The number of u64 (id, value) pairs */ + __u32 num_properties; + + /** + * Pointer to array of u64 (id, value) pairs configuring the stream + * to open. + */ + __u64 properties_ptr; +}; + +/** + * Enable data capture for a stream that was either opened in a disabled state + * via I915_PERF_FLAG_DISABLED or was later disabled via + * I915_PERF_IOCTL_DISABLE. + * + * It is intended to be cheaper to disable and enable a stream than it may be + * to close and re-open a stream with the same configuration. + * + * It's undefined whether any pending data for the stream will be lost. + */ +#define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) + +/** + * Disable data capture for a stream. + * + * It is an error to try and read a stream that is disabled. + */ +#define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) + +/** + * Common to all i915 perf records + */ +struct drm_i915_perf_record_header { + __u32 type; + __u16 pad; + __u16 size; +}; + +enum drm_i915_perf_record_type { + + /** + * Samples are the work horse record type whose contents are extensible + * and defined when opening an i915 perf stream based on the given + * properties. + * + * Boolean properties following the naming convention + * DRM_I915_PERF_SAMPLE_xyz_PROP request the inclusion of 'xyz' data in + * every sample. + * + * The order of these sample properties given by userspace has no + * affect on the ordering of data within a sample. The order is + * documented here. + * + * struct { + * struct drm_i915_perf_record_header header; + * + * { u32 oa_report[]; } && DRM_I915_PERF_PROP_SAMPLE_OA + * }; + */ + DRM_I915_PERF_RECORD_SAMPLE = 1, + + /* + * Indicates that one or more OA reports were not written by the + * hardware. This can happen for example if an MI_REPORT_PERF_COUNT + * command collides with periodic sampling - which would be more likely + * at higher sampling frequencies. + */ + DRM_I915_PERF_RECORD_OA_REPORT_LOST = 2, + + /** + * An error occurred that resulted in all pending OA reports being lost. + */ + DRM_I915_PERF_RECORD_OA_BUFFER_LOST = 3, + + DRM_I915_PERF_RECORD_MAX /* non-ABI */ +}; + +/** + * Structure to upload perf dynamic configuration into the kernel. + */ +struct drm_i915_perf_oa_config { + /** String formatted like "%08x-%04x-%04x-%04x-%012x" */ + char uuid[36]; + + __u32 n_mux_regs; + __u32 n_boolean_regs; + __u32 n_flex_regs; + + /* + * These fields are pointers to tuples of u32 values (register address, + * value). For example the expected length of the buffer pointed by + * mux_regs_ptr is (2 * sizeof(u32) * n_mux_regs). + */ + __u64 mux_regs_ptr; + __u64 boolean_regs_ptr; + __u64 flex_regs_ptr; +}; + +struct drm_i915_query_item { + __u64 query_id; +#define DRM_I915_QUERY_TOPOLOGY_INFO 1 +/* Must be kept compact -- no holes and well documented */ + + /* + * When set to zero by userspace, this is filled with the size of the + * data to be written at the data_ptr pointer. The kernel sets this + * value to a negative value to signal an error on a particular query + * item. + */ + __s32 length; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * Data will be written at the location pointed by data_ptr when the + * value of length matches the length of the data to be written by the + * kernel. + */ + __u64 data_ptr; +}; + +struct drm_i915_query { + __u32 num_items; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * This points to an array of num_items drm_i915_query_item structures. + */ + __u64 items_ptr; +}; + +/* + * Data written by the kernel with query DRM_I915_QUERY_TOPOLOGY_INFO : + * + * data: contains the 3 pieces of information : + * + * - the slice mask with one bit per slice telling whether a slice is + * available. The availability of slice X can be queried with the following + * formula : + * + * (data[X / 8] >> (X % 8)) & 1 + * + * - the subslice mask for each slice with one bit per subslice telling + * whether a subslice is available. The availability of subslice Y in slice + * X can be queried with the following formula : + * + * (data[subslice_offset + + * X * subslice_stride + + * Y / 8] >> (Y % 8)) & 1 + * + * - the EU mask for each subslice in each slice with one bit per EU telling + * whether an EU is available. The availability of EU Z in subslice Y in + * slice X can be queried with the following formula : + * + * (data[eu_offset + + * (X * max_subslices + Y) * eu_stride + + * Z / 8] >> (Z % 8)) & 1 + */ +struct drm_i915_query_topology_info { + /* + * Unused for now. Must be cleared to zero. + */ + __u16 flags; + + __u16 max_slices; + __u16 max_subslices; + __u16 max_eus_per_subslice; + + /* + * Offset in data[] at which the subslice masks are stored. + */ + __u16 subslice_offset; + + /* + * Stride at which each of the subslice masks for each slice are + * stored. + */ + __u16 subslice_stride; + + /* + * Offset in data[] at which the EU masks are stored. + */ + __u16 eu_offset; + + /* + * Stride at which each of the EU masks for each subslice are stored. + */ + __u16 eu_stride; + + __u8 data[]; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _I915_DRM_H_ */ diff --git a/libs/common/drm/include/libdrm/mach64_drm.h b/libs/common/drm/include/libdrm/mach64_drm.h new file mode 100644 index 0000000..1f5fd84 --- /dev/null +++ b/libs/common/drm/include/libdrm/mach64_drm.h @@ -0,0 +1,256 @@ +/* mach64_drm.h -- Public header for the mach64 driver -*- linux-c -*- + * Created: Thu Nov 30 20:04:32 2000 by gareth@valinux.com + */ +/* + * Copyright 2000 Gareth Hughes + * Copyright 2002 Frank C. Earl + * Copyright 2002-2003 Leif Delgass + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * Frank C. Earl + * Leif Delgass + */ + +#ifndef __MACH64_DRM_H__ +#define __MACH64_DRM_H__ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mach64_sarea.h) + */ +#ifndef __MACH64_SAREA_DEFINES__ +#define __MACH64_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + * GH: We're going to be pedantic about this. We want the card to do as + * little as possible, so let's avoid having it fetch a whole bunch of + * register values that don't change all that often, if at all. + */ +#define MACH64_UPLOAD_DST_OFF_PITCH 0x0001 +#define MACH64_UPLOAD_Z_OFF_PITCH 0x0002 +#define MACH64_UPLOAD_Z_ALPHA_CNTL 0x0004 +#define MACH64_UPLOAD_SCALE_3D_CNTL 0x0008 +#define MACH64_UPLOAD_DP_FOG_CLR 0x0010 +#define MACH64_UPLOAD_DP_WRITE_MASK 0x0020 +#define MACH64_UPLOAD_DP_PIX_WIDTH 0x0040 +#define MACH64_UPLOAD_SETUP_CNTL 0x0080 +#define MACH64_UPLOAD_MISC 0x0100 +#define MACH64_UPLOAD_TEXTURE 0x0200 +#define MACH64_UPLOAD_TEX0IMAGE 0x0400 +#define MACH64_UPLOAD_TEX1IMAGE 0x0800 +#define MACH64_UPLOAD_CLIPRECTS 0x1000 /* handled client-side */ +#define MACH64_UPLOAD_CONTEXT 0x00ff +#define MACH64_UPLOAD_ALL 0x1fff + +/* DMA buffer size + */ +#define MACH64_BUFFER_SIZE 16384 + +/* Max number of swaps allowed on the ring + * before the client must wait + */ +#define MACH64_MAX_QUEUED_FRAMES 3U + +/* Byte offsets for host blit buffer data + */ +#define MACH64_HOSTDATA_BLIT_OFFSET 104 + +/* Keep these small for testing. + */ +#define MACH64_NR_SAREA_CLIPRECTS 8 + +#define MACH64_CARD_HEAP 0 +#define MACH64_AGP_HEAP 1 +#define MACH64_NR_TEX_HEAPS 2 +#define MACH64_NR_TEX_REGIONS 64 +#define MACH64_LOG_TEX_GRANULARITY 16 + +#define MACH64_TEX_MAXLEVELS 1 + +#define MACH64_NR_CONTEXT_REGS 15 +#define MACH64_NR_TEXTURE_REGS 4 + +#endif /* __MACH64_SAREA_DEFINES__ */ + +typedef struct { + unsigned int dst_off_pitch; + + unsigned int z_off_pitch; + unsigned int z_cntl; + unsigned int alpha_tst_cntl; + + unsigned int scale_3d_cntl; + + unsigned int sc_left_right; + unsigned int sc_top_bottom; + + unsigned int dp_fog_clr; + unsigned int dp_write_mask; + unsigned int dp_pix_width; + unsigned int dp_mix; + unsigned int dp_src; + + unsigned int clr_cmp_cntl; + unsigned int gui_traj_cntl; + + unsigned int setup_cntl; + + unsigned int tex_size_pitch; + unsigned int tex_cntl; + unsigned int secondary_tex_off; + unsigned int tex_offset; +} drm_mach64_context_regs_t; + +typedef struct drm_mach64_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex dma buffer. + */ + drm_mach64_context_regs_t context_state; + unsigned int dirty; + unsigned int vertsize; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[MACH64_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int frames_queued; + + /* Texture memory LRU. + */ + struct drm_tex_region tex_list[MACH64_NR_TEX_HEAPS][MACH64_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[MACH64_NR_TEX_HEAPS]; + int ctx_owner; +} drm_mach64_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mach64_common.h) + */ + +/* Mach64 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ + +#define DRM_MACH64_INIT 0x00 +#define DRM_MACH64_IDLE 0x01 +#define DRM_MACH64_RESET 0x02 +#define DRM_MACH64_SWAP 0x03 +#define DRM_MACH64_CLEAR 0x04 +#define DRM_MACH64_VERTEX 0x05 +#define DRM_MACH64_BLIT 0x06 +#define DRM_MACH64_FLUSH 0x07 +#define DRM_MACH64_GETPARAM 0x08 + +#define DRM_IOCTL_MACH64_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_INIT, drm_mach64_init_t) +#define DRM_IOCTL_MACH64_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_IDLE ) +#define DRM_IOCTL_MACH64_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_RESET ) +#define DRM_IOCTL_MACH64_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_SWAP ) +#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_CLEAR, drm_mach64_clear_t) +#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_VERTEX, drm_mach64_vertex_t) +#define DRM_IOCTL_MACH64_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_BLIT, drm_mach64_blit_t) +#define DRM_IOCTL_MACH64_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_FLUSH ) +#define DRM_IOCTL_MACH64_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_MACH64_GETPARAM, drm_mach64_getparam_t) + +/* Buffer flags for clears + */ +#define MACH64_FRONT 0x1 +#define MACH64_BACK 0x2 +#define MACH64_DEPTH 0x4 + +/* Primitive types for vertex buffers + */ +#define MACH64_PRIM_POINTS 0x00000000 +#define MACH64_PRIM_LINES 0x00000001 +#define MACH64_PRIM_LINE_LOOP 0x00000002 +#define MACH64_PRIM_LINE_STRIP 0x00000003 +#define MACH64_PRIM_TRIANGLES 0x00000004 +#define MACH64_PRIM_TRIANGLE_STRIP 0x00000005 +#define MACH64_PRIM_TRIANGLE_FAN 0x00000006 +#define MACH64_PRIM_QUADS 0x00000007 +#define MACH64_PRIM_QUAD_STRIP 0x00000008 +#define MACH64_PRIM_POLYGON 0x00000009 + +typedef enum _drm_mach64_dma_mode_t { + MACH64_MODE_DMA_ASYNC, + MACH64_MODE_DMA_SYNC, + MACH64_MODE_MMIO +} drm_mach64_dma_mode_t; + +typedef struct drm_mach64_init { + enum { + DRM_MACH64_INIT_DMA = 0x01, + DRM_MACH64_CLEANUP_DMA = 0x02 + } func; + + unsigned long sarea_priv_offset; + int is_pci; + drm_mach64_dma_mode_t dma_mode; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; +} drm_mach64_init_t; + +typedef struct drm_mach64_clear { + unsigned int flags; + int x, y, w, h; + unsigned int clear_color; + unsigned int clear_depth; +} drm_mach64_clear_t; + +typedef struct drm_mach64_vertex { + int prim; + void *buf; /* Address of vertex buffer */ + unsigned long used; /* Number of bytes in buffer */ + int discard; /* Client finished with buffer? */ +} drm_mach64_vertex_t; + +typedef struct drm_mach64_blit { + void *buf; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_mach64_blit_t; + +typedef struct drm_mach64_getparam { + enum { + MACH64_PARAM_FRAMES_QUEUED = 0x01, + MACH64_PARAM_IRQ_NR = 0x02 + } param; + void *value; +} drm_mach64_getparam_t; + +#endif diff --git a/libs/common/drm/include/libdrm/mga_drm.h b/libs/common/drm/include/libdrm/mga_drm.h new file mode 100644 index 0000000..7930011 --- /dev/null +++ b/libs/common/drm/include/libdrm/mga_drm.h @@ -0,0 +1,427 @@ +/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jeff Hartmann + * Keith Whitwell + * + * Rewritten by: + * Gareth Hughes + */ + +#ifndef __MGA_DRM_H__ +#define __MGA_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mga_sarea.h) + */ + +#ifndef __MGA_SAREA_DEFINES__ +#define __MGA_SAREA_DEFINES__ + +/* WARP pipe flags + */ +#define MGA_F 0x1 /* fog */ +#define MGA_A 0x2 /* alpha */ +#define MGA_S 0x4 /* specular */ +#define MGA_T2 0x8 /* multitexture */ + +#define MGA_WARP_TGZ 0 +#define MGA_WARP_TGZF (MGA_F) +#define MGA_WARP_TGZA (MGA_A) +#define MGA_WARP_TGZAF (MGA_F|MGA_A) +#define MGA_WARP_TGZS (MGA_S) +#define MGA_WARP_TGZSF (MGA_S|MGA_F) +#define MGA_WARP_TGZSA (MGA_S|MGA_A) +#define MGA_WARP_TGZSAF (MGA_S|MGA_F|MGA_A) +#define MGA_WARP_T2GZ (MGA_T2) +#define MGA_WARP_T2GZF (MGA_T2|MGA_F) +#define MGA_WARP_T2GZA (MGA_T2|MGA_A) +#define MGA_WARP_T2GZAF (MGA_T2|MGA_A|MGA_F) +#define MGA_WARP_T2GZS (MGA_T2|MGA_S) +#define MGA_WARP_T2GZSF (MGA_T2|MGA_S|MGA_F) +#define MGA_WARP_T2GZSA (MGA_T2|MGA_S|MGA_A) +#define MGA_WARP_T2GZSAF (MGA_T2|MGA_S|MGA_F|MGA_A) + +#define MGA_MAX_G200_PIPES 8 /* no multitex */ +#define MGA_MAX_G400_PIPES 16 +#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES +#define MGA_WARP_UCODE_SIZE 32768 /* in bytes */ + +#define MGA_CARD_TYPE_G200 1 +#define MGA_CARD_TYPE_G400 2 +#define MGA_CARD_TYPE_G450 3 /* not currently used */ +#define MGA_CARD_TYPE_G550 4 + +#define MGA_FRONT 0x1 +#define MGA_BACK 0x2 +#define MGA_DEPTH 0x4 + +/* What needs to be changed for the current vertex dma buffer? + */ +#define MGA_UPLOAD_CONTEXT 0x1 +#define MGA_UPLOAD_TEX0 0x2 +#define MGA_UPLOAD_TEX1 0x4 +#define MGA_UPLOAD_PIPE 0x8 +#define MGA_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */ +#define MGA_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */ +#define MGA_UPLOAD_2D 0x40 +#define MGA_WAIT_AGE 0x80 /* handled client-side */ +#define MGA_UPLOAD_CLIPRECTS 0x100 /* handled client-side */ +#if 0 +#define MGA_DMA_FLUSH 0x200 /* set when someone gets the lock + quiescent */ +#endif + +/* 32 buffers of 64k each, total 2 meg. + */ +#define MGA_BUFFER_SIZE (1 << 16) +#define MGA_NUM_BUFFERS 128 + +/* Keep these small for testing. + */ +#define MGA_NR_SAREA_CLIPRECTS 8 + +/* 2 heaps (1 for card, 1 for agp), each divided into up to 128 + * regions, subject to a minimum region size of (1<<16) == 64k. + * + * Clients may subdivide regions internally, but when sharing between + * clients, the region size is the minimum granularity. + */ + +#define MGA_CARD_HEAP 0 +#define MGA_AGP_HEAP 1 +#define MGA_NR_TEX_HEAPS 2 +#define MGA_NR_TEX_REGIONS 16 +#define MGA_LOG_MIN_TEX_REGION_SIZE 16 + +#define DRM_MGA_IDLE_RETRY 2048 + +#endif /* __MGA_SAREA_DEFINES__ */ + +/* Setup registers for 3D context + */ +typedef struct { + unsigned int dstorg; + unsigned int maccess; + unsigned int plnwt; + unsigned int dwgctl; + unsigned int alphactrl; + unsigned int fogcolor; + unsigned int wflag; + unsigned int tdualstage0; + unsigned int tdualstage1; + unsigned int fcol; + unsigned int stencil; + unsigned int stencilctl; +} drm_mga_context_regs_t; + +/* Setup registers for 2D, X server + */ +typedef struct { + unsigned int pitch; +} drm_mga_server_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int texctl; + unsigned int texctl2; + unsigned int texfilter; + unsigned int texbordercol; + unsigned int texorg; + unsigned int texwidth; + unsigned int texheight; + unsigned int texorg1; + unsigned int texorg2; + unsigned int texorg3; + unsigned int texorg4; +} drm_mga_texture_regs_t; + +/* General aging mechanism + */ +typedef struct { + unsigned int head; /* Position of head pointer */ + unsigned int wrap; /* Primary DMA wrap count */ +} drm_mga_age_t; + +typedef struct _drm_mga_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex dma buffer. + */ + drm_mga_context_regs_t context_state; + drm_mga_server_regs_t server_state; + drm_mga_texture_regs_t tex_state[2]; + unsigned int warp_pipe; + unsigned int dirty; + unsigned int vertsize; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[MGA_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Information about the most recently used 3d drawable. The + * client fills in the req_* fields, the server fills in the + * exported_ fields and puts the cliprects into boxes, above. + * + * The client clears the exported_drawable field before + * clobbering the boxes data. + */ + unsigned int req_drawable; /* the X drawable id */ + unsigned int req_draw_buffer; /* MGA_FRONT or MGA_BACK */ + + unsigned int exported_drawable; + unsigned int exported_index; + unsigned int exported_stamp; + unsigned int exported_buffers; + unsigned int exported_nfront; + unsigned int exported_nback; + int exported_back_x, exported_front_x, exported_w; + int exported_back_y, exported_front_y, exported_h; + struct drm_clip_rect exported_boxes[MGA_NR_SAREA_CLIPRECTS]; + + /* Counters for aging textures and for client-side throttling. + */ + unsigned int status[4]; + unsigned int last_wrap; + + drm_mga_age_t last_frame; + unsigned int last_enqueue; /* last time a buffer was enqueued */ + unsigned int last_dispatch; /* age of the most recently dispatched buffer */ + unsigned int last_quiescent; /* */ + + /* LRU lists for texture memory in agp space and on the card. + */ + struct drm_tex_region texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS + 1]; + unsigned int texAge[MGA_NR_TEX_HEAPS]; + + /* Mechanism to validate card state. + */ + int ctxOwner; +} drm_mga_sarea_t; + +/* MGA specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_MGA_INIT 0x00 +#define DRM_MGA_FLUSH 0x01 +#define DRM_MGA_RESET 0x02 +#define DRM_MGA_SWAP 0x03 +#define DRM_MGA_CLEAR 0x04 +#define DRM_MGA_VERTEX 0x05 +#define DRM_MGA_INDICES 0x06 +#define DRM_MGA_ILOAD 0x07 +#define DRM_MGA_BLIT 0x08 +#define DRM_MGA_GETPARAM 0x09 + +/* 3.2: + * ioctls for operating on fences. + */ +#define DRM_MGA_SET_FENCE 0x0a +#define DRM_MGA_WAIT_FENCE 0x0b +#define DRM_MGA_DMA_BOOTSTRAP 0x0c + +#define DRM_IOCTL_MGA_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_INIT, drm_mga_init_t) +#define DRM_IOCTL_MGA_FLUSH DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_FLUSH, struct drm_lock) +#define DRM_IOCTL_MGA_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MGA_RESET) +#define DRM_IOCTL_MGA_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MGA_SWAP) +#define DRM_IOCTL_MGA_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_CLEAR, drm_mga_clear_t) +#define DRM_IOCTL_MGA_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_VERTEX, drm_mga_vertex_t) +#define DRM_IOCTL_MGA_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_INDICES, drm_mga_indices_t) +#define DRM_IOCTL_MGA_ILOAD DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_ILOAD, drm_mga_iload_t) +#define DRM_IOCTL_MGA_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_BLIT, drm_mga_blit_t) +#define DRM_IOCTL_MGA_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_GETPARAM, drm_mga_getparam_t) +#define DRM_IOCTL_MGA_SET_FENCE DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_SET_FENCE, __u32) +#define DRM_IOCTL_MGA_WAIT_FENCE DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_WAIT_FENCE, __u32) +#define DRM_IOCTL_MGA_DMA_BOOTSTRAP DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_DMA_BOOTSTRAP, drm_mga_dma_bootstrap_t) + +typedef struct _drm_mga_warp_index { + int installed; + unsigned long phys_addr; + int size; +} drm_mga_warp_index_t; + +typedef struct drm_mga_init { + enum { + MGA_INIT_DMA = 0x01, + MGA_CLEANUP_DMA = 0x02 + } func; + + unsigned long sarea_priv_offset; + + int chipset; + int sgram; + + unsigned int maccess; + + unsigned int fb_cpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + + unsigned int depth_cpp; + unsigned int depth_offset, depth_pitch; + + unsigned int texture_offset[MGA_NR_TEX_HEAPS]; + unsigned int texture_size[MGA_NR_TEX_HEAPS]; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long status_offset; + unsigned long warp_offset; + unsigned long primary_offset; + unsigned long buffers_offset; +} drm_mga_init_t; + +typedef struct drm_mga_dma_bootstrap { + /** + * \name AGP texture region + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, these fields will + * be filled in with the actual AGP texture settings. + * + * \warning + * If these fields are non-zero, but dma_mga_dma_bootstrap::agp_mode + * is zero, it means that PCI memory (most likely through the use of + * an IOMMU) is being used for "AGP" textures. + */ + /*@{ */ + unsigned long texture_handle; /**< Handle used to map AGP textures. */ + __u32 texture_size; /**< Size of the AGP texture region. */ + /*@} */ + + /** + * Requested size of the primary DMA region. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual AGP mode. If AGP was not available + */ + __u32 primary_size; + + /** + * Requested number of secondary DMA buffers. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual number of secondary DMA buffers + * allocated. Particularly when PCI DMA is used, this may be + * (subtantially) less than the number requested. + */ + __u32 secondary_bin_count; + + /** + * Requested size of each secondary DMA buffer. + * + * While the kernel \b is free to reduce + * dma_mga_dma_bootstrap::secondary_bin_count, it is \b not allowed + * to reduce dma_mga_dma_bootstrap::secondary_bin_size. + */ + __u32 secondary_bin_size; + + /** + * Bit-wise mask of AGPSTAT2_* values. Currently only \c AGPSTAT2_1X, + * \c AGPSTAT2_2X, and \c AGPSTAT2_4X are supported. If this value is + * zero, it means that PCI DMA should be used, even if AGP is + * possible. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual AGP mode. If AGP was not available + * (i.e., PCI DMA was used), this value will be zero. + */ + __u32 agp_mode; + + /** + * Desired AGP GART size, measured in megabytes. + */ + __u8 agp_size; +} drm_mga_dma_bootstrap_t; + +typedef struct drm_mga_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; +} drm_mga_clear_t; + +typedef struct drm_mga_vertex { + int idx; /* buffer to queue */ + int used; /* bytes in use */ + int discard; /* client finished with buffer? */ +} drm_mga_vertex_t; + +typedef struct drm_mga_indices { + int idx; /* buffer to queue */ + unsigned int start; + unsigned int end; + int discard; /* client finished with buffer? */ +} drm_mga_indices_t; + +typedef struct drm_mga_iload { + int idx; + unsigned int dstorg; + unsigned int length; +} drm_mga_iload_t; + +typedef struct _drm_mga_blit { + unsigned int planemask; + unsigned int srcorg; + unsigned int dstorg; + int src_pitch, dst_pitch; + int delta_sx, delta_sy; + int delta_dx, delta_dy; + int height, ydir; /* flip image vertically */ + int source_pitch, dest_pitch; +} drm_mga_blit_t; + +/* 3.1: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define MGA_PARAM_IRQ_NR 1 + +/* 3.2: Query the actual card type. The DDX only distinguishes between + * G200 chips and non-G200 chips, which it calls G400. It turns out that + * there are some very sublte differences between the G4x0 chips and the G550 + * chips. Using this parameter query, a client-side driver can detect the + * difference between a G4x0 and a G550. + */ +#define MGA_PARAM_CARD_TYPE 2 + +typedef struct drm_mga_getparam { + int param; + void *value; +} drm_mga_getparam_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/nouveau_drm.h b/libs/common/drm/include/libdrm/nouveau_drm.h new file mode 100644 index 0000000..d42105c --- /dev/null +++ b/libs/common/drm/include/libdrm/nouveau_drm.h @@ -0,0 +1,221 @@ +/* + * Copyright 2005 Stephane Marchesin. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NOUVEAU_DRM_H__ +#define __NOUVEAU_DRM_H__ + +#define NOUVEAU_DRM_HEADER_PATCHLEVEL 16 + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +struct drm_nouveau_channel_alloc { + uint32_t fb_ctxdma_handle; + uint32_t tt_ctxdma_handle; + + int channel; + uint32_t pushbuf_domains; + + /* Notifier memory */ + uint32_t notifier_handle; + + /* DRM-enforced subchannel assignments */ + struct { + uint32_t handle; + uint32_t grclass; + } subchan[8]; + uint32_t nr_subchan; +}; + +struct drm_nouveau_channel_free { + int channel; +}; + +struct drm_nouveau_grobj_alloc { + int channel; + uint32_t handle; + int class; +}; + +struct drm_nouveau_notifierobj_alloc { + uint32_t channel; + uint32_t handle; + uint32_t size; + uint32_t offset; +}; + +struct drm_nouveau_gpuobj_free { + int channel; + uint32_t handle; +}; + +/* FIXME : maybe unify {GET,SET}PARAMs */ +#define NOUVEAU_GETPARAM_PCI_VENDOR 3 +#define NOUVEAU_GETPARAM_PCI_DEVICE 4 +#define NOUVEAU_GETPARAM_BUS_TYPE 5 +#define NOUVEAU_GETPARAM_FB_PHYSICAL 6 +#define NOUVEAU_GETPARAM_AGP_PHYSICAL 7 +#define NOUVEAU_GETPARAM_FB_SIZE 8 +#define NOUVEAU_GETPARAM_AGP_SIZE 9 +#define NOUVEAU_GETPARAM_PCI_PHYSICAL 10 +#define NOUVEAU_GETPARAM_CHIPSET_ID 11 +#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12 +#define NOUVEAU_GETPARAM_GRAPH_UNITS 13 +#define NOUVEAU_GETPARAM_PTIMER_TIME 14 +#define NOUVEAU_GETPARAM_HAS_BO_USAGE 15 +#define NOUVEAU_GETPARAM_HAS_PAGEFLIP 16 +struct drm_nouveau_getparam { + uint64_t param; + uint64_t value; +}; + +struct drm_nouveau_setparam { + uint64_t param; + uint64_t value; +}; + +#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0) +#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1) +#define NOUVEAU_GEM_DOMAIN_GART (1 << 2) +#define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3) +#define NOUVEAU_GEM_DOMAIN_COHERENT (1 << 4) + +#define NOUVEAU_GEM_TILE_COMP 0x00030000 /* nv50-only */ +#define NOUVEAU_GEM_TILE_LAYOUT_MASK 0x0000ff00 +#define NOUVEAU_GEM_TILE_16BPP 0x00000001 +#define NOUVEAU_GEM_TILE_32BPP 0x00000002 +#define NOUVEAU_GEM_TILE_ZETA 0x00000004 +#define NOUVEAU_GEM_TILE_NONCONTIG 0x00000008 + +struct drm_nouveau_gem_info { + __u32 handle; + __u32 domain; + __u64 size; + __u64 offset; + __u64 map_handle; + __u32 tile_mode; + __u32 tile_flags; +}; + +struct drm_nouveau_gem_new { + struct drm_nouveau_gem_info info; + __u32 channel_hint; + __u32 align; +}; + +#define NOUVEAU_GEM_MAX_BUFFERS 1024 +struct drm_nouveau_gem_pushbuf_bo_presumed { + __u32 valid; + __u32 domain; + __u64 offset; +}; + +struct drm_nouveau_gem_pushbuf_bo { + __u64 user_priv; + __u32 handle; + __u32 read_domains; + __u32 write_domains; + __u32 valid_domains; + struct drm_nouveau_gem_pushbuf_bo_presumed presumed; +}; + +#define NOUVEAU_GEM_RELOC_LOW (1 << 0) +#define NOUVEAU_GEM_RELOC_HIGH (1 << 1) +#define NOUVEAU_GEM_RELOC_OR (1 << 2) +#define NOUVEAU_GEM_MAX_RELOCS 1024 +struct drm_nouveau_gem_pushbuf_reloc { + __u32 reloc_bo_index; + __u32 reloc_bo_offset; + __u32 bo_index; + __u32 flags; + __u32 data; + __u32 vor; + __u32 tor; +}; + +#define NOUVEAU_GEM_MAX_PUSH 512 +struct drm_nouveau_gem_pushbuf_push { + __u32 bo_index; + __u32 pad; + __u64 offset; + __u64 length; +}; + +struct drm_nouveau_gem_pushbuf { + __u32 channel; + __u32 nr_buffers; + __u64 buffers; + __u32 nr_relocs; + __u32 nr_push; + __u64 relocs; + __u64 push; + __u32 suffix0; + __u32 suffix1; + __u64 vram_available; + __u64 gart_available; +}; + +#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001 +#define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002 +#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004 +struct drm_nouveau_gem_cpu_prep { + __u32 handle; + __u32 flags; +}; + +struct drm_nouveau_gem_cpu_fini { + __u32 handle; +}; + +enum nouveau_bus_type { + NV_AGP = 0, + NV_PCI = 1, + NV_PCIE = 2, +}; + +struct drm_nouveau_sarea { +}; + +#define DRM_NOUVEAU_GETPARAM 0x00 +#define DRM_NOUVEAU_SETPARAM 0x01 +#define DRM_NOUVEAU_CHANNEL_ALLOC 0x02 +#define DRM_NOUVEAU_CHANNEL_FREE 0x03 +#define DRM_NOUVEAU_GROBJ_ALLOC 0x04 +#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x05 +#define DRM_NOUVEAU_GPUOBJ_FREE 0x06 +#define DRM_NOUVEAU_NVIF 0x07 +#define DRM_NOUVEAU_GEM_NEW 0x40 +#define DRM_NOUVEAU_GEM_PUSHBUF 0x41 +#define DRM_NOUVEAU_GEM_CPU_PREP 0x42 +#define DRM_NOUVEAU_GEM_CPU_FINI 0x43 +#define DRM_NOUVEAU_GEM_INFO 0x44 + +#if defined(__cplusplus) +} +#endif + +#endif /* __NOUVEAU_DRM_H__ */ diff --git a/libs/common/drm/include/libdrm/qxl_drm.h b/libs/common/drm/include/libdrm/qxl_drm.h new file mode 100644 index 0000000..880999d --- /dev/null +++ b/libs/common/drm/include/libdrm/qxl_drm.h @@ -0,0 +1,158 @@ +/* + * Copyright 2013 Red Hat + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef QXL_DRM_H +#define QXL_DRM_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + * + * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel + * compatibility Keep fields aligned to their size + */ + +#define QXL_GEM_DOMAIN_CPU 0 +#define QXL_GEM_DOMAIN_VRAM 1 +#define QXL_GEM_DOMAIN_SURFACE 2 + +#define DRM_QXL_ALLOC 0x00 +#define DRM_QXL_MAP 0x01 +#define DRM_QXL_EXECBUFFER 0x02 +#define DRM_QXL_UPDATE_AREA 0x03 +#define DRM_QXL_GETPARAM 0x04 +#define DRM_QXL_CLIENTCAP 0x05 + +#define DRM_QXL_ALLOC_SURF 0x06 + +struct drm_qxl_alloc { + __u32 size; + __u32 handle; /* 0 is an invalid handle */ +}; + +struct drm_qxl_map { + __u64 offset; /* use for mmap system call */ + __u32 handle; + __u32 pad; +}; + +/* + * dest is the bo we are writing the relocation into + * src is bo we are relocating. + * *(dest_handle.base_addr + dest_offset) = physical_address(src_handle.addr + + * src_offset) + */ +#define QXL_RELOC_TYPE_BO 1 +#define QXL_RELOC_TYPE_SURF 2 + +struct drm_qxl_reloc { + __u64 src_offset; /* offset into src_handle or src buffer */ + __u64 dst_offset; /* offset in dest handle */ + __u32 src_handle; /* dest handle to compute address from */ + __u32 dst_handle; /* 0 if to command buffer */ + __u32 reloc_type; + __u32 pad; +}; + +struct drm_qxl_command { + __u64 command; /* void* */ + __u64 relocs; /* struct drm_qxl_reloc* */ + __u32 type; + __u32 command_size; + __u32 relocs_num; + __u32 pad; +}; + +struct drm_qxl_execbuffer { + __u32 flags; /* for future use */ + __u32 commands_num; + __u64 commands; /* struct drm_qxl_command* */ +}; + +struct drm_qxl_update_area { + __u32 handle; + __u32 top; + __u32 left; + __u32 bottom; + __u32 right; + __u32 pad; +}; + +#define QXL_PARAM_NUM_SURFACES 1 /* rom->n_surfaces */ +#define QXL_PARAM_MAX_RELOCS 2 +struct drm_qxl_getparam { + __u64 param; + __u64 value; +}; + +/* these are one bit values */ +struct drm_qxl_clientcap { + __u32 index; + __u32 pad; +}; + +struct drm_qxl_alloc_surf { + __u32 format; + __u32 width; + __u32 height; + __s32 stride; + __u32 handle; + __u32 pad; +}; + +#define DRM_IOCTL_QXL_ALLOC \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC, struct drm_qxl_alloc) + +#define DRM_IOCTL_QXL_MAP \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_MAP, struct drm_qxl_map) + +#define DRM_IOCTL_QXL_EXECBUFFER \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_EXECBUFFER,\ + struct drm_qxl_execbuffer) + +#define DRM_IOCTL_QXL_UPDATE_AREA \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_UPDATE_AREA,\ + struct drm_qxl_update_area) + +#define DRM_IOCTL_QXL_GETPARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_GETPARAM,\ + struct drm_qxl_getparam) + +#define DRM_IOCTL_QXL_CLIENTCAP \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_CLIENTCAP,\ + struct drm_qxl_clientcap) + +#define DRM_IOCTL_QXL_ALLOC_SURF \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC_SURF,\ + struct drm_qxl_alloc_surf) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/r128_drm.h b/libs/common/drm/include/libdrm/r128_drm.h new file mode 100644 index 0000000..bf431a0 --- /dev/null +++ b/libs/common/drm/include/libdrm/r128_drm.h @@ -0,0 +1,336 @@ +/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + */ +/* + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * Kevin E. Martin + */ + +#ifndef __R128_DRM_H__ +#define __R128_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (r128_sarea.h) + */ +#ifndef __R128_SAREA_DEFINES__ +#define __R128_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + */ +#define R128_UPLOAD_CONTEXT 0x001 +#define R128_UPLOAD_SETUP 0x002 +#define R128_UPLOAD_TEX0 0x004 +#define R128_UPLOAD_TEX1 0x008 +#define R128_UPLOAD_TEX0IMAGES 0x010 +#define R128_UPLOAD_TEX1IMAGES 0x020 +#define R128_UPLOAD_CORE 0x040 +#define R128_UPLOAD_MASKS 0x080 +#define R128_UPLOAD_WINDOW 0x100 +#define R128_UPLOAD_CLIPRECTS 0x200 /* handled client-side */ +#define R128_REQUIRE_QUIESCENCE 0x400 +#define R128_UPLOAD_ALL 0x7ff + +#define R128_FRONT 0x1 +#define R128_BACK 0x2 +#define R128_DEPTH 0x4 + +/* Primitive types + */ +#define R128_POINTS 0x1 +#define R128_LINES 0x2 +#define R128_LINE_STRIP 0x3 +#define R128_TRIANGLES 0x4 +#define R128_TRIANGLE_FAN 0x5 +#define R128_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define R128_BUFFER_SIZE 16384 + +/* Byte offsets for indirect buffer data + */ +#define R128_INDEX_PRIM_OFFSET 20 +#define R128_HOSTDATA_BLIT_OFFSET 32 + +/* Keep these small for testing. + */ +#define R128_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/AGP). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define R128_LOCAL_TEX_HEAP 0 +#define R128_AGP_TEX_HEAP 1 +#define R128_NR_TEX_HEAPS 2 +#define R128_NR_TEX_REGIONS 64 +#define R128_LOG_TEX_GRANULARITY 16 + +#define R128_NR_CONTEXT_REGS 12 + +#define R128_MAX_TEXTURE_LEVELS 11 +#define R128_MAX_TEXTURE_UNITS 2 + +#endif /* __R128_SAREA_DEFINES__ */ + +typedef struct { + /* Context state - can be written in one large chunk */ + unsigned int dst_pitch_offset_c; + unsigned int dp_gui_master_cntl_c; + unsigned int sc_top_left_c; + unsigned int sc_bottom_right_c; + unsigned int z_offset_c; + unsigned int z_pitch_c; + unsigned int z_sten_cntl_c; + unsigned int tex_cntl_c; + unsigned int misc_3d_state_cntl_reg; + unsigned int texture_clr_cmp_clr_c; + unsigned int texture_clr_cmp_msk_c; + unsigned int fog_color_c; + + /* Texture state */ + unsigned int tex_size_pitch_c; + unsigned int constant_color_c; + + /* Setup state */ + unsigned int pm4_vc_fpu_setup; + unsigned int setup_cntl; + + /* Mask state */ + unsigned int dp_write_mask; + unsigned int sten_ref_mask_c; + unsigned int plane_3d_mask_c; + + /* Window state */ + unsigned int window_xy_offset; + + /* Core state */ + unsigned int scale_3d_cntl; +} drm_r128_context_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int tex_cntl; + unsigned int tex_combine_cntl; + unsigned int tex_size_pitch; + unsigned int tex_offset[R128_MAX_TEXTURE_LEVELS]; + unsigned int tex_border_color; +} drm_r128_texture_regs_t; + +typedef struct drm_r128_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex buffer. + */ + drm_r128_context_regs_t context_state; + drm_r128_texture_regs_t tex_state[R128_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[R128_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + + struct drm_tex_region tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS + 1]; + unsigned int tex_age[R128_NR_TEX_HEAPS]; + int ctx_owner; + int pfAllowPageFlip; /* number of 3d windows (0,1,2 or more) */ + int pfCurrentPage; /* which buffer is being displayed? */ +} drm_r128_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmR128.h) + */ + +/* Rage 128 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_R128_INIT 0x00 +#define DRM_R128_CCE_START 0x01 +#define DRM_R128_CCE_STOP 0x02 +#define DRM_R128_CCE_RESET 0x03 +#define DRM_R128_CCE_IDLE 0x04 +/* 0x05 not used */ +#define DRM_R128_RESET 0x06 +#define DRM_R128_SWAP 0x07 +#define DRM_R128_CLEAR 0x08 +#define DRM_R128_VERTEX 0x09 +#define DRM_R128_INDICES 0x0a +#define DRM_R128_BLIT 0x0b +#define DRM_R128_DEPTH 0x0c +#define DRM_R128_STIPPLE 0x0d +/* 0x0e not used */ +#define DRM_R128_INDIRECT 0x0f +#define DRM_R128_FULLSCREEN 0x10 +#define DRM_R128_CLEAR2 0x11 +#define DRM_R128_GETPARAM 0x12 +#define DRM_R128_FLIP 0x13 + +#define DRM_IOCTL_R128_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_R128_INIT, drm_r128_init_t) +#define DRM_IOCTL_R128_CCE_START DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_START) +#define DRM_IOCTL_R128_CCE_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CCE_STOP, drm_r128_cce_stop_t) +#define DRM_IOCTL_R128_CCE_RESET DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_RESET) +#define DRM_IOCTL_R128_CCE_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_IDLE) +/* 0x05 not used */ +#define DRM_IOCTL_R128_RESET DRM_IO( DRM_COMMAND_BASE + DRM_R128_RESET) +#define DRM_IOCTL_R128_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_R128_SWAP) +#define DRM_IOCTL_R128_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CLEAR, drm_r128_clear_t) +#define DRM_IOCTL_R128_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_R128_VERTEX, drm_r128_vertex_t) +#define DRM_IOCTL_R128_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_R128_INDICES, drm_r128_indices_t) +#define DRM_IOCTL_R128_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_R128_BLIT, drm_r128_blit_t) +#define DRM_IOCTL_R128_DEPTH DRM_IOW( DRM_COMMAND_BASE + DRM_R128_DEPTH, drm_r128_depth_t) +#define DRM_IOCTL_R128_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_R128_STIPPLE, drm_r128_stipple_t) +/* 0x0e not used */ +#define DRM_IOCTL_R128_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_R128_INDIRECT, drm_r128_indirect_t) +#define DRM_IOCTL_R128_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_R128_FULLSCREEN, drm_r128_fullscreen_t) +#define DRM_IOCTL_R128_CLEAR2 DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CLEAR2, drm_r128_clear2_t) +#define DRM_IOCTL_R128_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_R128_GETPARAM, drm_r128_getparam_t) +#define DRM_IOCTL_R128_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_R128_FLIP) + +typedef struct drm_r128_init { + enum { + R128_INIT_CCE = 0x01, + R128_CLEANUP_CCE = 0x02 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cce_mode; + int cce_secure; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + unsigned int span_offset; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; +} drm_r128_init_t; + +typedef struct drm_r128_cce_stop { + int flush; + int idle; +} drm_r128_cce_stop_t; + +typedef struct drm_r128_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; +} drm_r128_clear_t; + +typedef struct drm_r128_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_r128_vertex_t; + +typedef struct drm_r128_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_r128_indices_t; + +typedef struct drm_r128_blit { + int idx; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_r128_blit_t; + +typedef struct drm_r128_depth { + enum { + R128_WRITE_SPAN = 0x01, + R128_WRITE_PIXELS = 0x02, + R128_READ_SPAN = 0x03, + R128_READ_PIXELS = 0x04 + } func; + int n; + int *x; + int *y; + unsigned int *buffer; + unsigned char *mask; +} drm_r128_depth_t; + +typedef struct drm_r128_stipple { + unsigned int *mask; +} drm_r128_stipple_t; + +typedef struct drm_r128_indirect { + int idx; + int start; + int end; + int discard; +} drm_r128_indirect_t; + +typedef struct drm_r128_fullscreen { + enum { + R128_INIT_FULLSCREEN = 0x01, + R128_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_r128_fullscreen_t; + +/* 2.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define R128_PARAM_IRQ_NR 1 + +typedef struct drm_r128_getparam { + int param; + void *value; +} drm_r128_getparam_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/radeon_drm.h b/libs/common/drm/include/libdrm/radeon_drm.h new file mode 100644 index 0000000..a1e385d --- /dev/null +++ b/libs/common/drm/include/libdrm/radeon_drm.h @@ -0,0 +1,1079 @@ +/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __RADEON_DRM_H__ +#define __RADEON_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (radeon_sarea.h) + */ +#ifndef __RADEON_SAREA_DEFINES__ +#define __RADEON_SAREA_DEFINES__ + +/* Old style state flags, required for sarea interface (1.1 and 1.2 + * clears) and 1.2 drm_vertex2 ioctl. + */ +#define RADEON_UPLOAD_CONTEXT 0x00000001 +#define RADEON_UPLOAD_VERTFMT 0x00000002 +#define RADEON_UPLOAD_LINE 0x00000004 +#define RADEON_UPLOAD_BUMPMAP 0x00000008 +#define RADEON_UPLOAD_MASKS 0x00000010 +#define RADEON_UPLOAD_VIEWPORT 0x00000020 +#define RADEON_UPLOAD_SETUP 0x00000040 +#define RADEON_UPLOAD_TCL 0x00000080 +#define RADEON_UPLOAD_MISC 0x00000100 +#define RADEON_UPLOAD_TEX0 0x00000200 +#define RADEON_UPLOAD_TEX1 0x00000400 +#define RADEON_UPLOAD_TEX2 0x00000800 +#define RADEON_UPLOAD_TEX0IMAGES 0x00001000 +#define RADEON_UPLOAD_TEX1IMAGES 0x00002000 +#define RADEON_UPLOAD_TEX2IMAGES 0x00004000 +#define RADEON_UPLOAD_CLIPRECTS 0x00008000 /* handled client-side */ +#define RADEON_REQUIRE_QUIESCENCE 0x00010000 +#define RADEON_UPLOAD_ZBIAS 0x00020000 /* version 1.2 and newer */ +#define RADEON_UPLOAD_ALL 0x003effff +#define RADEON_UPLOAD_CONTEXT_ALL 0x003e01ff + +/* New style per-packet identifiers for use in cmd_buffer ioctl with + * the RADEON_EMIT_PACKET command. Comments relate new packets to old + * state bits and the packet size: + */ +#define RADEON_EMIT_PP_MISC 0 /* context/7 */ +#define RADEON_EMIT_PP_CNTL 1 /* context/3 */ +#define RADEON_EMIT_RB3D_COLORPITCH 2 /* context/1 */ +#define RADEON_EMIT_RE_LINE_PATTERN 3 /* line/2 */ +#define RADEON_EMIT_SE_LINE_WIDTH 4 /* line/1 */ +#define RADEON_EMIT_PP_LUM_MATRIX 5 /* bumpmap/1 */ +#define RADEON_EMIT_PP_ROT_MATRIX_0 6 /* bumpmap/2 */ +#define RADEON_EMIT_RB3D_STENCILREFMASK 7 /* masks/3 */ +#define RADEON_EMIT_SE_VPORT_XSCALE 8 /* viewport/6 */ +#define RADEON_EMIT_SE_CNTL 9 /* setup/2 */ +#define RADEON_EMIT_SE_CNTL_STATUS 10 /* setup/1 */ +#define RADEON_EMIT_RE_MISC 11 /* misc/1 */ +#define RADEON_EMIT_PP_TXFILTER_0 12 /* tex0/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_0 13 /* tex0/1 */ +#define RADEON_EMIT_PP_TXFILTER_1 14 /* tex1/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_1 15 /* tex1/1 */ +#define RADEON_EMIT_PP_TXFILTER_2 16 /* tex2/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_2 17 /* tex2/1 */ +#define RADEON_EMIT_SE_ZBIAS_FACTOR 18 /* zbias/2 */ +#define RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT 19 /* tcl/11 */ +#define RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED 20 /* material/17 */ +#define R200_EMIT_PP_TXCBLEND_0 21 /* tex0/4 */ +#define R200_EMIT_PP_TXCBLEND_1 22 /* tex1/4 */ +#define R200_EMIT_PP_TXCBLEND_2 23 /* tex2/4 */ +#define R200_EMIT_PP_TXCBLEND_3 24 /* tex3/4 */ +#define R200_EMIT_PP_TXCBLEND_4 25 /* tex4/4 */ +#define R200_EMIT_PP_TXCBLEND_5 26 /* tex5/4 */ +#define R200_EMIT_PP_TXCBLEND_6 27 /* /4 */ +#define R200_EMIT_PP_TXCBLEND_7 28 /* /4 */ +#define R200_EMIT_TCL_LIGHT_MODEL_CTL_0 29 /* tcl/7 */ +#define R200_EMIT_TFACTOR_0 30 /* tf/7 */ +#define R200_EMIT_VTX_FMT_0 31 /* vtx/5 */ +#define R200_EMIT_VAP_CTL 32 /* vap/1 */ +#define R200_EMIT_MATRIX_SELECT_0 33 /* msl/5 */ +#define R200_EMIT_TEX_PROC_CTL_2 34 /* tcg/5 */ +#define R200_EMIT_TCL_UCP_VERT_BLEND_CTL 35 /* tcl/1 */ +#define R200_EMIT_PP_TXFILTER_0 36 /* tex0/6 */ +#define R200_EMIT_PP_TXFILTER_1 37 /* tex1/6 */ +#define R200_EMIT_PP_TXFILTER_2 38 /* tex2/6 */ +#define R200_EMIT_PP_TXFILTER_3 39 /* tex3/6 */ +#define R200_EMIT_PP_TXFILTER_4 40 /* tex4/6 */ +#define R200_EMIT_PP_TXFILTER_5 41 /* tex5/6 */ +#define R200_EMIT_PP_TXOFFSET_0 42 /* tex0/1 */ +#define R200_EMIT_PP_TXOFFSET_1 43 /* tex1/1 */ +#define R200_EMIT_PP_TXOFFSET_2 44 /* tex2/1 */ +#define R200_EMIT_PP_TXOFFSET_3 45 /* tex3/1 */ +#define R200_EMIT_PP_TXOFFSET_4 46 /* tex4/1 */ +#define R200_EMIT_PP_TXOFFSET_5 47 /* tex5/1 */ +#define R200_EMIT_VTE_CNTL 48 /* vte/1 */ +#define R200_EMIT_OUTPUT_VTX_COMP_SEL 49 /* vtx/1 */ +#define R200_EMIT_PP_TAM_DEBUG3 50 /* tam/1 */ +#define R200_EMIT_PP_CNTL_X 51 /* cst/1 */ +#define R200_EMIT_RB3D_DEPTHXY_OFFSET 52 /* cst/1 */ +#define R200_EMIT_RE_AUX_SCISSOR_CNTL 53 /* cst/1 */ +#define R200_EMIT_RE_SCISSOR_TL_0 54 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_1 55 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_2 56 /* cst/2 */ +#define R200_EMIT_SE_VAP_CNTL_STATUS 57 /* cst/1 */ +#define R200_EMIT_SE_VTX_STATE_CNTL 58 /* cst/1 */ +#define R200_EMIT_RE_POINTSIZE 59 /* cst/1 */ +#define R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0 60 /* cst/4 */ +#define R200_EMIT_PP_CUBIC_FACES_0 61 +#define R200_EMIT_PP_CUBIC_OFFSETS_0 62 +#define R200_EMIT_PP_CUBIC_FACES_1 63 +#define R200_EMIT_PP_CUBIC_OFFSETS_1 64 +#define R200_EMIT_PP_CUBIC_FACES_2 65 +#define R200_EMIT_PP_CUBIC_OFFSETS_2 66 +#define R200_EMIT_PP_CUBIC_FACES_3 67 +#define R200_EMIT_PP_CUBIC_OFFSETS_3 68 +#define R200_EMIT_PP_CUBIC_FACES_4 69 +#define R200_EMIT_PP_CUBIC_OFFSETS_4 70 +#define R200_EMIT_PP_CUBIC_FACES_5 71 +#define R200_EMIT_PP_CUBIC_OFFSETS_5 72 +#define RADEON_EMIT_PP_TEX_SIZE_0 73 +#define RADEON_EMIT_PP_TEX_SIZE_1 74 +#define RADEON_EMIT_PP_TEX_SIZE_2 75 +#define R200_EMIT_RB3D_BLENDCOLOR 76 +#define R200_EMIT_TCL_POINT_SPRITE_CNTL 77 +#define RADEON_EMIT_PP_CUBIC_FACES_0 78 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T0 79 +#define RADEON_EMIT_PP_CUBIC_FACES_1 80 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T1 81 +#define RADEON_EMIT_PP_CUBIC_FACES_2 82 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T2 83 +#define R200_EMIT_PP_TRI_PERF_CNTL 84 +#define R200_EMIT_PP_AFS_0 85 +#define R200_EMIT_PP_AFS_1 86 +#define R200_EMIT_ATF_TFACTOR 87 +#define R200_EMIT_PP_TXCTLALL_0 88 +#define R200_EMIT_PP_TXCTLALL_1 89 +#define R200_EMIT_PP_TXCTLALL_2 90 +#define R200_EMIT_PP_TXCTLALL_3 91 +#define R200_EMIT_PP_TXCTLALL_4 92 +#define R200_EMIT_PP_TXCTLALL_5 93 +#define R200_EMIT_VAP_PVS_CNTL 94 +#define RADEON_MAX_STATE_PACKETS 95 + +/* Commands understood by cmd_buffer ioctl. More can be added but + * obviously these can't be removed or changed: + */ +#define RADEON_CMD_PACKET 1 /* emit one of the register packets above */ +#define RADEON_CMD_SCALARS 2 /* emit scalar data */ +#define RADEON_CMD_VECTORS 3 /* emit vector data */ +#define RADEON_CMD_DMA_DISCARD 4 /* discard current dma buf */ +#define RADEON_CMD_PACKET3 5 /* emit hw packet */ +#define RADEON_CMD_PACKET3_CLIP 6 /* emit hw packet wrapped in cliprects */ +#define RADEON_CMD_SCALARS2 7 /* r200 stopgap */ +#define RADEON_CMD_WAIT 8 /* emit hw wait commands -- note: + * doesn't make the cpu wait, just + * the graphics hardware */ +#define RADEON_CMD_VECLINEAR 9 /* another r200 stopgap */ + +typedef union { + int i; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, packet_id, pad0, pad1; + } packet; + struct { + unsigned char cmd_type, offset, stride, count; + } scalars; + struct { + unsigned char cmd_type, offset, stride, count; + } vectors; + struct { + unsigned char cmd_type, addr_lo, addr_hi, count; + } veclinear; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; +} drm_radeon_cmd_header_t; + +#define RADEON_WAIT_2D 0x1 +#define RADEON_WAIT_3D 0x2 + +/* Allowed parameters for R300_CMD_PACKET3 + */ +#define R300_CMD_PACKET3_CLEAR 0 +#define R300_CMD_PACKET3_RAW 1 + +/* Commands understood by cmd_buffer ioctl for R300. + * The interface has not been stabilized, so some of these may be removed + * and eventually reordered before stabilization. + */ +#define R300_CMD_PACKET0 1 +#define R300_CMD_VPU 2 /* emit vertex program upload */ +#define R300_CMD_PACKET3 3 /* emit a packet3 */ +#define R300_CMD_END3D 4 /* emit sequence ending 3d rendering */ +#define R300_CMD_CP_DELAY 5 +#define R300_CMD_DMA_DISCARD 6 +#define R300_CMD_WAIT 7 +# define R300_WAIT_2D 0x1 +# define R300_WAIT_3D 0x2 +/* these two defines are DOING IT WRONG - however + * we have userspace which relies on using these. + * The wait interface is backwards compat new + * code should use the NEW_WAIT defines below + * THESE ARE NOT BIT FIELDS + */ +# define R300_WAIT_2D_CLEAN 0x3 +# define R300_WAIT_3D_CLEAN 0x4 + +# define R300_NEW_WAIT_2D_3D 0x3 +# define R300_NEW_WAIT_2D_2D_CLEAN 0x4 +# define R300_NEW_WAIT_3D_3D_CLEAN 0x6 +# define R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN 0x8 + +#define R300_CMD_SCRATCH 8 +#define R300_CMD_R500FP 9 + +typedef union { + unsigned int u; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, count, reglo, reghi; + } packet0; + struct { + unsigned char cmd_type, count, adrlo, adrhi; + } vpu; + struct { + unsigned char cmd_type, packet, pad0, pad1; + } packet3; + struct { + unsigned char cmd_type, packet; + unsigned short count; /* amount of packet2 to emit */ + } delay; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; + struct { + unsigned char cmd_type, reg, n_bufs, flags; + } scratch; + struct { + unsigned char cmd_type, count, adrlo, adrhi_flags; + } r500fp; +} drm_r300_cmd_header_t; + +#define RADEON_FRONT 0x1 +#define RADEON_BACK 0x2 +#define RADEON_DEPTH 0x4 +#define RADEON_STENCIL 0x8 +#define RADEON_CLEAR_FASTZ 0x80000000 +#define RADEON_USE_HIERZ 0x40000000 +#define RADEON_USE_COMP_ZBUF 0x20000000 + +#define R500FP_CONSTANT_TYPE (1 << 1) +#define R500FP_CONSTANT_CLAMP (1 << 2) + +/* Primitive types + */ +#define RADEON_POINTS 0x1 +#define RADEON_LINES 0x2 +#define RADEON_LINE_STRIP 0x3 +#define RADEON_TRIANGLES 0x4 +#define RADEON_TRIANGLE_FAN 0x5 +#define RADEON_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define RADEON_BUFFER_SIZE 65536 + +/* Byte offsets for indirect buffer data + */ +#define RADEON_INDEX_PRIM_OFFSET 20 + +#define RADEON_SCRATCH_REG_OFFSET 32 + +#define R600_SCRATCH_REG_OFFSET 256 + +#define RADEON_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/GART). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define RADEON_LOCAL_TEX_HEAP 0 +#define RADEON_GART_TEX_HEAP 1 +#define RADEON_NR_TEX_HEAPS 2 +#define RADEON_NR_TEX_REGIONS 64 +#define RADEON_LOG_TEX_GRANULARITY 16 + +#define RADEON_MAX_TEXTURE_LEVELS 12 +#define RADEON_MAX_TEXTURE_UNITS 3 + +#define RADEON_MAX_SURFACES 8 + +/* Blits have strict offset rules. All blit offset must be aligned on + * a 1K-byte boundary. + */ +#define RADEON_OFFSET_SHIFT 10 +#define RADEON_OFFSET_ALIGN (1 << RADEON_OFFSET_SHIFT) +#define RADEON_OFFSET_MASK (RADEON_OFFSET_ALIGN - 1) + +#endif /* __RADEON_SAREA_DEFINES__ */ + +typedef struct { + unsigned int red; + unsigned int green; + unsigned int blue; + unsigned int alpha; +} radeon_color_regs_t; + +typedef struct { + /* Context state */ + unsigned int pp_misc; /* 0x1c14 */ + unsigned int pp_fog_color; + unsigned int re_solid_color; + unsigned int rb3d_blendcntl; + unsigned int rb3d_depthoffset; + unsigned int rb3d_depthpitch; + unsigned int rb3d_zstencilcntl; + + unsigned int pp_cntl; /* 0x1c38 */ + unsigned int rb3d_cntl; + unsigned int rb3d_coloroffset; + unsigned int re_width_height; + unsigned int rb3d_colorpitch; + unsigned int se_cntl; + + /* Vertex format state */ + unsigned int se_coord_fmt; /* 0x1c50 */ + + /* Line state */ + unsigned int re_line_pattern; /* 0x1cd0 */ + unsigned int re_line_state; + + unsigned int se_line_width; /* 0x1db8 */ + + /* Bumpmap state */ + unsigned int pp_lum_matrix; /* 0x1d00 */ + + unsigned int pp_rot_matrix_0; /* 0x1d58 */ + unsigned int pp_rot_matrix_1; + + /* Mask state */ + unsigned int rb3d_stencilrefmask; /* 0x1d7c */ + unsigned int rb3d_ropcntl; + unsigned int rb3d_planemask; + + /* Viewport state */ + unsigned int se_vport_xscale; /* 0x1d98 */ + unsigned int se_vport_xoffset; + unsigned int se_vport_yscale; + unsigned int se_vport_yoffset; + unsigned int se_vport_zscale; + unsigned int se_vport_zoffset; + + /* Setup state */ + unsigned int se_cntl_status; /* 0x2140 */ + + /* Misc state */ + unsigned int re_top_left; /* 0x26c0 */ + unsigned int re_misc; +} drm_radeon_context_regs_t; + +typedef struct { + /* Zbias state */ + unsigned int se_zbias_factor; /* 0x1dac */ + unsigned int se_zbias_constant; +} drm_radeon_context2_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int pp_txfilter; + unsigned int pp_txformat; + unsigned int pp_txoffset; + unsigned int pp_txcblend; + unsigned int pp_txablend; + unsigned int pp_tfactor; + unsigned int pp_border_color; +} drm_radeon_texture_regs_t; + +typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim:8; + unsigned int stateidx:8; + unsigned int numverts:16; /* overloaded as offset/64 for elt prims */ + unsigned int vc_format; /* vertex format */ +} drm_radeon_prim_t; + +typedef struct { + drm_radeon_context_regs_t context; + drm_radeon_texture_regs_t tex[RADEON_MAX_TEXTURE_UNITS]; + drm_radeon_context2_regs_t context2; + unsigned int dirty; +} drm_radeon_state_t; + +typedef struct { + /* The channel for communication of state information to the + * kernel on firing a vertex buffer with either of the + * obsoleted vertex/index ioctls. + */ + drm_radeon_context_regs_t context_state; + drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[RADEON_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + unsigned int last_clear; + + struct drm_tex_region tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[RADEON_NR_TEX_HEAPS]; + int ctx_owner; + int pfState; /* number of 3d windows (0,1,2ormore) */ + int pfCurrentPage; /* which buffer is being displayed? */ + int crtc2_base; /* CRTC2 frame offset */ + int tiling_enabled; /* set by drm, read by 2d + 3d clients */ +} drm_radeon_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmRadeon.h) + * + * KW: actually it's illegal to change any of this (backwards compatibility). + */ + +/* Radeon specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_RADEON_CP_INIT 0x00 +#define DRM_RADEON_CP_START 0x01 +#define DRM_RADEON_CP_STOP 0x02 +#define DRM_RADEON_CP_RESET 0x03 +#define DRM_RADEON_CP_IDLE 0x04 +#define DRM_RADEON_RESET 0x05 +#define DRM_RADEON_FULLSCREEN 0x06 +#define DRM_RADEON_SWAP 0x07 +#define DRM_RADEON_CLEAR 0x08 +#define DRM_RADEON_VERTEX 0x09 +#define DRM_RADEON_INDICES 0x0A +#define DRM_RADEON_NOT_USED +#define DRM_RADEON_STIPPLE 0x0C +#define DRM_RADEON_INDIRECT 0x0D +#define DRM_RADEON_TEXTURE 0x0E +#define DRM_RADEON_VERTEX2 0x0F +#define DRM_RADEON_CMDBUF 0x10 +#define DRM_RADEON_GETPARAM 0x11 +#define DRM_RADEON_FLIP 0x12 +#define DRM_RADEON_ALLOC 0x13 +#define DRM_RADEON_FREE 0x14 +#define DRM_RADEON_INIT_HEAP 0x15 +#define DRM_RADEON_IRQ_EMIT 0x16 +#define DRM_RADEON_IRQ_WAIT 0x17 +#define DRM_RADEON_CP_RESUME 0x18 +#define DRM_RADEON_SETPARAM 0x19 +#define DRM_RADEON_SURF_ALLOC 0x1a +#define DRM_RADEON_SURF_FREE 0x1b +/* KMS ioctl */ +#define DRM_RADEON_GEM_INFO 0x1c +#define DRM_RADEON_GEM_CREATE 0x1d +#define DRM_RADEON_GEM_MMAP 0x1e +#define DRM_RADEON_GEM_PREAD 0x21 +#define DRM_RADEON_GEM_PWRITE 0x22 +#define DRM_RADEON_GEM_SET_DOMAIN 0x23 +#define DRM_RADEON_GEM_WAIT_IDLE 0x24 +#define DRM_RADEON_CS 0x26 +#define DRM_RADEON_INFO 0x27 +#define DRM_RADEON_GEM_SET_TILING 0x28 +#define DRM_RADEON_GEM_GET_TILING 0x29 +#define DRM_RADEON_GEM_BUSY 0x2a +#define DRM_RADEON_GEM_VA 0x2b +#define DRM_RADEON_GEM_OP 0x2c +#define DRM_RADEON_GEM_USERPTR 0x2d + +#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) +#define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) +#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t) +#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESET) +#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_IDLE) +#define DRM_IOCTL_RADEON_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_RESET) +#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FULLSCREEN, drm_radeon_fullscreen_t) +#define DRM_IOCTL_RADEON_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_SWAP) +#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CLEAR, drm_radeon_clear_t) +#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX, drm_radeon_vertex_t) +#define DRM_IOCTL_RADEON_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INDICES, drm_radeon_indices_t) +#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_STIPPLE, drm_radeon_stipple_t) +#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INDIRECT, drm_radeon_indirect_t) +#define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_TEXTURE, drm_radeon_texture_t) +#define DRM_IOCTL_RADEON_VERTEX2 DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX2, drm_radeon_vertex2_t) +#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CMDBUF, drm_radeon_cmd_buffer_t) +#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GETPARAM, drm_radeon_getparam_t) +#define DRM_IOCTL_RADEON_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_FLIP) +#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_ALLOC, drm_radeon_mem_alloc_t) +#define DRM_IOCTL_RADEON_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FREE, drm_radeon_mem_free_t) +#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INIT_HEAP, drm_radeon_mem_init_heap_t) +#define DRM_IOCTL_RADEON_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_IRQ_EMIT, drm_radeon_irq_emit_t) +#define DRM_IOCTL_RADEON_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_IRQ_WAIT, drm_radeon_irq_wait_t) +#define DRM_IOCTL_RADEON_CP_RESUME DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESUME) +#define DRM_IOCTL_RADEON_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SETPARAM, drm_radeon_setparam_t) +#define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t) +#define DRM_IOCTL_RADEON_SURF_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t) +/* KMS */ +#define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) +#define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) +#define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) +#define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) +#define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) +#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) +#define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle) +#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) +#define DRM_IOCTL_RADEON_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INFO, struct drm_radeon_info) +#define DRM_IOCTL_RADEON_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_TILING, struct drm_radeon_gem_set_tiling) +#define DRM_IOCTL_RADEON_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_GET_TILING, struct drm_radeon_gem_get_tiling) +#define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy) +#define DRM_IOCTL_RADEON_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_VA, struct drm_radeon_gem_va) +#define DRM_IOCTL_RADEON_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_OP, struct drm_radeon_gem_op) +#define DRM_IOCTL_RADEON_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_USERPTR, struct drm_radeon_gem_userptr) + +typedef struct drm_radeon_init { + enum { + RADEON_INIT_CP = 0x01, + RADEON_CLEANUP_CP = 0x02, + RADEON_INIT_R200_CP = 0x03, + RADEON_INIT_R300_CP = 0x04, + RADEON_INIT_R600_CP = 0x05 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cp_mode; + int gart_size; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long gart_textures_offset; +} drm_radeon_init_t; + +typedef struct drm_radeon_cp_stop { + int flush; + int idle; +} drm_radeon_cp_stop_t; + +typedef struct drm_radeon_fullscreen { + enum { + RADEON_INIT_FULLSCREEN = 0x01, + RADEON_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_radeon_fullscreen_t; + +#define CLEAR_X1 0 +#define CLEAR_Y1 1 +#define CLEAR_X2 2 +#define CLEAR_Y2 3 +#define CLEAR_DEPTH 4 + +typedef union drm_radeon_clear_rect { + float f[5]; + unsigned int ui[5]; +} drm_radeon_clear_rect_t; + +typedef struct drm_radeon_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; /* misnamed field: should be stencil */ + drm_radeon_clear_rect_t *depth_boxes; +} drm_radeon_clear_t; + +typedef struct drm_radeon_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_radeon_vertex_t; + +typedef struct drm_radeon_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_radeon_indices_t; + +/* v1.2 - obsoletes drm_radeon_vertex and drm_radeon_indices + * - allows multiple primitives and state changes in a single ioctl + * - supports driver change to emit native primitives + */ +typedef struct drm_radeon_vertex2 { + int idx; /* Index of vertex buffer */ + int discard; /* Client finished with buffer? */ + int nr_states; + drm_radeon_state_t *state; + int nr_prims; + drm_radeon_prim_t *prim; +} drm_radeon_vertex2_t; + +/* v1.3 - obsoletes drm_radeon_vertex2 + * - allows arbitrarily large cliprect list + * - allows updating of tcl packet, vector and scalar state + * - allows memory-efficient description of state updates + * - allows state to be emitted without a primitive + * (for clears, ctx switches) + * - allows more than one dma buffer to be referenced per ioctl + * - supports tcl driver + * - may be extended in future versions with new cmd types, packets + */ +typedef struct drm_radeon_cmd_buffer { + int bufsz; + char *buf; + int nbox; + struct drm_clip_rect *boxes; +} drm_radeon_cmd_buffer_t; + +typedef struct drm_radeon_tex_image { + unsigned int x, y; /* Blit coordinates */ + unsigned int width, height; + const void *data; +} drm_radeon_tex_image_t; + +typedef struct drm_radeon_texture { + unsigned int offset; + int pitch; + int format; + int width; /* Texture image coordinates */ + int height; + drm_radeon_tex_image_t *image; +} drm_radeon_texture_t; + +typedef struct drm_radeon_stipple { + unsigned int *mask; +} drm_radeon_stipple_t; + +typedef struct drm_radeon_indirect { + int idx; + int start; + int end; + int discard; +} drm_radeon_indirect_t; + +/* enum for card type parameters */ +#define RADEON_CARD_PCI 0 +#define RADEON_CARD_AGP 1 +#define RADEON_CARD_PCIE 2 + +/* 1.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define RADEON_PARAM_GART_BUFFER_OFFSET 1 /* card offset of 1st GART buffer */ +#define RADEON_PARAM_LAST_FRAME 2 +#define RADEON_PARAM_LAST_DISPATCH 3 +#define RADEON_PARAM_LAST_CLEAR 4 +/* Added with DRM version 1.6. */ +#define RADEON_PARAM_IRQ_NR 5 +#define RADEON_PARAM_GART_BASE 6 /* card offset of GART base */ +/* Added with DRM version 1.8. */ +#define RADEON_PARAM_REGISTER_HANDLE 7 /* for drmMap() */ +#define RADEON_PARAM_STATUS_HANDLE 8 +#define RADEON_PARAM_SAREA_HANDLE 9 +#define RADEON_PARAM_GART_TEX_HANDLE 10 +#define RADEON_PARAM_SCRATCH_OFFSET 11 +#define RADEON_PARAM_CARD_TYPE 12 +#define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ +#define RADEON_PARAM_FB_LOCATION 14 /* FB location */ +#define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ +#define RADEON_PARAM_DEVICE_ID 16 +#define RADEON_PARAM_NUM_Z_PIPES 17 /* num Z pipes */ + +typedef struct drm_radeon_getparam { + int param; + void *value; +} drm_radeon_getparam_t; + +/* 1.6: Set up a memory manager for regions of shared memory: + */ +#define RADEON_MEM_REGION_GART 1 +#define RADEON_MEM_REGION_FB 2 + +typedef struct drm_radeon_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or GART */ +} drm_radeon_mem_alloc_t; + +typedef struct drm_radeon_mem_free { + int region; + int region_offset; +} drm_radeon_mem_free_t; + +typedef struct drm_radeon_mem_init_heap { + int region; + int size; + int start; +} drm_radeon_mem_init_heap_t; + +/* 1.6: Userspace can request & wait on irq's: + */ +typedef struct drm_radeon_irq_emit { + int *irq_seq; +} drm_radeon_irq_emit_t; + +typedef struct drm_radeon_irq_wait { + int irq_seq; +} drm_radeon_irq_wait_t; + +/* 1.10: Clients tell the DRM where they think the framebuffer is located in + * the card's address space, via a new generic ioctl to set parameters + */ + +typedef struct drm_radeon_setparam { + unsigned int param; + __s64 value; +} drm_radeon_setparam_t; + +#define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ +#define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ +#define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ +#define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ +#define RADEON_SETPARAM_PCIGART_TABLE_SIZE 5 /* PCI GART Table Size */ +#define RADEON_SETPARAM_VBLANK_CRTC 6 /* VBLANK CRTC */ +/* 1.14: Clients can allocate/free a surface + */ +typedef struct drm_radeon_surface_alloc { + unsigned int address; + unsigned int size; + unsigned int flags; +} drm_radeon_surface_alloc_t; + +typedef struct drm_radeon_surface_free { + unsigned int address; +} drm_radeon_surface_free_t; + +#define DRM_RADEON_VBLANK_CRTC1 1 +#define DRM_RADEON_VBLANK_CRTC2 2 + +/* + * Kernel modesetting world below. + */ +#define RADEON_GEM_DOMAIN_CPU 0x1 +#define RADEON_GEM_DOMAIN_GTT 0x2 +#define RADEON_GEM_DOMAIN_VRAM 0x4 + +struct drm_radeon_gem_info { + __u64 gart_size; + __u64 vram_size; + __u64 vram_visible; +}; + +#define RADEON_GEM_NO_BACKING_STORE (1 << 0) +#define RADEON_GEM_GTT_UC (1 << 1) +#define RADEON_GEM_GTT_WC (1 << 2) +/* BO is expected to be accessed by the CPU */ +#define RADEON_GEM_CPU_ACCESS (1 << 3) +/* CPU access is not expected to work for this BO */ +#define RADEON_GEM_NO_CPU_ACCESS (1 << 4) + +struct drm_radeon_gem_create { + __u64 size; + __u64 alignment; + __u32 handle; + __u32 initial_domain; + __u32 flags; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define RADEON_GEM_USERPTR_READONLY (1 << 0) +#define RADEON_GEM_USERPTR_ANONONLY (1 << 1) +#define RADEON_GEM_USERPTR_VALIDATE (1 << 2) +#define RADEON_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_radeon_gem_userptr { + __u64 addr; + __u64 size; + __u32 flags; + __u32 handle; +}; + +#define RADEON_TILING_MACRO 0x1 +#define RADEON_TILING_MICRO 0x2 +#define RADEON_TILING_SWAP_16BIT 0x4 +#define RADEON_TILING_R600_NO_SCANOUT RADEON_TILING_SWAP_16BIT +#define RADEON_TILING_SWAP_32BIT 0x8 +/* this object requires a surface when mapped - i.e. front buffer */ +#define RADEON_TILING_SURFACE 0x10 +#define RADEON_TILING_MICRO_SQUARE 0x20 +#define RADEON_TILING_EG_BANKW_SHIFT 8 +#define RADEON_TILING_EG_BANKW_MASK 0xf +#define RADEON_TILING_EG_BANKH_SHIFT 12 +#define RADEON_TILING_EG_BANKH_MASK 0xf +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf +#define RADEON_TILING_EG_TILE_SPLIT_SHIFT 24 +#define RADEON_TILING_EG_TILE_SPLIT_MASK 0xf +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf + +struct drm_radeon_gem_set_tiling { + __u32 handle; + __u32 tiling_flags; + __u32 pitch; +}; + +struct drm_radeon_gem_get_tiling { + __u32 handle; + __u32 tiling_flags; + __u32 pitch; +}; + +struct drm_radeon_gem_mmap { + __u32 handle; + __u32 pad; + __u64 offset; + __u64 size; + __u64 addr_ptr; +}; + +struct drm_radeon_gem_set_domain { + __u32 handle; + __u32 read_domains; + __u32 write_domain; +}; + +struct drm_radeon_gem_wait_idle { + __u32 handle; + __u32 pad; +}; + +struct drm_radeon_gem_busy { + __u32 handle; + __u32 domain; +}; + +struct drm_radeon_gem_pread { + /** Handle for the object being read. */ + __u32 handle; + __u32 pad; + /** Offset into the object to read from */ + __u64 offset; + /** Length of data to read */ + __u64 size; + /** Pointer to write the data into. */ + /* void *, but pointers are not 32/64 compatible */ + __u64 data_ptr; +}; + +struct drm_radeon_gem_pwrite { + /** Handle for the object being written to. */ + __u32 handle; + __u32 pad; + /** Offset into the object to write to */ + __u64 offset; + /** Length of data to write */ + __u64 size; + /** Pointer to read the data from. */ + /* void *, but pointers are not 32/64 compatible */ + __u64 data_ptr; +}; + +/* Sets or returns a value associated with a buffer. */ +struct drm_radeon_gem_op { + __u32 handle; /* buffer */ + __u32 op; /* RADEON_GEM_OP_* */ + __u64 value; /* input or return value */ +}; + +#define RADEON_GEM_OP_GET_INITIAL_DOMAIN 0 +#define RADEON_GEM_OP_SET_INITIAL_DOMAIN 1 + +#define RADEON_VA_MAP 1 +#define RADEON_VA_UNMAP 2 + +#define RADEON_VA_RESULT_OK 0 +#define RADEON_VA_RESULT_ERROR 1 +#define RADEON_VA_RESULT_VA_EXIST 2 + +#define RADEON_VM_PAGE_VALID (1 << 0) +#define RADEON_VM_PAGE_READABLE (1 << 1) +#define RADEON_VM_PAGE_WRITEABLE (1 << 2) +#define RADEON_VM_PAGE_SYSTEM (1 << 3) +#define RADEON_VM_PAGE_SNOOPED (1 << 4) + +struct drm_radeon_gem_va { + __u32 handle; + __u32 operation; + __u32 vm_id; + __u32 flags; + __u64 offset; +}; + +#define RADEON_CHUNK_ID_RELOCS 0x01 +#define RADEON_CHUNK_ID_IB 0x02 +#define RADEON_CHUNK_ID_FLAGS 0x03 +#define RADEON_CHUNK_ID_CONST_IB 0x04 + +/* The first dword of RADEON_CHUNK_ID_FLAGS is a uint32 of these flags: */ +#define RADEON_CS_KEEP_TILING_FLAGS 0x01 +#define RADEON_CS_USE_VM 0x02 +#define RADEON_CS_END_OF_FRAME 0x04 /* a hint from userspace which CS is the last one */ +/* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */ +#define RADEON_CS_RING_GFX 0 +#define RADEON_CS_RING_COMPUTE 1 +#define RADEON_CS_RING_DMA 2 +#define RADEON_CS_RING_UVD 3 +#define RADEON_CS_RING_VCE 4 +/* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */ +/* 0 = normal, + = higher priority, - = lower priority */ + +struct drm_radeon_cs_chunk { + __u32 chunk_id; + __u32 length_dw; + __u64 chunk_data; +}; + +/* drm_radeon_cs_reloc.flags */ +#define RADEON_RELOC_PRIO_MASK (0xf << 0) + +struct drm_radeon_cs_reloc { + __u32 handle; + __u32 read_domains; + __u32 write_domain; + __u32 flags; +}; + +struct drm_radeon_cs { + __u32 num_chunks; + __u32 cs_id; + /* this points to __u64 * which point to cs chunks */ + __u64 chunks; + /* updates to the limits after this CS ioctl */ + __u64 gart_limit; + __u64 vram_limit; +}; + +#define RADEON_INFO_DEVICE_ID 0x00 +#define RADEON_INFO_NUM_GB_PIPES 0x01 +#define RADEON_INFO_NUM_Z_PIPES 0x02 +#define RADEON_INFO_ACCEL_WORKING 0x03 +#define RADEON_INFO_CRTC_FROM_ID 0x04 +#define RADEON_INFO_ACCEL_WORKING2 0x05 +#define RADEON_INFO_TILING_CONFIG 0x06 +#define RADEON_INFO_WANT_HYPERZ 0x07 +#define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */ +#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */ +#define RADEON_INFO_NUM_BACKENDS 0x0a /* DB/backends for r600+ - need for OQ */ +#define RADEON_INFO_NUM_TILE_PIPES 0x0b /* tile pipes for r600+ */ +#define RADEON_INFO_FUSION_GART_WORKING 0x0c /* fusion writes to GTT were broken before this */ +#define RADEON_INFO_BACKEND_MAP 0x0d /* pipe to backend map, needed by mesa */ +/* virtual address start, va < start are reserved by the kernel */ +#define RADEON_INFO_VA_START 0x0e +/* maximum size of ib using the virtual memory cs */ +#define RADEON_INFO_IB_VM_MAX_SIZE 0x0f +/* max pipes - needed for compute shaders */ +#define RADEON_INFO_MAX_PIPES 0x10 +/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ +#define RADEON_INFO_TIMESTAMP 0x11 +/* max shader engines (SE) - needed for geometry shaders, etc. */ +#define RADEON_INFO_MAX_SE 0x12 +/* max SH per SE */ +#define RADEON_INFO_MAX_SH_PER_SE 0x13 +/* fast fb access is enabled */ +#define RADEON_INFO_FASTFB_WORKING 0x14 +/* query if a RADEON_CS_RING_* submission is supported */ +#define RADEON_INFO_RING_WORKING 0x15 +/* SI tile mode array */ +#define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16 +/* query if CP DMA is supported on the compute ring */ +#define RADEON_INFO_SI_CP_DMA_COMPUTE 0x17 +/* CIK macrotile mode array */ +#define RADEON_INFO_CIK_MACROTILE_MODE_ARRAY 0x18 +/* query the number of render backends */ +#define RADEON_INFO_SI_BACKEND_ENABLED_MASK 0x19 +/* max engine clock - needed for OpenCL */ +#define RADEON_INFO_MAX_SCLK 0x1a +/* version of VCE firmware */ +#define RADEON_INFO_VCE_FW_VERSION 0x1b +/* version of VCE feedback */ +#define RADEON_INFO_VCE_FB_VERSION 0x1c +#define RADEON_INFO_NUM_BYTES_MOVED 0x1d +#define RADEON_INFO_VRAM_USAGE 0x1e +#define RADEON_INFO_GTT_USAGE 0x1f +#define RADEON_INFO_ACTIVE_CU_COUNT 0x20 +#define RADEON_INFO_CURRENT_GPU_TEMP 0x21 +#define RADEON_INFO_CURRENT_GPU_SCLK 0x22 +#define RADEON_INFO_CURRENT_GPU_MCLK 0x23 +#define RADEON_INFO_READ_REG 0x24 +#define RADEON_INFO_VA_UNMAP_WORKING 0x25 +#define RADEON_INFO_GPU_RESET_COUNTER 0x26 + +struct drm_radeon_info { + __u32 request; + __u32 pad; + __u64 value; +}; + +/* Those correspond to the tile index to use, this is to explicitly state + * the API that is implicitly defined by the tile mode array. + */ +#define SI_TILE_MODE_COLOR_LINEAR_ALIGNED 8 +#define SI_TILE_MODE_COLOR_1D 13 +#define SI_TILE_MODE_COLOR_1D_SCANOUT 9 +#define SI_TILE_MODE_COLOR_2D_8BPP 14 +#define SI_TILE_MODE_COLOR_2D_16BPP 15 +#define SI_TILE_MODE_COLOR_2D_32BPP 16 +#define SI_TILE_MODE_COLOR_2D_64BPP 17 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP 11 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP 12 +#define SI_TILE_MODE_DEPTH_STENCIL_1D 4 +#define SI_TILE_MODE_DEPTH_STENCIL_2D 0 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_2AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_4AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA 2 + +#define CIK_TILE_MODE_DEPTH_STENCIL_1D 5 + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/savage_drm.h b/libs/common/drm/include/libdrm/savage_drm.h new file mode 100644 index 0000000..1a91234 --- /dev/null +++ b/libs/common/drm/include/libdrm/savage_drm.h @@ -0,0 +1,220 @@ +/* savage_drm.h -- Public header for the savage driver + * + * Copyright 2004 Felix Kuehling + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SAVAGE_DRM_H__ +#define __SAVAGE_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef __SAVAGE_SAREA_DEFINES__ +#define __SAVAGE_SAREA_DEFINES__ + +/* 2 heaps (1 for card, 1 for agp), each divided into up to 128 + * regions, subject to a minimum region size of (1<<16) == 64k. + * + * Clients may subdivide regions internally, but when sharing between + * clients, the region size is the minimum granularity. + */ + +#define SAVAGE_CARD_HEAP 0 +#define SAVAGE_AGP_HEAP 1 +#define SAVAGE_NR_TEX_HEAPS 2 +#define SAVAGE_NR_TEX_REGIONS 16 +#define SAVAGE_LOG_MIN_TEX_REGION_SIZE 16 + +#endif /* __SAVAGE_SAREA_DEFINES__ */ + +typedef struct _drm_savage_sarea { + /* LRU lists for texture memory in agp space and on the card. + */ + struct drm_tex_region texList[SAVAGE_NR_TEX_HEAPS][SAVAGE_NR_TEX_REGIONS + + 1]; + unsigned int texAge[SAVAGE_NR_TEX_HEAPS]; + + /* Mechanism to validate card state. + */ + int ctxOwner; +} drm_savage_sarea_t, *drm_savage_sarea_ptr; + +/* Savage-specific ioctls + */ +#define DRM_SAVAGE_BCI_INIT 0x00 +#define DRM_SAVAGE_BCI_CMDBUF 0x01 +#define DRM_SAVAGE_BCI_EVENT_EMIT 0x02 +#define DRM_SAVAGE_BCI_EVENT_WAIT 0x03 + +#define DRM_IOCTL_SAVAGE_BCI_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_INIT, drm_savage_init_t) +#define DRM_IOCTL_SAVAGE_BCI_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_CMDBUF, drm_savage_cmdbuf_t) +#define DRM_IOCTL_SAVAGE_BCI_EVENT_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_EMIT, drm_savage_event_emit_t) +#define DRM_IOCTL_SAVAGE_BCI_EVENT_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_WAIT, drm_savage_event_wait_t) + +#define SAVAGE_DMA_PCI 1 +#define SAVAGE_DMA_AGP 3 +typedef struct drm_savage_init { + enum { + SAVAGE_INIT_BCI = 1, + SAVAGE_CLEANUP_BCI = 2 + } func; + unsigned int sarea_priv_offset; + + /* some parameters */ + unsigned int cob_size; + unsigned int bci_threshold_lo, bci_threshold_hi; + unsigned int dma_type; + + /* frame buffer layout */ + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + /* local textures */ + unsigned int texture_offset; + unsigned int texture_size; + + /* physical locations of non-permanent maps */ + unsigned long status_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; + unsigned long cmd_dma_offset; +} drm_savage_init_t; + +typedef union drm_savage_cmd_header drm_savage_cmd_header_t; +typedef struct drm_savage_cmdbuf { + /* command buffer in client's address space */ + drm_savage_cmd_header_t *cmd_addr; + unsigned int size; /* size of the command buffer in 64bit units */ + + unsigned int dma_idx; /* DMA buffer index to use */ + int discard; /* discard DMA buffer when done */ + /* vertex buffer in client's address space */ + unsigned int *vb_addr; + unsigned int vb_size; /* size of client vertex buffer in bytes */ + unsigned int vb_stride; /* stride of vertices in 32bit words */ + /* boxes in client's address space */ + struct drm_clip_rect *box_addr; + unsigned int nbox; /* number of clipping boxes */ +} drm_savage_cmdbuf_t; + +#define SAVAGE_WAIT_2D 0x1 /* wait for 2D idle before updating event tag */ +#define SAVAGE_WAIT_3D 0x2 /* wait for 3D idle before updating event tag */ +#define SAVAGE_WAIT_IRQ 0x4 /* emit or wait for IRQ, not implemented yet */ +typedef struct drm_savage_event { + unsigned int count; + unsigned int flags; +} drm_savage_event_emit_t, drm_savage_event_wait_t; + +/* Commands for the cmdbuf ioctl + */ +#define SAVAGE_CMD_STATE 0 /* a range of state registers */ +#define SAVAGE_CMD_DMA_PRIM 1 /* vertices from DMA buffer */ +#define SAVAGE_CMD_VB_PRIM 2 /* vertices from client vertex buffer */ +#define SAVAGE_CMD_DMA_IDX 3 /* indexed vertices from DMA buffer */ +#define SAVAGE_CMD_VB_IDX 4 /* indexed vertices client vertex buffer */ +#define SAVAGE_CMD_CLEAR 5 /* clear buffers */ +#define SAVAGE_CMD_SWAP 6 /* swap buffers */ + +/* Primitive types +*/ +#define SAVAGE_PRIM_TRILIST 0 /* triangle list */ +#define SAVAGE_PRIM_TRISTRIP 1 /* triangle strip */ +#define SAVAGE_PRIM_TRIFAN 2 /* triangle fan */ +#define SAVAGE_PRIM_TRILIST_201 3 /* reorder verts for correct flat + * shading on s3d */ + +/* Skip flags (vertex format) + */ +#define SAVAGE_SKIP_Z 0x01 +#define SAVAGE_SKIP_W 0x02 +#define SAVAGE_SKIP_C0 0x04 +#define SAVAGE_SKIP_C1 0x08 +#define SAVAGE_SKIP_S0 0x10 +#define SAVAGE_SKIP_T0 0x20 +#define SAVAGE_SKIP_ST0 0x30 +#define SAVAGE_SKIP_S1 0x40 +#define SAVAGE_SKIP_T1 0x80 +#define SAVAGE_SKIP_ST1 0xc0 +#define SAVAGE_SKIP_ALL_S3D 0x3f +#define SAVAGE_SKIP_ALL_S4 0xff + +/* Buffer names for clear command + */ +#define SAVAGE_FRONT 0x1 +#define SAVAGE_BACK 0x2 +#define SAVAGE_DEPTH 0x4 + +/* 64-bit command header + */ +union drm_savage_cmd_header { + struct { + unsigned char cmd; /* command */ + unsigned char pad0; + unsigned short pad1; + unsigned short pad2; + unsigned short pad3; + } cmd; /* generic */ + struct { + unsigned char cmd; + unsigned char global; /* need idle engine? */ + unsigned short count; /* number of consecutive registers */ + unsigned short start; /* first register */ + unsigned short pad3; + } state; /* SAVAGE_CMD_STATE */ + struct { + unsigned char cmd; + unsigned char prim; /* primitive type */ + unsigned short skip; /* vertex format (skip flags) */ + unsigned short count; /* number of vertices */ + unsigned short start; /* first vertex in DMA/vertex buffer */ + } prim; /* SAVAGE_CMD_DMA_PRIM, SAVAGE_CMD_VB_PRIM */ + struct { + unsigned char cmd; + unsigned char prim; + unsigned short skip; + unsigned short count; /* number of indices that follow */ + unsigned short pad3; + } idx; /* SAVAGE_CMD_DMA_IDX, SAVAGE_CMD_VB_IDX */ + struct { + unsigned char cmd; + unsigned char pad0; + unsigned short pad1; + unsigned int flags; + } clear0; /* SAVAGE_CMD_CLEAR */ + struct { + unsigned int mask; + unsigned int value; + } clear1; /* SAVAGE_CMD_CLEAR data */ +}; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/sis_drm.h b/libs/common/drm/include/libdrm/sis_drm.h new file mode 100644 index 0000000..8e51bb9 --- /dev/null +++ b/libs/common/drm/include/libdrm/sis_drm.h @@ -0,0 +1,77 @@ +/* sis_drv.h -- Private header for sis driver -*- linux-c -*- */ +/* + * Copyright 2005 Eric Anholt + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __SIS_DRM_H__ +#define __SIS_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* SiS specific ioctls */ +#define NOT_USED_0_3 +#define DRM_SIS_FB_ALLOC 0x04 +#define DRM_SIS_FB_FREE 0x05 +#define NOT_USED_6_12 +#define DRM_SIS_AGP_INIT 0x13 +#define DRM_SIS_AGP_ALLOC 0x14 +#define DRM_SIS_AGP_FREE 0x15 +#define DRM_SIS_FB_INIT 0x16 + +#define DRM_IOCTL_SIS_FB_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_FB_ALLOC, drm_sis_mem_t) +#define DRM_IOCTL_SIS_FB_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_FB_FREE, drm_sis_mem_t) +#define DRM_IOCTL_SIS_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_AGP_INIT, drm_sis_agp_t) +#define DRM_IOCTL_SIS_AGP_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_AGP_ALLOC, drm_sis_mem_t) +#define DRM_IOCTL_SIS_AGP_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_AGP_FREE, drm_sis_mem_t) +#define DRM_IOCTL_SIS_FB_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_FB_INIT, drm_sis_fb_t) +/* +#define DRM_IOCTL_SIS_FLIP DRM_IOW( 0x48, drm_sis_flip_t) +#define DRM_IOCTL_SIS_FLIP_INIT DRM_IO( 0x49) +#define DRM_IOCTL_SIS_FLIP_FINAL DRM_IO( 0x50) +*/ + +typedef struct { + int context; + unsigned int offset; + unsigned int size; + unsigned long free; +} drm_sis_mem_t; + +typedef struct { + unsigned int offset, size; +} drm_sis_agp_t; + +typedef struct { + unsigned int offset, size; +} drm_sis_fb_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* __SIS_DRM_H__ */ diff --git a/libs/common/drm/include/libdrm/tegra_drm.h b/libs/common/drm/include/libdrm/tegra_drm.h new file mode 100644 index 0000000..6c07919 --- /dev/null +++ b/libs/common/drm/include/libdrm/tegra_drm.h @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _TEGRA_DRM_H_ +#define _TEGRA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0) +#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1) + +/** + * struct drm_tegra_gem_create - parameters for the GEM object creation IOCTL + */ +struct drm_tegra_gem_create { + /** + * @size: + * + * The size, in bytes, of the buffer object to be created. + */ + __u64 size; + + /** + * @flags: + * + * A bitmask of flags that influence the creation of GEM objects: + * + * DRM_TEGRA_GEM_CREATE_TILED + * Use the 16x16 tiling format for this buffer. + * + * DRM_TEGRA_GEM_CREATE_BOTTOM_UP + * The buffer has a bottom-up layout. + */ + __u32 flags; + + /** + * @handle: + * + * The handle of the created GEM object. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u32 handle; +}; + +/** + * struct drm_tegra_gem_mmap - parameters for the GEM mmap IOCTL + */ +struct drm_tegra_gem_mmap { + /** + * @handle: + * + * Handle of the GEM object to obtain an mmap offset for. + */ + __u32 handle; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; + + /** + * @offset: + * + * The mmap offset for the given GEM object. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u64 offset; +}; + +/** + * struct drm_tegra_syncpt_read - parameters for the read syncpoint IOCTL + */ +struct drm_tegra_syncpt_read { + /** + * @id: + * + * ID of the syncpoint to read the current value from. + */ + __u32 id; + + /** + * @value: + * + * The current syncpoint value. Set by the kernel upon successful + * completion of the IOCTL. + */ + __u32 value; +}; + +/** + * struct drm_tegra_syncpt_incr - parameters for the increment syncpoint IOCTL + */ +struct drm_tegra_syncpt_incr { + /** + * @id: + * + * ID of the syncpoint to increment. + */ + __u32 id; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_syncpt_wait - parameters for the wait syncpoint IOCTL + */ +struct drm_tegra_syncpt_wait { + /** + * @id: + * + * ID of the syncpoint to wait on. + */ + __u32 id; + + /** + * @thresh: + * + * Threshold value for which to wait. + */ + __u32 thresh; + + /** + * @timeout: + * + * Timeout, in milliseconds, to wait. + */ + __u32 timeout; + + /** + * @value: + * + * The new syncpoint value after the wait. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u32 value; +}; + +#define DRM_TEGRA_NO_TIMEOUT (0xffffffff) + +/** + * struct drm_tegra_open_channel - parameters for the open channel IOCTL + */ +struct drm_tegra_open_channel { + /** + * @client: + * + * The client ID for this channel. + */ + __u32 client; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; + + /** + * @context: + * + * The application context of this channel. Set by the kernel upon + * successful completion of the IOCTL. This context needs to be passed + * to the DRM_TEGRA_CHANNEL_CLOSE or the DRM_TEGRA_SUBMIT IOCTLs. + */ + __u64 context; +}; + +/** + * struct drm_tegra_close_channel - parameters for the close channel IOCTL + */ +struct drm_tegra_close_channel { + /** + * @context: + * + * The application context of this channel. This is obtained from the + * DRM_TEGRA_OPEN_CHANNEL IOCTL. + */ + __u64 context; +}; + +/** + * struct drm_tegra_get_syncpt - parameters for the get syncpoint IOCTL + */ +struct drm_tegra_get_syncpt { + /** + * @context: + * + * The application context identifying the channel for which to obtain + * the syncpoint ID. + */ + __u64 context; + + /** + * @index: + * + * Index of the client syncpoint for which to obtain the ID. + */ + __u32 index; + + /** + * @id: + * + * The ID of the given syncpoint. Set by the kernel upon successful + * completion of the IOCTL. + */ + __u32 id; +}; + +/** + * struct drm_tegra_get_syncpt_base - parameters for the get wait base IOCTL + */ +struct drm_tegra_get_syncpt_base { + /** + * @context: + * + * The application context identifying for which channel to obtain the + * wait base. + */ + __u64 context; + + /** + * @syncpt: + * + * ID of the syncpoint for which to obtain the wait base. + */ + __u32 syncpt; + + /** + * @id: + * + * The ID of the wait base corresponding to the client syncpoint. Set + * by the kernel upon successful completion of the IOCTL. + */ + __u32 id; +}; + +/** + * struct drm_tegra_syncpt - syncpoint increment operation + */ +struct drm_tegra_syncpt { + /** + * @id: + * + * ID of the syncpoint to operate on. + */ + __u32 id; + + /** + * @incrs: + * + * Number of increments to perform for the syncpoint. + */ + __u32 incrs; +}; + +/** + * struct drm_tegra_cmdbuf - structure describing a command buffer + */ +struct drm_tegra_cmdbuf { + /** + * @handle: + * + * Handle to a GEM object containing the command buffer. + */ + __u32 handle; + + /** + * @offset: + * + * Offset, in bytes, into the GEM object identified by @handle at + * which the command buffer starts. + */ + __u32 offset; + + /** + * @words: + * + * Number of 32-bit words in this command buffer. + */ + __u32 words; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_reloc - GEM object relocation structure + */ +struct drm_tegra_reloc { + struct { + /** + * @cmdbuf.handle: + * + * Handle to the GEM object containing the command buffer for + * which to perform this GEM object relocation. + */ + __u32 handle; + + /** + * @cmdbuf.offset: + * + * Offset, in bytes, into the command buffer at which to + * insert the relocated address. + */ + __u32 offset; + } cmdbuf; + struct { + /** + * @target.handle: + * + * Handle to the GEM object to be relocated. + */ + __u32 handle; + + /** + * @target.offset: + * + * Offset, in bytes, into the target GEM object at which the + * relocated data starts. + */ + __u32 offset; + } target; + + /** + * @shift: + * + * The number of bits by which to shift relocated addresses. + */ + __u32 shift; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_waitchk - wait check structure + */ +struct drm_tegra_waitchk { + /** + * @handle: + * + * Handle to the GEM object containing a command stream on which to + * perform the wait check. + */ + __u32 handle; + + /** + * @offset: + * + * Offset, in bytes, of the location in the command stream to perform + * the wait check on. + */ + __u32 offset; + + /** + * @syncpt: + * + * ID of the syncpoint to wait check. + */ + __u32 syncpt; + + /** + * @thresh: + * + * Threshold value for which to check. + */ + __u32 thresh; +}; + +/** + * struct drm_tegra_submit - job submission structure + */ +struct drm_tegra_submit { + /** + * @context: + * + * The application context identifying the channel to use for the + * execution of this job. + */ + __u64 context; + + /** + * @num_syncpts: + * + * The number of syncpoints operated on by this job. This defines the + * length of the array pointed to by @syncpts. + */ + __u32 num_syncpts; + + /** + * @num_cmdbufs: + * + * The number of command buffers to execute as part of this job. This + * defines the length of the array pointed to by @cmdbufs. + */ + __u32 num_cmdbufs; + + /** + * @num_relocs: + * + * The number of relocations to perform before executing this job. + * This defines the length of the array pointed to by @relocs. + */ + __u32 num_relocs; + + /** + * @num_waitchks: + * + * The number of wait checks to perform as part of this job. This + * defines the length of the array pointed to by @waitchks. + */ + __u32 num_waitchks; + + /** + * @waitchk_mask: + * + * Bitmask of valid wait checks. + */ + __u32 waitchk_mask; + + /** + * @timeout: + * + * Timeout, in milliseconds, before this job is cancelled. + */ + __u32 timeout; + + /** + * @syncpts: + * + * A pointer to an array of &struct drm_tegra_syncpt structures that + * specify the syncpoint operations performed as part of this job. + * The number of elements in the array must be equal to the value + * given by @num_syncpts. + */ + __u64 syncpts; + + /** + * @cmdbufs: + * + * A pointer to an array of &struct drm_tegra_cmdbuf structures that + * define the command buffers to execute as part of this job. The + * number of elements in the array must be equal to the value given + * by @num_syncpts. + */ + __u64 cmdbufs; + + /** + * @relocs: + * + * A pointer to an array of &struct drm_tegra_reloc structures that + * specify the relocations that need to be performed before executing + * this job. The number of elements in the array must be equal to the + * value given by @num_relocs. + */ + __u64 relocs; + + /** + * @waitchks: + * + * A pointer to an array of &struct drm_tegra_waitchk structures that + * specify the wait checks to be performed while executing this job. + * The number of elements in the array must be equal to the value + * given by @num_waitchks. + */ + __u64 waitchks; + + /** + * @fence: + * + * The threshold of the syncpoint associated with this job after it + * has been completed. Set by the kernel upon successful completion of + * the IOCTL. This can be used with the DRM_TEGRA_SYNCPT_WAIT IOCTL to + * wait for this job to be finished. + */ + __u32 fence; + + /** + * @reserved: + * + * This field is reserved for future use. Must be 0. + */ + __u32 reserved[5]; +}; + +#define DRM_TEGRA_GEM_TILING_MODE_PITCH 0 +#define DRM_TEGRA_GEM_TILING_MODE_TILED 1 +#define DRM_TEGRA_GEM_TILING_MODE_BLOCK 2 + +/** + * struct drm_tegra_gem_set_tiling - parameters for the set tiling IOCTL + */ +struct drm_tegra_gem_set_tiling { + /** + * @handle: + * + * Handle to the GEM object for which to set the tiling parameters. + */ + __u32 handle; + + /** + * @mode: + * + * The tiling mode to set. Must be one of: + * + * DRM_TEGRA_GEM_TILING_MODE_PITCH + * pitch linear format + * + * DRM_TEGRA_GEM_TILING_MODE_TILED + * 16x16 tiling format + * + * DRM_TEGRA_GEM_TILING_MODE_BLOCK + * 16Bx2 tiling format + */ + __u32 mode; + + /** + * @value: + * + * The value to set for the tiling mode parameter. + */ + __u32 value; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_gem_get_tiling - parameters for the get tiling IOCTL + */ +struct drm_tegra_gem_get_tiling { + /** + * @handle: + * + * Handle to the GEM object for which to query the tiling parameters. + */ + __u32 handle; + + /** + * @mode: + * + * The tiling mode currently associated with the GEM object. Set by + * the kernel upon successful completion of the IOCTL. + */ + __u32 mode; + + /** + * @value: + * + * The tiling mode parameter currently associated with the GEM object. + * Set by the kernel upon successful completion of the IOCTL. + */ + __u32 value; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +#define DRM_TEGRA_GEM_BOTTOM_UP (1 << 0) +#define DRM_TEGRA_GEM_FLAGS (DRM_TEGRA_GEM_BOTTOM_UP) + +/** + * struct drm_tegra_gem_set_flags - parameters for the set flags IOCTL + */ +struct drm_tegra_gem_set_flags { + /** + * @handle: + * + * Handle to the GEM object for which to set the flags. + */ + __u32 handle; + + /** + * @flags: + * + * The flags to set for the GEM object. + */ + __u32 flags; +}; + +/** + * struct drm_tegra_gem_get_flags - parameters for the get flags IOCTL + */ +struct drm_tegra_gem_get_flags { + /** + * @handle: + * + * Handle to the GEM object for which to query the flags. + */ + __u32 handle; + + /** + * @flags: + * + * The flags currently associated with the GEM object. Set by the + * kernel upon successful completion of the IOCTL. + */ + __u32 flags; +}; + +#define DRM_TEGRA_GEM_CREATE 0x00 +#define DRM_TEGRA_GEM_MMAP 0x01 +#define DRM_TEGRA_SYNCPT_READ 0x02 +#define DRM_TEGRA_SYNCPT_INCR 0x03 +#define DRM_TEGRA_SYNCPT_WAIT 0x04 +#define DRM_TEGRA_OPEN_CHANNEL 0x05 +#define DRM_TEGRA_CLOSE_CHANNEL 0x06 +#define DRM_TEGRA_GET_SYNCPT 0x07 +#define DRM_TEGRA_SUBMIT 0x08 +#define DRM_TEGRA_GET_SYNCPT_BASE 0x09 +#define DRM_TEGRA_GEM_SET_TILING 0x0a +#define DRM_TEGRA_GEM_GET_TILING 0x0b +#define DRM_TEGRA_GEM_SET_FLAGS 0x0c +#define DRM_TEGRA_GEM_GET_FLAGS 0x0d + +#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create) +#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap) +#define DRM_IOCTL_TEGRA_SYNCPT_READ DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_READ, struct drm_tegra_syncpt_read) +#define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr) +#define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait) +#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel) +#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_close_channel) +#define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt) +#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit) +#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base) +#define DRM_IOCTL_TEGRA_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_TILING, struct drm_tegra_gem_set_tiling) +#define DRM_IOCTL_TEGRA_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_TILING, struct drm_tegra_gem_get_tiling) +#define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags) +#define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/libdrm/vc4_drm.h b/libs/common/drm/include/libdrm/vc4_drm.h new file mode 100644 index 0000000..31f50de --- /dev/null +++ b/libs/common/drm/include/libdrm/vc4_drm.h @@ -0,0 +1,442 @@ +/* + * Copyright © 2014-2015 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _VC4_DRM_H_ +#define _VC4_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_VC4_SUBMIT_CL 0x00 +#define DRM_VC4_WAIT_SEQNO 0x01 +#define DRM_VC4_WAIT_BO 0x02 +#define DRM_VC4_CREATE_BO 0x03 +#define DRM_VC4_MMAP_BO 0x04 +#define DRM_VC4_CREATE_SHADER_BO 0x05 +#define DRM_VC4_GET_HANG_STATE 0x06 +#define DRM_VC4_GET_PARAM 0x07 +#define DRM_VC4_SET_TILING 0x08 +#define DRM_VC4_GET_TILING 0x09 +#define DRM_VC4_LABEL_BO 0x0a +#define DRM_VC4_GEM_MADVISE 0x0b +#define DRM_VC4_PERFMON_CREATE 0x0c +#define DRM_VC4_PERFMON_DESTROY 0x0d +#define DRM_VC4_PERFMON_GET_VALUES 0x0e + +#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) +#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) +#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) +#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) +#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) +#define DRM_IOCTL_VC4_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_PARAM, struct drm_vc4_get_param) +#define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) +#define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) +#define DRM_IOCTL_VC4_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_LABEL_BO, struct drm_vc4_label_bo) +#define DRM_IOCTL_VC4_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GEM_MADVISE, struct drm_vc4_gem_madvise) +#define DRM_IOCTL_VC4_PERFMON_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_CREATE, struct drm_vc4_perfmon_create) +#define DRM_IOCTL_VC4_PERFMON_DESTROY DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_DESTROY, struct drm_vc4_perfmon_destroy) +#define DRM_IOCTL_VC4_PERFMON_GET_VALUES DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_GET_VALUES, struct drm_vc4_perfmon_get_values) + +struct drm_vc4_submit_rcl_surface { + __u32 hindex; /* Handle index, or ~0 if not present. */ + __u32 offset; /* Offset to start of buffer. */ + /* + * Bits for either render config (color_write) or load/store packet. + * Bits should all be 0 for MSAA load/stores. + */ + __u16 bits; + +#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) + __u16 flags; +}; + +/** + * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D + * engine. + * + * Drivers typically use GPU BOs to store batchbuffers / command lists and + * their associated state. However, because the VC4 lacks an MMU, we have to + * do validation of memory accesses by the GPU commands. If we were to store + * our commands in BOs, we'd need to do uncached readback from them to do the + * validation process, which is too expensive. Instead, userspace accumulates + * commands and associated state in plain memory, then the kernel copies the + * data to its own address space, and then validates and stores it in a GPU + * BO. + */ +struct drm_vc4_submit_cl { + /* Pointer to the binner command list. + * + * This is the first set of commands executed, which runs the + * coordinate shader to determine where primitives land on the screen, + * then writes out the state updates and draw calls necessary per tile + * to the tile allocation BO. + */ + __u64 bin_cl; + + /* Pointer to the shader records. + * + * Shader records are the structures read by the hardware that contain + * pointers to uniforms, shaders, and vertex attributes. The + * reference to the shader record has enough information to determine + * how many pointers are necessary (fixed number for shaders/uniforms, + * and an attribute count), so those BO indices into bo_handles are + * just stored as __u32s before each shader record passed in. + */ + __u64 shader_rec; + + /* Pointer to uniform data and texture handles for the textures + * referenced by the shader. + * + * For each shader state record, there is a set of uniform data in the + * order referenced by the record (FS, VS, then CS). Each set of + * uniform data has a __u32 index into bo_handles per texture + * sample operation, in the order the QPU_W_TMUn_S writes appear in + * the program. Following the texture BO handle indices is the actual + * uniform data. + * + * The individual uniform state blocks don't have sizes passed in, + * because the kernel has to determine the sizes anyway during shader + * code validation. + */ + __u64 uniforms; + __u64 bo_handles; + + /* Size in bytes of the binner command list. */ + __u32 bin_cl_size; + /* Size in bytes of the set of shader records. */ + __u32 shader_rec_size; + /* Number of shader records. + * + * This could just be computed from the contents of shader_records and + * the address bits of references to them from the bin CL, but it + * keeps the kernel from having to resize some allocations it makes. + */ + __u32 shader_rec_count; + /* Size in bytes of the uniform state. */ + __u32 uniforms_size; + + /* Number of BO handles passed in (size is that times 4). */ + __u32 bo_handle_count; + + /* RCL setup: */ + __u16 width; + __u16 height; + __u8 min_x_tile; + __u8 min_y_tile; + __u8 max_x_tile; + __u8 max_y_tile; + struct drm_vc4_submit_rcl_surface color_read; + struct drm_vc4_submit_rcl_surface color_write; + struct drm_vc4_submit_rcl_surface zs_read; + struct drm_vc4_submit_rcl_surface zs_write; + struct drm_vc4_submit_rcl_surface msaa_color_write; + struct drm_vc4_submit_rcl_surface msaa_zs_write; + __u32 clear_color[2]; + __u32 clear_z; + __u8 clear_s; + + __u32 pad:24; + +#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) +/* By default, the kernel gets to choose the order that the tiles are + * rendered in. If this is set, then the tiles will be rendered in a + * raster order, with the right-to-left vs left-to-right and + * top-to-bottom vs bottom-to-top dictated by + * VC4_SUBMIT_CL_RCL_ORDER_INCREASING_*. This allows overlapping + * blits to be implemented using the 3D engine. + */ +#define VC4_SUBMIT_CL_FIXED_RCL_ORDER (1 << 1) +#define VC4_SUBMIT_CL_RCL_ORDER_INCREASING_X (1 << 2) +#define VC4_SUBMIT_CL_RCL_ORDER_INCREASING_Y (1 << 3) + __u32 flags; + + /* Returned value of the seqno of this render job (for the + * wait ioctl). + */ + __u64 seqno; + + /* ID of the perfmon to attach to this job. 0 means no perfmon. */ + __u32 perfmonid; + + /* Syncobj handle to wait on. If set, processing of this render job + * will not start until the syncobj is signaled. 0 means ignore. + */ + __u32 in_sync; + + /* Syncobj handle to export fence to. If set, the fence in the syncobj + * will be replaced with a fence that signals upon completion of this + * render job. 0 means ignore. + */ + __u32 out_sync; + + __u32 pad2; +}; + +/** + * struct drm_vc4_wait_seqno - ioctl argument for waiting for + * DRM_VC4_SUBMIT_CL completion using its returned seqno. + * + * timeout_ns is the timeout in nanoseconds, where "0" means "don't + * block, just return the status." + */ +struct drm_vc4_wait_seqno { + __u64 seqno; + __u64 timeout_ns; +}; + +/** + * struct drm_vc4_wait_bo - ioctl argument for waiting for + * completion of the last DRM_VC4_SUBMIT_CL on a BO. + * + * This is useful for cases where multiple processes might be + * rendering to a BO and you want to wait for all rendering to be + * completed. + */ +struct drm_vc4_wait_bo { + __u32 handle; + __u32 pad; + __u64 timeout_ns; +}; + +/** + * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_create_bo { + __u32 size; + __u32 flags; + /** Returned GEM handle for the BO. */ + __u32 handle; + __u32 pad; +}; + +/** + * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. + * + * This doesn't actually perform an mmap. Instead, it returns the + * offset you need to use in an mmap on the DRM device node. This + * means that tools like valgrind end up knowing about the mapped + * memory. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_mmap_bo { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 flags; + /** offset into the drm node to use for subsequent mmap call. */ + __u64 offset; +}; + +/** + * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 + * shader BOs. + * + * Since allowing a shader to be overwritten while it's also being + * executed from would allow privlege escalation, shaders must be + * created using this ioctl, and they can't be mmapped later. + */ +struct drm_vc4_create_shader_bo { + /* Size of the data argument. */ + __u32 size; + /* Flags, currently must be 0. */ + __u32 flags; + + /* Pointer to the data. */ + __u64 data; + + /** Returned GEM handle for the BO. */ + __u32 handle; + /* Pad, must be 0. */ + __u32 pad; +}; + +struct drm_vc4_get_hang_state_bo { + __u32 handle; + __u32 paddr; + __u32 size; + __u32 pad; +}; + +/** + * struct drm_vc4_hang_state - ioctl argument for collecting state + * from a GPU hang for analysis. +*/ +struct drm_vc4_get_hang_state { + /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ + __u64 bo; + /** + * On input, the size of the bo array. Output is the number + * of bos to be returned. + */ + __u32 bo_count; + + __u32 start_bin, start_render; + + __u32 ct0ca, ct0ea; + __u32 ct1ca, ct1ea; + __u32 ct0cs, ct1cs; + __u32 ct0ra0, ct1ra0; + + __u32 bpca, bpcs; + __u32 bpoa, bpos; + + __u32 vpmbase; + + __u32 dbge; + __u32 fdbgo; + __u32 fdbgb; + __u32 fdbgr; + __u32 fdbgs; + __u32 errstat; + + /* Pad that we may save more registers into in the future. */ + __u32 pad[16]; +}; + +#define DRM_VC4_PARAM_V3D_IDENT0 0 +#define DRM_VC4_PARAM_V3D_IDENT1 1 +#define DRM_VC4_PARAM_V3D_IDENT2 2 +#define DRM_VC4_PARAM_SUPPORTS_BRANCHES 3 +#define DRM_VC4_PARAM_SUPPORTS_ETC1 4 +#define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 +#define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 +#define DRM_VC4_PARAM_SUPPORTS_MADVISE 7 +#define DRM_VC4_PARAM_SUPPORTS_PERFMON 8 + +struct drm_vc4_get_param { + __u32 param; + __u32 pad; + __u64 value; +}; + +struct drm_vc4_get_tiling { + __u32 handle; + __u32 flags; + __u64 modifier; +}; + +struct drm_vc4_set_tiling { + __u32 handle; + __u32 flags; + __u64 modifier; +}; + +/** + * struct drm_vc4_label_bo - Attach a name to a BO for debug purposes. + */ +struct drm_vc4_label_bo { + __u32 handle; + __u32 len; + __u64 name; +}; + +/* + * States prefixed with '__' are internal states and cannot be passed to the + * DRM_IOCTL_VC4_GEM_MADVISE ioctl. + */ +#define VC4_MADV_WILLNEED 0 +#define VC4_MADV_DONTNEED 1 +#define __VC4_MADV_PURGED 2 +#define __VC4_MADV_NOTSUPP 3 + +struct drm_vc4_gem_madvise { + __u32 handle; + __u32 madv; + __u32 retained; + __u32 pad; +}; + +enum { + VC4_PERFCNT_FEP_VALID_PRIMS_NO_RENDER, + VC4_PERFCNT_FEP_VALID_PRIMS_RENDER, + VC4_PERFCNT_FEP_CLIPPED_QUADS, + VC4_PERFCNT_FEP_VALID_QUADS, + VC4_PERFCNT_TLB_QUADS_NOT_PASSING_STENCIL, + VC4_PERFCNT_TLB_QUADS_NOT_PASSING_Z_AND_STENCIL, + VC4_PERFCNT_TLB_QUADS_PASSING_Z_AND_STENCIL, + VC4_PERFCNT_TLB_QUADS_ZERO_COVERAGE, + VC4_PERFCNT_TLB_QUADS_NON_ZERO_COVERAGE, + VC4_PERFCNT_TLB_QUADS_WRITTEN_TO_COLOR_BUF, + VC4_PERFCNT_PLB_PRIMS_OUTSIDE_VIEWPORT, + VC4_PERFCNT_PLB_PRIMS_NEED_CLIPPING, + VC4_PERFCNT_PSE_PRIMS_REVERSED, + VC4_PERFCNT_QPU_TOTAL_IDLE_CYCLES, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_VERTEX_COORD_SHADING, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_FRAGMENT_SHADING, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_EXEC_VALID_INST, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_TMUS, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_SCOREBOARD, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_VARYINGS, + VC4_PERFCNT_QPU_TOTAL_INST_CACHE_HIT, + VC4_PERFCNT_QPU_TOTAL_INST_CACHE_MISS, + VC4_PERFCNT_QPU_TOTAL_UNIFORM_CACHE_HIT, + VC4_PERFCNT_QPU_TOTAL_UNIFORM_CACHE_MISS, + VC4_PERFCNT_TMU_TOTAL_TEXT_QUADS_PROCESSED, + VC4_PERFCNT_TMU_TOTAL_TEXT_CACHE_MISS, + VC4_PERFCNT_VPM_TOTAL_CLK_CYCLES_VDW_STALLED, + VC4_PERFCNT_VPM_TOTAL_CLK_CYCLES_VCD_STALLED, + VC4_PERFCNT_L2C_TOTAL_L2_CACHE_HIT, + VC4_PERFCNT_L2C_TOTAL_L2_CACHE_MISS, + VC4_PERFCNT_NUM_EVENTS, +}; + +#define DRM_VC4_MAX_PERF_COUNTERS 16 + +struct drm_vc4_perfmon_create { + __u32 id; + __u32 ncounters; + __u8 events[DRM_VC4_MAX_PERF_COUNTERS]; +}; + +struct drm_vc4_perfmon_destroy { + __u32 id; +}; + +/* + * Returns the values of the performance counters tracked by this + * perfmon (as an array of ncounters u64 values). + * + * No implicit synchronization is performed, so the user has to + * guarantee that any jobs using this perfmon have already been + * completed (probably by blocking on the seqno returned by the + * last exec that used the perfmon). + */ +struct drm_vc4_perfmon_get_values { + __u32 id; + __u64 values_ptr; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VC4_DRM_H_ */ diff --git a/libs/common/drm/include/libdrm/via_drm.h b/libs/common/drm/include/libdrm/via_drm.h new file mode 100644 index 0000000..8b69e81 --- /dev/null +++ b/libs/common/drm/include/libdrm/via_drm.h @@ -0,0 +1,283 @@ +/* + * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef _VIA_DRM_H_ +#define _VIA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _VIA_DEFINES_ +#define _VIA_DEFINES_ + +#include "via_drmclient.h" + +#define VIA_NR_SAREA_CLIPRECTS 8 +#define VIA_NR_XVMC_PORTS 10 +#define VIA_NR_XVMC_LOCKS 5 +#define VIA_MAX_CACHELINE_SIZE 64 +#define XVMCLOCKPTR(saPriv,lockNo) \ + ((__volatile__ struct drm_hw_lock *)(((((unsigned long) (saPriv)->XvMCLockArea) + \ + (VIA_MAX_CACHELINE_SIZE - 1)) & \ + ~(VIA_MAX_CACHELINE_SIZE - 1)) + \ + VIA_MAX_CACHELINE_SIZE*(lockNo))) + +/* Each region is a minimum of 64k, and there are at most 64 of them. + */ +#define VIA_NR_TEX_REGIONS 64 +#define VIA_LOG_MIN_TEX_REGION_SIZE 16 +#endif + +#define VIA_UPLOAD_TEX0IMAGE 0x1 /* handled clientside */ +#define VIA_UPLOAD_TEX1IMAGE 0x2 /* handled clientside */ +#define VIA_UPLOAD_CTX 0x4 +#define VIA_UPLOAD_BUFFERS 0x8 +#define VIA_UPLOAD_TEX0 0x10 +#define VIA_UPLOAD_TEX1 0x20 +#define VIA_UPLOAD_CLIPRECTS 0x40 +#define VIA_UPLOAD_ALL 0xff + +/* VIA specific ioctls */ +#define DRM_VIA_ALLOCMEM 0x00 +#define DRM_VIA_FREEMEM 0x01 +#define DRM_VIA_AGP_INIT 0x02 +#define DRM_VIA_FB_INIT 0x03 +#define DRM_VIA_MAP_INIT 0x04 +#define DRM_VIA_DEC_FUTEX 0x05 +#define NOT_USED +#define DRM_VIA_DMA_INIT 0x07 +#define DRM_VIA_CMDBUFFER 0x08 +#define DRM_VIA_FLUSH 0x09 +#define DRM_VIA_PCICMD 0x0a +#define DRM_VIA_CMDBUF_SIZE 0x0b +#define NOT_USED +#define DRM_VIA_WAIT_IRQ 0x0d +#define DRM_VIA_DMA_BLIT 0x0e +#define DRM_VIA_BLIT_SYNC 0x0f + +#define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_AGP_INIT, drm_via_agp_t) +#define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) +#define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) +#define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) +#define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) +#define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) +#define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ + drm_via_cmdbuf_size_t) +#define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) +#define DRM_IOCTL_VIA_DMA_BLIT DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_DMA_BLIT, drm_via_dmablit_t) +#define DRM_IOCTL_VIA_BLIT_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_BLIT_SYNC, drm_via_blitsync_t) + +/* Indices into buf.Setup where various bits of state are mirrored per + * context and per buffer. These can be fired at the card as a unit, + * or in a piecewise fashion as required. + */ + +#define VIA_TEX_SETUP_SIZE 8 + +/* Flags for clear ioctl + */ +#define VIA_FRONT 0x1 +#define VIA_BACK 0x2 +#define VIA_DEPTH 0x4 +#define VIA_STENCIL 0x8 +#define VIA_MEM_VIDEO 0 /* matches drm constant */ +#define VIA_MEM_AGP 1 /* matches drm constant */ +#define VIA_MEM_SYSTEM 2 +#define VIA_MEM_MIXED 3 +#define VIA_MEM_UNKNOWN 4 + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_agp_t; + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_fb_t; + +typedef struct { + __u32 context; + __u32 type; + __u32 size; + unsigned long index; + unsigned long offset; +} drm_via_mem_t; + +typedef struct _drm_via_init { + enum { + VIA_INIT_MAP = 0x01, + VIA_CLEANUP_MAP = 0x02 + } func; + + unsigned long sarea_priv_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long agpAddr; +} drm_via_init_t; + +typedef struct _drm_via_futex { + enum { + VIA_FUTEX_WAIT = 0x00, + VIA_FUTEX_WAKE = 0X01 + } func; + __u32 ms; + __u32 lock; + __u32 val; +} drm_via_futex_t; + +typedef struct _drm_via_dma_init { + enum { + VIA_INIT_DMA = 0x01, + VIA_CLEANUP_DMA = 0x02, + VIA_DMA_INITIALIZED = 0x03 + } func; + + unsigned long offset; + unsigned long size; + unsigned long reg_pause_addr; +} drm_via_dma_init_t; + +typedef struct _drm_via_cmdbuffer { + char *buf; + unsigned long size; +} drm_via_cmdbuffer_t; + +/* Warning: If you change the SAREA structure you must change the Xserver + * structure as well */ + +typedef struct _drm_via_tex_region { + unsigned char next, prev; /* indices to form a circular LRU */ + unsigned char inUse; /* owned by a client, or free? */ + int age; /* tracked by clients to update local LRU's */ +} drm_via_tex_region_t; + +typedef struct _drm_via_sarea { + unsigned int dirty; + unsigned int nbox; + struct drm_clip_rect boxes[VIA_NR_SAREA_CLIPRECTS]; + drm_via_tex_region_t texList[VIA_NR_TEX_REGIONS + 1]; + int texAge; /* last time texture was uploaded */ + int ctxOwner; /* last context to upload state */ + int vertexPrim; + + /* + * Below is for XvMC. + * We want the lock integers alone on, and aligned to, a cache line. + * Therefore this somewhat strange construct. + */ + + char XvMCLockArea[VIA_MAX_CACHELINE_SIZE * (VIA_NR_XVMC_LOCKS + 1)]; + + unsigned int XvMCDisplaying[VIA_NR_XVMC_PORTS]; + unsigned int XvMCSubPicOn[VIA_NR_XVMC_PORTS]; + unsigned int XvMCCtxNoGrabbed; /* Last context to hold decoder */ + + /* Used by the 3d driver only at this point, for pageflipping: + */ + unsigned int pfCurrentOffset; +} drm_via_sarea_t; + +typedef struct _drm_via_cmdbuf_size { + enum { + VIA_CMDBUF_SPACE = 0x01, + VIA_CMDBUF_LAG = 0x02 + } func; + int wait; + __u32 size; +} drm_via_cmdbuf_size_t; + +typedef enum { + VIA_IRQ_ABSOLUTE = 0x0, + VIA_IRQ_RELATIVE = 0x1, + VIA_IRQ_SIGNAL = 0x10000000, + VIA_IRQ_FORCE_SEQUENCE = 0x20000000 +} via_irq_seq_type_t; + +#define VIA_IRQ_FLAGS_MASK 0xF0000000 + +enum drm_via_irqs { + drm_via_irq_hqv0 = 0, + drm_via_irq_hqv1, + drm_via_irq_dma0_dd, + drm_via_irq_dma0_td, + drm_via_irq_dma1_dd, + drm_via_irq_dma1_td, + drm_via_irq_num +}; + +struct drm_via_wait_irq_request { + unsigned irq; + via_irq_seq_type_t type; + __u32 sequence; + __u32 signal; +}; + +typedef union drm_via_irqwait { + struct drm_via_wait_irq_request request; + struct drm_wait_vblank_reply reply; +} drm_via_irqwait_t; + +typedef struct drm_via_blitsync { + __u32 sync_handle; + unsigned engine; +} drm_via_blitsync_t; + +/* - * Below,"flags" is currently unused but will be used for possible future + * extensions like kernel space bounce buffers for bad alignments and + * blit engine busy-wait polling for better latency in the absence of + * interrupts. + */ + +typedef struct drm_via_dmablit { + __u32 num_lines; + __u32 line_length; + + __u32 fb_addr; + __u32 fb_stride; + + unsigned char *mem_addr; + __u32 mem_stride; + + __u32 flags; + int to_fb; + + drm_via_blitsync_t sync; +} drm_via_dmablit_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VIA_DRM_H_ */ diff --git a/libs/common/drm/include/libdrm/virtgpu_drm.h b/libs/common/drm/include/libdrm/virtgpu_drm.h new file mode 100644 index 0000000..f06a789 --- /dev/null +++ b/libs/common/drm/include/libdrm/virtgpu_drm.h @@ -0,0 +1,182 @@ +/* + * Copyright 2013 Red Hat + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VIRTGPU_DRM_H +#define VIRTGPU_DRM_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + * + * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel + * compatibility Keep fields aligned to their size + */ + +#define DRM_VIRTGPU_MAP 0x01 +#define DRM_VIRTGPU_EXECBUFFER 0x02 +#define DRM_VIRTGPU_GETPARAM 0x03 +#define DRM_VIRTGPU_RESOURCE_CREATE 0x04 +#define DRM_VIRTGPU_RESOURCE_INFO 0x05 +#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06 +#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07 +#define DRM_VIRTGPU_WAIT 0x08 +#define DRM_VIRTGPU_GET_CAPS 0x09 + +#define VIRTGPU_EXECBUF_FENCE_FD_IN 0x01 +#define VIRTGPU_EXECBUF_FENCE_FD_OUT 0x02 +#define VIRTGPU_EXECBUF_FLAGS (\ + VIRTGPU_EXECBUF_FENCE_FD_IN |\ + VIRTGPU_EXECBUF_FENCE_FD_OUT |\ + 0) + +struct drm_virtgpu_map { + __u64 offset; /* use for mmap system call */ + __u32 handle; + __u32 pad; +}; + +struct drm_virtgpu_execbuffer { + __u32 flags; + __u32 size; + __u64 command; /* void* */ + __u64 bo_handles; + __u32 num_bo_handles; + __s32 fence_fd; /* in/out fence fd (see VIRTGPU_EXECBUF_FENCE_FD_IN/OUT) */ +}; + +#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */ +#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */ + +struct drm_virtgpu_getparam { + __u64 param; + __u64 value; +}; + +/* NO_BO flags? NO resource flag? */ +/* resource flag for y_0_top */ +struct drm_virtgpu_resource_create { + __u32 target; + __u32 format; + __u32 bind; + __u32 width; + __u32 height; + __u32 depth; + __u32 array_size; + __u32 last_level; + __u32 nr_samples; + __u32 flags; + __u32 bo_handle; /* if this is set - recreate a new resource attached to this bo ? */ + __u32 res_handle; /* returned by kernel */ + __u32 size; /* validate transfer in the host */ + __u32 stride; /* validate transfer in the host */ +}; + +struct drm_virtgpu_resource_info { + __u32 bo_handle; + __u32 res_handle; + __u32 size; + __u32 stride; +}; + +struct drm_virtgpu_3d_box { + __u32 x; + __u32 y; + __u32 z; + __u32 w; + __u32 h; + __u32 d; +}; + +struct drm_virtgpu_3d_transfer_to_host { + __u32 bo_handle; + struct drm_virtgpu_3d_box box; + __u32 level; + __u32 offset; +}; + +struct drm_virtgpu_3d_transfer_from_host { + __u32 bo_handle; + struct drm_virtgpu_3d_box box; + __u32 level; + __u32 offset; +}; + +#define VIRTGPU_WAIT_NOWAIT 1 /* like it */ +struct drm_virtgpu_3d_wait { + __u32 handle; /* 0 is an invalid handle */ + __u32 flags; +}; + +struct drm_virtgpu_get_caps { + __u32 cap_set_id; + __u32 cap_set_ver; + __u64 addr; + __u32 size; + __u32 pad; +}; + +#define DRM_IOCTL_VIRTGPU_MAP \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map) + +#define DRM_IOCTL_VIRTGPU_EXECBUFFER \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\ + struct drm_virtgpu_execbuffer) + +#define DRM_IOCTL_VIRTGPU_GETPARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\ + struct drm_virtgpu_getparam) + +#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE, \ + struct drm_virtgpu_resource_create) + +#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \ + struct drm_virtgpu_resource_info) + +#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST, \ + struct drm_virtgpu_3d_transfer_from_host) + +#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST, \ + struct drm_virtgpu_3d_transfer_to_host) + +#define DRM_IOCTL_VIRTGPU_WAIT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT, \ + struct drm_virtgpu_3d_wait) + +#define DRM_IOCTL_VIRTGPU_GET_CAPS \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \ + struct drm_virtgpu_get_caps) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/tool.h b/libs/common/drm/include/tool.h new file mode 100644 index 0000000..c7b334d --- /dev/null +++ b/libs/common/drm/include/tool.h @@ -0,0 +1,145 @@ +/* + + */ + +#ifndef TOOL_H_ +#define TOOL_H_ +//ڴ˴ͷļ +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +#ifdef __cplusplus +extern "C" { +#endif +#define YUV420 0 +#define YUV422 YUV420 + 1 +#define YUV444 YUV422 + 1 +/************************************************************* +Function: ReadBmpFile +Description: ȡbmpͼڴ +Input: pFilePathbmp· + pDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgrrgbֵΪ8bit + widthͼ + heightͼ +Output: +*************************************************************/ +extern void ReadBmpFile(char *pFilePath, unsigned char *pData, int & width, int & height); +/************************************************************* +Function: SaveBmpFile +Description: rgbͼݱΪbmp +Input: pFilePathbmp· + pDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgrrgbֵΪ8bit + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveBmpFile(char *pFilePath, unsigned char *pData, int width, int height); +/************************************************************* +Function: SaveRaw +Description: rawͼ +Input: pSavePathraw· + pDataraw + widthrawͼ + heightrawͼ +Output: +*************************************************************/ +extern void SaveRaw(char *pSavePath, short *pRawData, int width, int height); + +extern void SaveRaw32bit(char *pSavePath, long *pRawData, int width, int height); + +/************************************************************* +Function: SaveBmpFile2 +Description: λ8bitbmpͼ +Input: pFilePathbmp· + widthͼ + heightͼ + bitValueͼλ + pRGBDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgr +Output: +*************************************************************/ +extern void SaveBmpFile2(char *pFilePath, int width, int height, int bitValue, short *pRGBData); + +/************************************************************* +Function: SaveYUVData +Description: 8bit YUVͼ +Input: pSavePath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData(char *pSavePath, unsigned char *pData, int width, int height); + + + +/************************************************************* +Function: SaveYUVData2 +Description: λ8bitYUVͼ +Input: pSavePath· + pDatayuvݣλ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData2(char *pSavePath, short *pData, int width, int height, int bitValue); +/************************************************************* +Function: SaveYUVData1 +Description: 8bit YUV420ͼ +Input: pSavePath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData1(char *pSavePath, unsigned char *pData, int width, int height, int fmt); +/************************************************************* +Function: ReadYUVData1 +Description: ȡ8bit YUV420ͼ +Input: pReadPath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void ReadYUVData1(char *pReadPath, unsigned char *pData, int width, int height, int fmt); +/************************************************************* +Function: Yuvfmtconv +Description: yuv fmt conversion.444 420 422 to 444 420 422 +Input: pDatain 뻺 + pDataout + width + height + fmt_in ʽ + fmt_out ʽ +Output: +*************************************************************/ +extern void Yuvfmtconv(void *pDatain, void *pDataout, int width, int height, int fmt_in, int fmt_out, int size); +/************************************************************* +Function: Yuvbitstochar +Description: save yuv to 8 bitdepth +Input: pDatain 뻺 + pDataout + size yuv + height λ +Output: +*************************************************************/ +extern void Yuvbitstochar(short *pDatain, unsigned char *pDataout, int size, int bitdepth); + +/************************************************************* +Function: SaveCfaBmp +Description: rawcfaͼ +Input: pRawDatarawͼ + widthrawͼ + heightrawͼߣ + bayerPatternbayer patternʽȡֵΧ[03] + bitValuerawλ +Output: +*************************************************************/ +extern void SaveCfaBmp(char *pFilePath, short *pRawData, int width, int height, int bayerPattern, int bitValue); + +#ifdef __cplusplus +} +#endif + +#endif // TOOL_H_ diff --git a/libs/common/drm/include/xf86drm.h b/libs/common/drm/include/xf86drm.h new file mode 100644 index 0000000..d86428a --- /dev/null +++ b/libs/common/drm/include/xf86drm.h @@ -0,0 +1,930 @@ +/** + * \file xf86drm.h + * OS-independent header for DRM user-level library interface. + * + * \author Rickard E. (Rik) Faith + */ + +/* + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _XF86DRM_H_ +#define _XF86DRM_H_ + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef DRM_MAX_MINOR +#define DRM_MAX_MINOR 16 +#endif + +#if defined(__linux__) + +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IOC_VOID _IOC_NONE +#define DRM_IOC_READ _IOC_READ +#define DRM_IOC_WRITE _IOC_WRITE +#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#else /* One of the *BSDs */ + +#include +#define DRM_IOCTL_NR(n) ((n) & 0xff) +#define DRM_IOC_VOID IOC_VOID +#define DRM_IOC_READ IOC_OUT +#define DRM_IOC_WRITE IOC_IN +#define DRM_IOC_READWRITE IOC_INOUT +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#endif + + /* Defaults, if nothing set in xf86config */ +#define DRM_DEV_UID 0 +#define DRM_DEV_GID 0 +/* Default /dev/dri directory permissions 0755 */ +#define DRM_DEV_DIRMODE \ + (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) +#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) + +#ifdef __OpenBSD__ +#define DRM_DIR_NAME "/dev" +#define DRM_PRIMARY_MINOR_NAME "drm" +#define DRM_CONTROL_MINOR_NAME "drmC" +#define DRM_RENDER_MINOR_NAME "drmR" +#else +#define DRM_DIR_NAME "/dev/dri" +#define DRM_PRIMARY_MINOR_NAME "card" +#define DRM_CONTROL_MINOR_NAME "controlD" +#define DRM_RENDER_MINOR_NAME "renderD" +#define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */ +#endif + +#define DRM_DEV_NAME "%s/" DRM_PRIMARY_MINOR_NAME "%d" +#define DRM_CONTROL_DEV_NAME "%s/" DRM_CONTROL_MINOR_NAME "%d" +#define DRM_RENDER_DEV_NAME "%s/" DRM_RENDER_MINOR_NAME "%d" + +#define DRM_NODE_NAME_MAX \ + (sizeof(DRM_DIR_NAME) + 1 /* slash */ \ + + MAX3(sizeof(DRM_PRIMARY_MINOR_NAME), \ + sizeof(DRM_CONTROL_MINOR_NAME), \ + sizeof(DRM_RENDER_MINOR_NAME)) \ + + sizeof("144") /* highest possible node number */ \ + + 1) /* NULL-terminator */ + +#define DRM_ERR_NO_DEVICE (-1001) +#define DRM_ERR_NO_ACCESS (-1002) +#define DRM_ERR_NOT_ROOT (-1003) +#define DRM_ERR_INVALID (-1004) +#define DRM_ERR_NO_FD (-1005) + +#define DRM_AGP_NO_HANDLE 0 + +typedef unsigned int drmSize, *drmSizePtr; /**< For mapped regions */ +typedef void *drmAddress, **drmAddressPtr; /**< For mapped regions */ + +#if (__GNUC__ >= 3) +#define DRM_PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a))) +#else +#define DRM_PRINTFLIKE(f, a) +#endif + +typedef struct _drmServerInfo { + int (*debug_print)(const char *format, va_list ap) DRM_PRINTFLIKE(1,0); + int (*load_module)(const char *name); + void (*get_perms)(gid_t *, mode_t *); +} drmServerInfo, *drmServerInfoPtr; + +typedef struct drmHashEntry { + int fd; + void (*f)(int, void *, void *); + void *tagTable; +} drmHashEntry; + +extern int drmIoctl(int fd, unsigned long request, void *arg); +extern void *drmGetHashTable(void); +extern drmHashEntry *drmGetEntry(int fd); + +/** + * Driver version information. + * + * \sa drmGetVersion() and drmSetVersion(). + */ +typedef struct _drmVersion { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + int name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + int date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + int desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ +} drmVersion, *drmVersionPtr; + +typedef struct _drmStats { + unsigned long count; /**< Number of data */ + struct { + unsigned long value; /**< Value from kernel */ + const char *long_format; /**< Suggested format for long_name */ + const char *long_name; /**< Long name for value */ + const char *rate_format; /**< Suggested format for rate_name */ + const char *rate_name; /**< Short name for value per second */ + int isvalue; /**< True if value (vs. counter) */ + const char *mult_names; /**< Multiplier names (e.g., "KGM") */ + int mult; /**< Multiplier value (e.g., 1024) */ + int verbose; /**< Suggest only in verbose output */ + } data[15]; +} drmStatsT; + + + /* All of these enums *MUST* match with the + kernel implementation -- so do *NOT* + change them! (The drmlib implementation + will just copy the flags instead of + translating them.) */ +typedef enum { + DRM_FRAME_BUFFER = 0, /**< WC, no caching, no core dump */ + DRM_REGISTERS = 1, /**< no caching, no core dump */ + DRM_SHM = 2, /**< shared, cached */ + DRM_AGP = 3, /**< AGP/GART */ + DRM_SCATTER_GATHER = 4, /**< PCI scatter/gather */ + DRM_CONSISTENT = 5 /**< PCI consistent */ +} drmMapType; + +typedef enum { + DRM_RESTRICTED = 0x0001, /**< Cannot be mapped to client-virtual */ + DRM_READ_ONLY = 0x0002, /**< Read-only in client-virtual */ + DRM_LOCKED = 0x0004, /**< Physical pages locked */ + DRM_KERNEL = 0x0008, /**< Kernel requires access */ + DRM_WRITE_COMBINING = 0x0010, /**< Use write-combining, if available */ + DRM_CONTAINS_LOCK = 0x0020, /**< SHM page that contains lock */ + DRM_REMOVABLE = 0x0040 /**< Removable mapping */ +} drmMapFlags; + +/** + * \warning These values *MUST* match drm.h + */ +typedef enum { + /** \name Flags for DMA buffer dispatch */ + /*@{*/ + DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note the buffer may not yet have been + * processed by the hardware -- getting a + * hardware lock with the hardware quiescent + * will ensure that the buffer has been + * processed. + */ + DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + /*@}*/ + + /** \name Flags for DMA buffer request */ + /*@{*/ + DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ + /*@}*/ +} drmDMAFlags; + +typedef enum { + DRM_PAGE_ALIGN = 0x01, + DRM_AGP_BUFFER = 0x02, + DRM_SG_BUFFER = 0x04, + DRM_FB_BUFFER = 0x08, + DRM_PCI_BUFFER_RO = 0x10 +} drmBufDescFlags; + +typedef enum { + DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +} drmLockFlags; + +typedef enum { + DRM_CONTEXT_PRESERVED = 0x01, /**< This context is preserved and + never swapped. */ + DRM_CONTEXT_2DONLY = 0x02 /**< This context is for 2D rendering only. */ +} drm_context_tFlags, *drm_context_tFlagsPtr; + +typedef struct _drmBufDesc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ +} drmBufDesc, *drmBufDescPtr; + +typedef struct _drmBufInfo { + int count; /**< Number of buffers described in list */ + drmBufDescPtr list; /**< List of buffer descriptions */ +} drmBufInfo, *drmBufInfoPtr; + +typedef struct _drmBuf { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + drmAddress address; /**< Address */ +} drmBuf, *drmBufPtr; + +/** + * Buffer mapping information. + * + * Used by drmMapBufs() and drmUnmapBufs() to store information about the + * mapped buffers. + */ +typedef struct _drmBufMap { + int count; /**< Number of buffers mapped */ + drmBufPtr list; /**< Buffers */ +} drmBufMap, *drmBufMapPtr; + +typedef struct _drmLock { + volatile unsigned int lock; + char padding[60]; + /* This is big enough for most current (and future?) architectures: + DEC Alpha: 32 bytes + Intel Merced: ? + Intel P5/PPro/PII/PIII: 32 bytes + Intel StrongARM: 32 bytes + Intel i386/i486: 16 bytes + MIPS: 32 bytes (?) + Motorola 68k: 16 bytes + Motorola PowerPC: 32 bytes + Sun SPARC: 32 bytes + */ +} drmLock, *drmLockPtr; + +/** + * Indices here refer to the offset into + * list in drmBufInfo + */ +typedef struct _drmDMAReq { + drm_context_t context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_list; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send, in bytes */ + drmDMAFlags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size of buffers requested */ + int *request_list; /**< Buffer information */ + int *request_sizes; /**< Minimum acceptable sizes */ + int granted_count; /**< Number of buffers granted at this size */ +} drmDMAReq, *drmDMAReqPtr; + +typedef struct _drmRegion { + drm_handle_t handle; + unsigned int offset; + drmSize size; + drmAddress map; +} drmRegion, *drmRegionPtr; + +typedef struct _drmTextureRegion { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; /**< Explicitly pad this out */ + unsigned int age; +} drmTextureRegion, *drmTextureRegionPtr; + + +typedef enum { + DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + /* bits 1-6 are reserved for high crtcs */ + DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, + DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + DRM_VBLANK_SIGNAL = 0x40000000 /* Send signal instead of blocking */ +} drmVBlankSeqType; +#define DRM_VBLANK_HIGH_CRTC_SHIFT 1 + +typedef struct _drmVBlankReq { + drmVBlankSeqType type; + unsigned int sequence; + unsigned long signal; +} drmVBlankReq, *drmVBlankReqPtr; + +typedef struct _drmVBlankReply { + drmVBlankSeqType type; + unsigned int sequence; + long tval_sec; + long tval_usec; +} drmVBlankReply, *drmVBlankReplyPtr; + +typedef union _drmVBlank { + drmVBlankReq request; + drmVBlankReply reply; +} drmVBlank, *drmVBlankPtr; + +typedef struct _drmSetVersion { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +} drmSetVersion, *drmSetVersionPtr; + +#define __drm_dummy_lock(lock) (*(__volatile__ unsigned int *)lock) + +#define DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ + +#if defined(__GNUC__) && (__GNUC__ >= 2) +# if defined(__i386) || defined(__AMD64__) || defined(__x86_64__) || defined(__amd64__) + /* Reflect changes here to drmP.h */ +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + int __dummy; /* Can't mark eax as clobbered */ \ + __asm__ __volatile__( \ + "lock ; cmpxchg %4,%1\n\t" \ + "setnz %0" \ + : "=d" (__ret), \ + "=m" (__drm_dummy_lock(lock)), \ + "=a" (__dummy) \ + : "2" (old), \ + "r" (new)); \ + } while (0) + +#elif defined(__alpha__) + +#define DRM_CAS(lock, old, new, ret) \ + do { \ + int tmp, old32; \ + __asm__ __volatile__( \ + " addl $31, %5, %3\n" \ + "1: ldl_l %0, %2\n" \ + " cmpeq %0, %3, %1\n" \ + " beq %1, 2f\n" \ + " mov %4, %0\n" \ + " stl_c %0, %2\n" \ + " beq %0, 3f\n" \ + " mb\n" \ + "2: cmpeq %1, 0, %1\n" \ + ".subsection 2\n" \ + "3: br 1b\n" \ + ".previous" \ + : "=&r"(tmp), "=&r"(ret), \ + "=m"(__drm_dummy_lock(lock)), \ + "=&r"(old32) \ + : "r"(new), "r"(old) \ + : "memory"); \ + } while (0) + +#elif defined(__sparc__) + +#define DRM_CAS(lock,old,new,__ret) \ +do { register unsigned int __old __asm("o0"); \ + register unsigned int __new __asm("o1"); \ + register volatile unsigned int *__lock __asm("o2"); \ + __old = old; \ + __new = new; \ + __lock = (volatile unsigned int *)lock; \ + __asm__ __volatile__( \ + /*"cas [%2], %3, %0"*/ \ + ".word 0xd3e29008\n\t" \ + /*"membar #StoreStore | #StoreLoad"*/ \ + ".word 0x8143e00a" \ + : "=&r" (__new) \ + : "0" (__new), \ + "r" (__lock), \ + "r" (__old) \ + : "memory"); \ + __ret = (__new != __old); \ +} while(0) + +#elif defined(__ia64__) + +#ifdef __INTEL_COMPILER +/* this currently generates bad code (missing stop bits)... */ +#include + +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + unsigned long __result, __old = (old) & 0xffffffff; \ + __mf(); \ + __result = _InterlockedCompareExchange_acq(&__drm_dummy_lock(lock), (new), __old);\ + __ret = (__result) != (__old); \ +/* __ret = (__sync_val_compare_and_swap(&__drm_dummy_lock(lock), \ + (old), (new)) \ + != (old)); */\ + } while (0) + +#else +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + unsigned int __result, __old = (old); \ + __asm__ __volatile__( \ + "mf\n" \ + "mov ar.ccv=%2\n" \ + ";;\n" \ + "cmpxchg4.acq %0=%1,%3,ar.ccv" \ + : "=r" (__result), "=m" (__drm_dummy_lock(lock)) \ + : "r" ((unsigned long)__old), "r" (new) \ + : "memory"); \ + __ret = (__result) != (__old); \ + } while (0) + +#endif + +#elif defined(__powerpc__) + +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + __asm__ __volatile__( \ + "sync;" \ + "0: lwarx %0,0,%1;" \ + " xor. %0,%3,%0;" \ + " bne 1f;" \ + " stwcx. %2,0,%1;" \ + " bne- 0b;" \ + "1: " \ + "sync;" \ + : "=&r"(__ret) \ + : "r"(lock), "r"(new), "r"(old) \ + : "cr0", "memory"); \ + } while (0) + +# elif defined (__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined (__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ + || defined (__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) \ + || defined (__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7EM__) + #undef DRM_DEV_MODE + #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) + + #define DRM_CAS(lock,old,new,__ret) \ + do { \ + __asm__ __volatile__ ( \ + "1: ldrex %0, [%1]\n" \ + " teq %0, %2\n" \ + " ite eq\n" \ + " strexeq %0, %3, [%1]\n" \ + " movne %0, #1\n" \ + : "=&r" (__ret) \ + : "r" (lock), "r" (old), "r" (new) \ + : "cc","memory"); \ + } while (0) + +#endif /* architecture */ +#endif /* __GNUC__ >= 2 */ + +#ifndef DRM_CAS +#define DRM_CAS(lock,old,new,ret) do { ret=1; } while (0) /* FAST LOCK FAILS */ +#endif + +#if defined(__alpha__) +#define DRM_CAS_RESULT(_result) long _result +#elif defined(__powerpc__) +#define DRM_CAS_RESULT(_result) int _result +#else +#define DRM_CAS_RESULT(_result) char _result +#endif + +#define DRM_LIGHT_LOCK(fd,lock,context) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ + if (__ret) drmGetLock(fd,context,0); \ + } while(0) + + /* This one counts fast locks -- for + benchmarking only. */ +#define DRM_LIGHT_LOCK_COUNT(fd,lock,context,count) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ + if (__ret) drmGetLock(fd,context,0); \ + else ++count; \ + } while(0) + +#define DRM_LOCK(fd,lock,context,flags) \ + do { \ + if (flags) drmGetLock(fd,context,flags); \ + else DRM_LIGHT_LOCK(fd,lock,context); \ + } while(0) + +#define DRM_UNLOCK(fd,lock,context) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,DRM_LOCK_HELD|context,context,__ret); \ + if (__ret) drmUnlock(fd,context); \ + } while(0) + + /* Simple spin locks */ +#define DRM_SPINLOCK(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + do { \ + DRM_CAS(spin,0,val,__ret); \ + if (__ret) while ((spin)->lock); \ + } while (__ret); \ + } while(0) + +#define DRM_SPINLOCK_TAKE(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + int cur; \ + do { \ + cur = (*spin).lock; \ + DRM_CAS(spin,cur,val,__ret); \ + } while (__ret); \ + } while(0) + +#define DRM_SPINLOCK_COUNT(spin,val,count,__ret) \ + do { \ + int __i; \ + __ret = 1; \ + for (__i = 0; __ret && __i < count; __i++) { \ + DRM_CAS(spin,0,val,__ret); \ + if (__ret) for (;__i < count && (spin)->lock; __i++); \ + } \ + } while(0) + +#define DRM_SPINUNLOCK(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + if ((*spin).lock == val) { /* else server stole lock */ \ + do { \ + DRM_CAS(spin,val,0,__ret); \ + } while (__ret); \ + } \ + } while(0) + + + +/* General user-level programmer's API: unprivileged */ +extern int drmAvailable(void); +extern int drmOpen(const char *name, const char *busid); + +#define DRM_NODE_PRIMARY 0 +#define DRM_NODE_CONTROL 1 +#define DRM_NODE_RENDER 2 +#define DRM_NODE_MAX 3 + +extern int drmOpenWithType(const char *name, const char *busid, + int type); + +extern int drmOpenControl(int minor); +extern int drmOpenRender(int minor); +extern int drmClose(int fd); +extern drmVersionPtr drmGetVersion(int fd); +extern drmVersionPtr drmGetLibVersion(int fd); +extern int drmGetCap(int fd, uint64_t capability, uint64_t *value); +extern void drmFreeVersion(drmVersionPtr); +extern int drmGetMagic(int fd, drm_magic_t * magic); +extern char *drmGetBusid(int fd); +extern int drmGetInterruptFromBusID(int fd, int busnum, int devnum, + int funcnum); +extern int drmGetMap(int fd, int idx, drm_handle_t *offset, + drmSize *size, drmMapType *type, + drmMapFlags *flags, drm_handle_t *handle, + int *mtrr); +extern int drmGetClient(int fd, int idx, int *auth, int *pid, + int *uid, unsigned long *magic, + unsigned long *iocs); +extern int drmGetStats(int fd, drmStatsT *stats); +extern int drmSetInterfaceVersion(int fd, drmSetVersion *version); +extern int drmCommandNone(int fd, unsigned long drmCommandIndex); +extern int drmCommandRead(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); +extern int drmCommandWrite(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); +extern int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); + +/* General user-level programmer's API: X server (root) only */ +extern void drmFreeBusid(const char *busid); +extern int drmSetBusid(int fd, const char *busid); +extern int drmAuthMagic(int fd, drm_magic_t magic); +extern int drmAddMap(int fd, + drm_handle_t offset, + drmSize size, + drmMapType type, + drmMapFlags flags, + drm_handle_t * handle); +extern int drmRmMap(int fd, drm_handle_t handle); +extern int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id, + drm_handle_t handle); + +extern int drmAddBufs(int fd, int count, int size, + drmBufDescFlags flags, + int agp_offset); +extern int drmMarkBufs(int fd, double low, double high); +extern int drmCreateContext(int fd, drm_context_t * handle); +extern int drmSetContextFlags(int fd, drm_context_t context, + drm_context_tFlags flags); +extern int drmGetContextFlags(int fd, drm_context_t context, + drm_context_tFlagsPtr flags); +extern int drmAddContextTag(int fd, drm_context_t context, void *tag); +extern int drmDelContextTag(int fd, drm_context_t context); +extern void *drmGetContextTag(int fd, drm_context_t context); +extern drm_context_t * drmGetReservedContextList(int fd, int *count); +extern void drmFreeReservedContextList(drm_context_t *); +extern int drmSwitchToContext(int fd, drm_context_t context); +extern int drmDestroyContext(int fd, drm_context_t handle); +extern int drmCreateDrawable(int fd, drm_drawable_t * handle); +extern int drmDestroyDrawable(int fd, drm_drawable_t handle); +extern int drmUpdateDrawableInfo(int fd, drm_drawable_t handle, + drm_drawable_info_type_t type, + unsigned int num, void *data); +extern int drmCtlInstHandler(int fd, int irq); +extern int drmCtlUninstHandler(int fd); +extern int drmSetClientCap(int fd, uint64_t capability, + uint64_t value); + +extern int drmCrtcGetSequence(int fd, uint32_t crtcId, + uint64_t *sequence, uint64_t *ns); +extern int drmCrtcQueueSequence(int fd, uint32_t crtcId, + uint32_t flags, uint64_t sequence, + uint64_t *sequence_queued, + uint64_t user_data); +/* General user-level programmer's API: authenticated client and/or X */ +extern int drmMap(int fd, + drm_handle_t handle, + drmSize size, + drmAddressPtr address); +extern int drmUnmap(drmAddress address, drmSize size); +extern drmBufInfoPtr drmGetBufInfo(int fd); +extern drmBufMapPtr drmMapBufs(int fd); +extern int drmUnmapBufs(drmBufMapPtr bufs); +extern int drmDMA(int fd, drmDMAReqPtr request); +extern int drmFreeBufs(int fd, int count, int *list); +extern int drmGetLock(int fd, + drm_context_t context, + drmLockFlags flags); +extern int drmUnlock(int fd, drm_context_t context); +extern int drmFinish(int fd, int context, drmLockFlags flags); +extern int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id, + drm_handle_t * handle); + +/* AGP/GART support: X server (root) only */ +extern int drmAgpAcquire(int fd); +extern int drmAgpRelease(int fd); +extern int drmAgpEnable(int fd, unsigned long mode); +extern int drmAgpAlloc(int fd, unsigned long size, + unsigned long type, unsigned long *address, + drm_handle_t *handle); +extern int drmAgpFree(int fd, drm_handle_t handle); +extern int drmAgpBind(int fd, drm_handle_t handle, + unsigned long offset); +extern int drmAgpUnbind(int fd, drm_handle_t handle); + +/* AGP/GART info: authenticated client and/or X */ +extern int drmAgpVersionMajor(int fd); +extern int drmAgpVersionMinor(int fd); +extern unsigned long drmAgpGetMode(int fd); +extern unsigned long drmAgpBase(int fd); /* Physical location */ +extern unsigned long drmAgpSize(int fd); /* Bytes */ +extern unsigned long drmAgpMemoryUsed(int fd); +extern unsigned long drmAgpMemoryAvail(int fd); +extern unsigned int drmAgpVendorId(int fd); +extern unsigned int drmAgpDeviceId(int fd); + +/* PCI scatter/gather support: X server (root) only */ +extern int drmScatterGatherAlloc(int fd, unsigned long size, + drm_handle_t *handle); +extern int drmScatterGatherFree(int fd, drm_handle_t handle); + +extern int drmWaitVBlank(int fd, drmVBlankPtr vbl); + +/* Support routines */ +extern void drmSetServerInfo(drmServerInfoPtr info); +extern int drmError(int err, const char *label); +extern void *drmMalloc(int size); +extern void drmFree(void *pt); + +/* Hash table routines */ +extern void *drmHashCreate(void); +extern int drmHashDestroy(void *t); +extern int drmHashLookup(void *t, unsigned long key, void **value); +extern int drmHashInsert(void *t, unsigned long key, void *value); +extern int drmHashDelete(void *t, unsigned long key); +extern int drmHashFirst(void *t, unsigned long *key, void **value); +extern int drmHashNext(void *t, unsigned long *key, void **value); + +/* PRNG routines */ +extern void *drmRandomCreate(unsigned long seed); +extern int drmRandomDestroy(void *state); +extern unsigned long drmRandom(void *state); +extern double drmRandomDouble(void *state); + +/* Skip list routines */ + +extern void *drmSLCreate(void); +extern int drmSLDestroy(void *l); +extern int drmSLLookup(void *l, unsigned long key, void **value); +extern int drmSLInsert(void *l, unsigned long key, void *value); +extern int drmSLDelete(void *l, unsigned long key); +extern int drmSLNext(void *l, unsigned long *key, void **value); +extern int drmSLFirst(void *l, unsigned long *key, void **value); +extern void drmSLDump(void *l); +extern int drmSLLookupNeighbors(void *l, unsigned long key, + unsigned long *prev_key, void **prev_value, + unsigned long *next_key, void **next_value); + +extern int drmOpenOnce(void *unused, const char *BusID, int *newlyopened); +extern int drmOpenOnceWithType(const char *BusID, int *newlyopened, int type); +extern void drmCloseOnce(int fd); +extern void drmMsg(const char *format, ...) DRM_PRINTFLIKE(1, 2); + +extern int drmSetMaster(int fd); +extern int drmDropMaster(int fd); +extern int drmIsMaster(int fd); + +#define DRM_EVENT_CONTEXT_VERSION 4 + +typedef struct _drmEventContext { + + /* This struct is versioned so we can add more pointers if we + * add more events. */ + int version; + + void (*vblank_handler)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + + void (*page_flip_handler)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + + void (*page_flip_handler2)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + unsigned int crtc_id, + void *user_data); + + void (*sequence_handler)(int fd, + uint64_t sequence, + uint64_t ns, + uint64_t user_data); +} drmEventContext, *drmEventContextPtr; + +extern int drmHandleEvent(int fd, drmEventContextPtr evctx); + +extern char *drmGetDeviceNameFromFd(int fd); + +/* Improved version of drmGetDeviceNameFromFd which attributes for any type of + * device/node - card, control or renderD. + */ +extern char *drmGetDeviceNameFromFd2(int fd); +extern int drmGetNodeTypeFromFd(int fd); + +extern int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd); +extern int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle); + +extern char *drmGetPrimaryDeviceNameFromFd(int fd); +extern char *drmGetRenderDeviceNameFromFd(int fd); + +#define DRM_BUS_PCI 0 +#define DRM_BUS_USB 1 +#define DRM_BUS_PLATFORM 2 +#define DRM_BUS_HOST1X 3 + +typedef struct _drmPciBusInfo { + uint16_t domain; + uint8_t bus; + uint8_t dev; + uint8_t func; +} drmPciBusInfo, *drmPciBusInfoPtr; + +typedef struct _drmPciDeviceInfo { + uint16_t vendor_id; + uint16_t device_id; + uint16_t subvendor_id; + uint16_t subdevice_id; + uint8_t revision_id; +} drmPciDeviceInfo, *drmPciDeviceInfoPtr; + +typedef struct _drmUsbBusInfo { + uint8_t bus; + uint8_t dev; +} drmUsbBusInfo, *drmUsbBusInfoPtr; + +typedef struct _drmUsbDeviceInfo { + uint16_t vendor; + uint16_t product; +} drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; + +#define DRM_PLATFORM_DEVICE_NAME_LEN 512 + +typedef struct _drmPlatformBusInfo { + char fullname[DRM_PLATFORM_DEVICE_NAME_LEN]; +} drmPlatformBusInfo, *drmPlatformBusInfoPtr; + +typedef struct _drmPlatformDeviceInfo { + char **compatible; /* NULL terminated list of compatible strings */ +} drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr; + +#define DRM_HOST1X_DEVICE_NAME_LEN 512 + +typedef struct _drmHost1xBusInfo { + char fullname[DRM_HOST1X_DEVICE_NAME_LEN]; +} drmHost1xBusInfo, *drmHost1xBusInfoPtr; + +typedef struct _drmHost1xDeviceInfo { + char **compatible; /* NULL terminated list of compatible strings */ +} drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr; + +typedef struct _drmDevice { + char **nodes; /* DRM_NODE_MAX sized array */ + int available_nodes; /* DRM_NODE_* bitmask */ + int bustype; + union { + drmPciBusInfoPtr pci; + drmUsbBusInfoPtr usb; + drmPlatformBusInfoPtr platform; + drmHost1xBusInfoPtr host1x; + } businfo; + union { + drmPciDeviceInfoPtr pci; + drmUsbDeviceInfoPtr usb; + drmPlatformDeviceInfoPtr platform; + drmHost1xDeviceInfoPtr host1x; + } deviceinfo; +} drmDevice, *drmDevicePtr; + +extern int drmGetDevice(int fd, drmDevicePtr *device); +extern void drmFreeDevice(drmDevicePtr *device); + +extern int drmGetDevices(drmDevicePtr devices[], int max_devices); +extern void drmFreeDevices(drmDevicePtr devices[], int count); + +#define DRM_DEVICE_GET_PCI_REVISION (1 << 0) +extern int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device); +extern int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices); + +extern int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b); + +extern int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle); +extern int drmSyncobjDestroy(int fd, uint32_t handle); +extern int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd); +extern int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle); + +extern int drmSyncobjImportSyncFile(int fd, uint32_t handle, int sync_file_fd); +extern int drmSyncobjExportSyncFile(int fd, uint32_t handle, int *sync_file_fd); +extern int drmSyncobjWait(int fd, uint32_t *handles, unsigned num_handles, + int64_t timeout_nsec, unsigned flags, + uint32_t *first_signaled); +extern int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t handle_count); +extern int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t handle_count); +extern int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, + uint64_t *points, uint32_t handle_count); +extern int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, + unsigned num_handles, + int64_t timeout_nsec, unsigned flags, + uint32_t *first_signaled); +extern int drmSyncobjQuery(int fd, uint32_t *handles, uint64_t *points, + uint32_t handle_count); +extern int drmSyncobjTransfer(int fd, + uint32_t dst_handle, uint64_t dst_point, + uint32_t src_handle, uint64_t src_point, + uint32_t flags); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/drm/include/xf86drmMode.h b/libs/common/drm/include/xf86drmMode.h new file mode 100644 index 0000000..a32902f --- /dev/null +++ b/libs/common/drm/include/xf86drmMode.h @@ -0,0 +1,551 @@ +/* + * \file xf86drmMode.h + * Header for DRM modesetting interface. + * + * \author Jakob Bornecrantz + * + * \par Acknowledgements: + * Feb 2007, Dave Airlie + */ + +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2007-2008 Dave Airlie + * Copyright (c) 2007-2008 Jakob Bornecrantz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef _XF86DRMMODE_H_ +#define _XF86DRMMODE_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +/* + * This is the interface for modesetting for drm. + * + * In order to use this interface you must include either or another + * header defining uint32_t, int32_t and uint16_t. + * + * It aims to provide a randr1.2 compatible interface for modesettings in the + * kernel, the interface is also meant to be used by libraries like EGL. + * + * More information can be found in randrproto.txt which can be found here: + * http://gitweb.freedesktop.org/?p=xorg/proto/randrproto.git + * + * There are some major differences to be noted. Unlike the randr1.2 proto you + * need to create the memory object of the framebuffer yourself with the ttm + * buffer object interface. This object needs to be pinned. + */ + +/* + * If we pickup an old version of drm.h which doesn't include drm_mode.h + * we should redefine defines. This is so that builds doesn't breaks with + * new libdrm on old kernels. + */ +#ifndef _DRM_MODE_H + +#define DRM_DISPLAY_INFO_LEN 32 +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +/* Video mode flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) +#define DRM_MODE_FLAG_PIXMUX (1<<11) +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) +#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) +#define DRM_MODE_FLAG_3D_NONE (0<<14) +#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) +#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) +#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) +#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NON_GPU 0 +#define DRM_MODE_SCALE_FULLSCREEN 1 +#define DRM_MODE_SCALE_NO_SCALE 2 +#define DRM_MODE_SCALE_ASPECT 3 + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 +#define DRM_MODE_ENCODER_VIRTUAL 5 +#define DRM_MODE_ENCODER_DSI 6 +#define DRM_MODE_ENCODER_DPMST 7 +#define DRM_MODE_ENCODER_DPI 8 + +#define DRM_MODE_SUBCONNECTOR_Automatic 0 +#define DRM_MODE_SUBCONNECTOR_Unknown 0 +#define DRM_MODE_SUBCONNECTOR_DVID 3 +#define DRM_MODE_SUBCONNECTOR_DVIA 4 +#define DRM_MODE_SUBCONNECTOR_Composite 5 +#define DRM_MODE_SUBCONNECTOR_SVIDEO 6 +#define DRM_MODE_SUBCONNECTOR_Component 8 +#define DRM_MODE_SUBCONNECTOR_SCART 9 + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 +#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_eDP 14 +#define DRM_MODE_CONNECTOR_VIRTUAL 15 +#define DRM_MODE_CONNECTOR_DSI 16 +#define DRM_MODE_CONNECTOR_DPI 17 + +#define DRM_MODE_PROP_PENDING (1<<0) +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) + +#define DRM_MODE_CURSOR_BO (1<<0) +#define DRM_MODE_CURSOR_MOVE (1<<1) + +#endif /* _DRM_MODE_H */ + + +/* + * Feature defines + * + * Just because these are defined doesn't mean that the kernel + * can do that feature, its just for new code vs old libdrm. + */ +#define DRM_MODE_FEATURE_KMS 1 +#define DRM_MODE_FEATURE_DIRTYFB 1 + + +typedef struct _drmModeRes { + + int count_fbs; + uint32_t *fbs; + + int count_crtcs; + uint32_t *crtcs; + + int count_connectors; + uint32_t *connectors; + + int count_encoders; + uint32_t *encoders; + + uint32_t min_width, max_width; + uint32_t min_height, max_height; +} drmModeRes, *drmModeResPtr; + +typedef struct _drmModeModeInfo { + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t vrefresh; + + uint32_t flags; + uint32_t type; + char name[DRM_DISPLAY_MODE_LEN]; +} drmModeModeInfo, *drmModeModeInfoPtr; + +typedef struct _drmModeFB { + uint32_t fb_id; + uint32_t width, height; + uint32_t pitch; + uint32_t bpp; + uint32_t depth; + /* driver specific handle */ + uint32_t handle; +} drmModeFB, *drmModeFBPtr; + +typedef struct drm_clip_rect drmModeClip, *drmModeClipPtr; + +typedef struct _drmModePropertyBlob { + uint32_t id; + uint32_t length; + void *data; +} drmModePropertyBlobRes, *drmModePropertyBlobPtr; + +typedef struct _drmModeProperty { + uint32_t prop_id; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + int count_values; + uint64_t *values; /* store the blob lengths */ + int count_enums; + struct drm_mode_property_enum *enums; + int count_blobs; + uint32_t *blob_ids; /* store the blob IDs */ +} drmModePropertyRes, *drmModePropertyPtr; + +static __inline int drm_property_type_is(drmModePropertyPtr property, + uint32_t type) +{ + /* instanceof for props.. handles extended type vs original types: */ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; + return property->flags & type; +} + +typedef struct _drmModeCrtc { + uint32_t crtc_id; + uint32_t buffer_id; /**< FB id to connect to 0 = disconnect */ + + uint32_t x, y; /**< Position on the framebuffer */ + uint32_t width, height; + int mode_valid; + drmModeModeInfo mode; + + int gamma_size; /**< Number of gamma stops */ + +} drmModeCrtc, *drmModeCrtcPtr; + +typedef struct _drmModeEncoder { + uint32_t encoder_id; + uint32_t encoder_type; + uint32_t crtc_id; + uint32_t possible_crtcs; + uint32_t possible_clones; +} drmModeEncoder, *drmModeEncoderPtr; + +typedef enum { + DRM_MODE_CONNECTED = 1, + DRM_MODE_DISCONNECTED = 2, + DRM_MODE_UNKNOWNCONNECTION = 3 +} drmModeConnection; + +typedef enum { + DRM_MODE_SUBPIXEL_UNKNOWN = 1, + DRM_MODE_SUBPIXEL_HORIZONTAL_RGB = 2, + DRM_MODE_SUBPIXEL_HORIZONTAL_BGR = 3, + DRM_MODE_SUBPIXEL_VERTICAL_RGB = 4, + DRM_MODE_SUBPIXEL_VERTICAL_BGR = 5, + DRM_MODE_SUBPIXEL_NONE = 6 +} drmModeSubPixel; + +typedef struct _drmModeConnector { + uint32_t connector_id; + uint32_t encoder_id; /**< Encoder currently connected to */ + uint32_t connector_type; + uint32_t connector_type_id; + drmModeConnection connection; + uint32_t mmWidth, mmHeight; /**< HxW in millimeters */ + drmModeSubPixel subpixel; + + int count_modes; + drmModeModeInfoPtr modes; + + int count_props; + uint32_t *props; /**< List of property ids */ + uint64_t *prop_values; /**< List of property values */ + + int count_encoders; + uint32_t *encoders; /**< List of encoder ids */ +} drmModeConnector, *drmModeConnectorPtr; + +#define DRM_PLANE_TYPE_OVERLAY 0 +#define DRM_PLANE_TYPE_PRIMARY 1 +#define DRM_PLANE_TYPE_CURSOR 2 + +typedef struct _drmModeObjectProperties { + uint32_t count_props; + uint32_t *props; + uint64_t *prop_values; +} drmModeObjectProperties, *drmModeObjectPropertiesPtr; + +typedef struct _drmModePlane { + uint32_t count_formats; + uint32_t *formats; + uint32_t plane_id; + + uint32_t crtc_id; + uint32_t fb_id; + + uint32_t crtc_x, crtc_y; + uint32_t x, y; + + uint32_t possible_crtcs; + uint32_t gamma_size; +} drmModePlane, *drmModePlanePtr; + +typedef struct _drmModePlaneRes { + uint32_t count_planes; + uint32_t *planes; +} drmModePlaneRes, *drmModePlaneResPtr; + +extern void drmModeFreeModeInfo( drmModeModeInfoPtr ptr ); +extern void drmModeFreeResources( drmModeResPtr ptr ); +extern void drmModeFreeFB( drmModeFBPtr ptr ); +extern void drmModeFreeCrtc( drmModeCrtcPtr ptr ); +extern void drmModeFreeConnector( drmModeConnectorPtr ptr ); +extern void drmModeFreeEncoder( drmModeEncoderPtr ptr ); +extern void drmModeFreePlane( drmModePlanePtr ptr ); +extern void drmModeFreePlaneResources(drmModePlaneResPtr ptr); + +/** + * Retrieves all of the resources associated with a card. + */ +extern drmModeResPtr drmModeGetResources(int fd); + +/* + * FrameBuffer manipulation. + */ + +/** + * Retrieve information about framebuffer bufferId + */ +extern drmModeFBPtr drmModeGetFB(int fd, uint32_t bufferId); + +/** + * Creates a new framebuffer with an buffer object as its scanout buffer. + */ +extern int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle, + uint32_t *buf_id); +/* ...with a specific pixel format */ +extern int drmModeAddFB2(int fd, uint32_t width, uint32_t height, + uint32_t pixel_format, const uint32_t bo_handles[4], + const uint32_t pitches[4], const uint32_t offsets[4], + uint32_t *buf_id, uint32_t flags); + +/* ...with format modifiers */ +int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height, + uint32_t pixel_format, const uint32_t bo_handles[4], + const uint32_t pitches[4], const uint32_t offsets[4], + const uint64_t modifier[4], uint32_t *buf_id, + uint32_t flags); + +/** + * Destroies the given framebuffer. + */ +extern int drmModeRmFB(int fd, uint32_t bufferId); + +/** + * Mark a region of a framebuffer as dirty. + */ +extern int drmModeDirtyFB(int fd, uint32_t bufferId, + drmModeClipPtr clips, uint32_t num_clips); + + +/* + * Crtc functions + */ + +/** + * Retrieve information about the ctrt crtcId + */ +extern drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId); + +/** + * Set the mode on a crtc crtcId with the given mode modeId. + */ +int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, + uint32_t x, uint32_t y, uint32_t *connectors, int count, + drmModeModeInfoPtr mode); + +/* + * Cursor functions + */ + +/** + * Set the cursor on crtc + */ +int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height); + +int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); +/** + * Move the cursor on crtc + */ +int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y); + +/** + * Encoder functions + */ +drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id); + +/* + * Connector manipulation + */ + +/** + * Retrieve all information about the connector connectorId. This will do a + * forced probe on the connector to retrieve remote information such as EDIDs + * from the display device. + */ +extern drmModeConnectorPtr drmModeGetConnector(int fd, + uint32_t connectorId); + +/** + * Retrieve current information, i.e the currently active mode and encoder, + * about the connector connectorId. This will not do any probing on the + * connector or remote device, and only reports what is currently known. + * For the complete set of modes and encoders associated with the connector + * use drmModeGetConnector() which will do a probe to determine any display + * link changes first. + */ +extern drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, + uint32_t connector_id); + +/** + * Attaches the given mode to an connector. + */ +extern int drmModeAttachMode(int fd, uint32_t connectorId, drmModeModeInfoPtr mode_info); + +/** + * Detaches a mode from the connector + * must be unused, by the given mode. + */ +extern int drmModeDetachMode(int fd, uint32_t connectorId, drmModeModeInfoPtr mode_info); + +extern drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId); +extern void drmModeFreeProperty(drmModePropertyPtr ptr); + +extern drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id); +extern void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr); +extern int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, + uint64_t value); +extern int drmCheckModesettingSupported(const char *busid); + +extern int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, + uint32_t flags, void *user_data); +extern int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id, + uint32_t flags, void *user_data, + uint32_t target_vblank); + +extern drmModePlaneResPtr drmModeGetPlaneResources(int fd); +extern drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id); +extern int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id, + uint32_t fb_id, uint32_t flags, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + +extern drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd, + uint32_t object_id, + uint32_t object_type); +extern void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr); +extern int drmModeObjectSetProperty(int fd, uint32_t object_id, + uint32_t object_type, uint32_t property_id, + uint64_t value); + + +typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr; + +extern drmModeAtomicReqPtr drmModeAtomicAlloc(void); +extern drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr req); +extern int drmModeAtomicMerge(drmModeAtomicReqPtr base, + drmModeAtomicReqPtr augment); +extern void drmModeAtomicFree(drmModeAtomicReqPtr req); +extern int drmModeAtomicGetCursor(drmModeAtomicReqPtr req); +extern void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor); +extern int drmModeAtomicAddProperty(drmModeAtomicReqPtr req, + uint32_t object_id, + uint32_t property_id, + uint64_t value); +extern int drmModeAtomicCommit(int fd, + drmModeAtomicReqPtr req, + uint32_t flags, + void *user_data); + +extern int drmModeCreatePropertyBlob(int fd, const void *data, size_t size, + uint32_t *id); +extern int drmModeDestroyPropertyBlob(int fd, uint32_t id); + +/* + * DRM mode lease APIs. These create and manage new drm_masters with + * access to a subset of the available DRM resources + */ + +extern int drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id); + +typedef struct drmModeLesseeList { + uint32_t count; + uint32_t lessees[0]; +} drmModeLesseeListRes, *drmModeLesseeListPtr; + +extern drmModeLesseeListPtr drmModeListLessees(int fd); + +typedef struct drmModeObjectList { + uint32_t count; + uint32_t objects[0]; +} drmModeObjectListRes, *drmModeObjectListPtr; + +extern drmModeObjectListPtr drmModeGetLease(int fd); + +extern int drmModeRevokeLease(int fd, uint32_t lessee_id); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/common/stb/stb_image.h b/libs/common/stb/stb_image.h new file mode 100644 index 0000000..196dfd5 --- /dev/null +++ b/libs/common/stb/stb_image.h @@ -0,0 +1,7559 @@ +/* stb_image - v2.23 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt JR Smith github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/common/stb/stb_image_resize.h b/libs/common/stb/stb_image_resize.h new file mode 100644 index 0000000..4f6ad35 --- /dev/null +++ b/libs/common/stb/stb_image_resize.h @@ -0,0 +1,2630 @@ +/* stb_image_resize - v0.96 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb + + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + // WRAP/REFLECT/ZERO + + FULL API + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + SRGB & FLOATING POINT REPRESENTATION + The sRGB functions presume IEEE floating point. If you do not have + IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use + a slower implementation. + + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: + + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... + + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) some_func(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the 50/50 average of 99% transparent bright green + and 1% transparent black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source image.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + CONTRIBUTORS + Jorge L Rodriguez: Implementation + Sean Barrett: API design, optimizations + Aras Pranckevicius: bugfix + Nathan Reed: warning fixes + + REVISIONS + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. + + TODO + Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When processing wide images, break processing into tiles so data fits in L1 cache + Installable filters? + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + + +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. + +#define STBIR_ALPHA_CHANNEL_NONE -1 + +// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Alpha-channel can be processed separately +// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE +// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 +} stbir_filter; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + + + +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_datatype; + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset); + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1); +// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +// For memset +#include + +#include + +#ifndef STBIR_MALLOC +#include +// use comma operator to evaluate c, to avoid "unused parameter" warnings +#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) +#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) +#endif + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR__NOTUSED(v) (void)(v) +#else +#define STBIR__NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); + +typedef struct +{ + stbir__kernel_fn* kernel; + stbir__support_fn* support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors* vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; +} stbir__info; + + +static const float stbir__max_uint8_as_float = 255.0f; +static const float stbir__max_uint16_as_float = 65535.0f; +static const double stbir__max_uint32_as_float = 4294967295.0; + + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else +// sRGB transition values, scaled by 1<<28 +static int stbir__srgb_offset_to_linear_scaled[256] = +{ + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + + return (stbir_uint8) v; +} +#endif + +static float stbir__filter_trapezoid(float x, float scale) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + + x = (float)fabs(x); + + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale) +{ + STBIR_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_triangle(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_cubic(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + + return (0.0f); +} + +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s); + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s); + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s); + return 2; +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +{ + return stbir__get_filter_pixel_width(filter, scale) / 2; +} + +static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +{ + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); +} + +static int stbir__get_total_horizontal_coefficients(stbir__info* info) +{ + return info->horizontal_num_contributors + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); +} + +static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return info->vertical_num_contributors + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +{ + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + // NOTREACHED + + default: + STBIR_ASSERT(!"Unimplemented edge type"); + return 0; + } +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + + STBIR_ASSERT(total_filter > 0.9); + STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + + STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) +{ + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; + + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } + + STBIR_ASSERT(total > 0.9f); + STBIR_ASSERT(total < 1.1f); + + scale = 1 / total; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } + + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; + + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; + + contributors[j].n0 += skip; + + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } + + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; + + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } + + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); +} + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + + stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + size_t input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } + + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + { + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +#ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +#endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + stbir_info->ring_buffer_last_scanline = n; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR_ASSERT(n1 >= n0); + STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; + + STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; + + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). + } + } + + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x = 0, num_nonalpha = 0; x < channels; ++x) + { + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + { + nonalpha[num_nonalpha++] = (stbir_uint16)x; + } + } + + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) + #endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +{ + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + + info->horizontal_shift = s0 * info->output_w / (s1 - s0); + info->vertical_shift = t0 * info->output_h / (t1 - t0); + } +} + +static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) +{ + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; +} + +static stbir_uint32 stbir__calculate_memory(stbir__info *info) +{ + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + info->ring_buffer_num_entries = filter_height + 1; + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; + + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + } + + if (alpha_channel >= info->channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +static int stbir__resize_arbitrary( + void *alloc_context, + const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory, alloc_context); + + return result; +} + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/common/stb/stb_image_write.h b/libs/common/stb/stb_image_write.h new file mode 100644 index 0000000..c117344 --- /dev/null +++ b/libs/common/stb/stb_image_write.h @@ -0,0 +1,1619 @@ +/* stb_image_write - v1.13 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBI_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a; arr[1] = b; arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_WANT_SECURE_LIB__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = snprintf(buffer, 128, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + float r, g, b; + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/platform/RK1808/libjpeg/bin/cjpeg b/libs/platform/RK1808/libjpeg/bin/cjpeg new file mode 100644 index 0000000..a717a38 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/bin/cjpeg differ diff --git a/libs/platform/RK1808/libjpeg/bin/djpeg b/libs/platform/RK1808/libjpeg/bin/djpeg new file mode 100644 index 0000000..5c5e03c Binary files /dev/null and b/libs/platform/RK1808/libjpeg/bin/djpeg differ diff --git a/libs/platform/RK1808/libjpeg/bin/jpegtran b/libs/platform/RK1808/libjpeg/bin/jpegtran new file mode 100644 index 0000000..06f1446 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/bin/jpegtran differ diff --git a/libs/platform/RK1808/libjpeg/bin/rdjpgcom b/libs/platform/RK1808/libjpeg/bin/rdjpgcom new file mode 100644 index 0000000..94901a3 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/bin/rdjpgcom differ diff --git a/libs/platform/RK1808/libjpeg/bin/wrjpgcom b/libs/platform/RK1808/libjpeg/bin/wrjpgcom new file mode 100644 index 0000000..72764d7 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/bin/wrjpgcom differ diff --git a/libs/platform/RK1808/libjpeg/include/jconfig.h b/libs/platform/RK1808/libjpeg/include/jconfig.h new file mode 100644 index 0000000..966b1d5 --- /dev/null +++ b/libs/platform/RK1808/libjpeg/include/jconfig.h @@ -0,0 +1,54 @@ +/* jconfig.h. Generated from jconfig.cfg by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.txt for explanations */ + +#define HAVE_PROTOTYPES 1 +#define HAVE_UNSIGNED_CHAR 1 +#define HAVE_UNSIGNED_SHORT 1 +/* #undef void */ +/* #undef const */ +/* #undef CHAR_IS_UNSIGNED */ +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_LOCALE_H 1 +/* #undef NEED_BSD_STRINGS */ +/* #undef NEED_SYS_TYPES_H */ +/* #undef NEED_FAR_POINTERS */ +/* #undef NEED_SHORT_EXTERNAL_NAMES */ +/* Define this if you get warnings about undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +/* Define "boolean" as unsigned char, not int, on Windows systems. */ +#ifdef _WIN32 +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + +#ifdef JPEG_INTERNALS + +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +/* #undef DEFAULT_MAX_MEM */ +/* #undef NO_MKTEMP */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +/* #undef RLE_SUPPORTED */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* #undef TWO_FILE_COMMANDLINE */ +/* #undef NEED_SIGNAL_CATCHER */ +/* #undef DONT_USE_B_MODE */ + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +/* #undef PROGRESS_REPORT */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/libs/platform/RK1808/libjpeg/include/jerror.h b/libs/platform/RK1808/libjpeg/include/jerror.h new file mode 100644 index 0000000..1cfb2b1 --- /dev/null +++ b/libs/platform/RK1808/libjpeg/include/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/libs/platform/RK1808/libjpeg/include/jmorecfg.h b/libs/platform/RK1808/libjpeg/include/jmorecfg.h new file mode 100644 index 0000000..6c085c3 --- /dev/null +++ b/libs/platform/RK1808/libjpeg/include/jmorecfg.h @@ -0,0 +1,369 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2011 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/libs/platform/RK1808/libjpeg/include/jpeglib.h b/libs/platform/RK1808/libjpeg/include/jpeglib.h new file mode 100644 index 0000000..1327cff --- /dev/null +++ b/libs/platform/RK1808/libjpeg/include/jpeglib.h @@ -0,0 +1,1160 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2011 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version IDs for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 80". + */ + +#define JPEG_LIB_VERSION 80 /* Compatibility version 8.0 */ +#define JPEG_LIB_VERSION_MAJOR 8 +#define JPEG_LIB_VERSION_MINOR 4 + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 coefficients */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_h_scaled_size/DCTSIZE) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.a b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.a new file mode 100644 index 0000000..1fbd0a9 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.a differ diff --git a/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.la b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.la new file mode 100644 index 0000000..0b8f91c --- /dev/null +++ b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.la @@ -0,0 +1,41 @@ +# libjpeg.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.2 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libjpeg.so.8' + +# Names of this library. +library_names='libjpeg.so.8.4.0 libjpeg.so.8 libjpeg.so' + +# The name of the static archive. +old_library='libjpeg.a' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rk1808/lib' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libjpeg. +current=12 +age=4 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/home/xz/Documents/testing/compile_test/jpegsrc.v8d1/jpeg-8d1/rk1808/lib' diff --git a/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so new file mode 100644 index 0000000..f91df71 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so differ diff --git a/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so.8 b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so.8 new file mode 100644 index 0000000..f91df71 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so.8 differ diff --git a/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so.8.4.0 b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so.8.4.0 new file mode 100644 index 0000000..f91df71 Binary files /dev/null and b/libs/platform/RK1808/libjpeg/lib/Linux/libjpeg.so.8.4.0 differ diff --git a/libs/platform/RK1808/libpng/bin/libpng-config b/libs/platform/RK1808/libpng/bin/libpng-config new file mode 100644 index 0000000..923d561 --- /dev/null +++ b/libs/platform/RK1808/libpng/bin/libpng-config @@ -0,0 +1,127 @@ +#! /bin/sh + +# libpng-config +# provides configuration info for libpng. + +# Copyright (C) 2002, 2004, 2006, 2007 Glenn Randers-Pehrson + +# This code is released under the libpng license. +# For conditions of distribution and use, see the disclaimer +# and license in png.h + +# Modeled after libxml-config. + +version="1.6.37" +prefix="//home/xz/Documents/testing/compile_test/lpng1637/rk1808" +exec_prefix="${prefix}" +libdir="${exec_prefix}/lib" +includedir="${prefix}/include/libpng16" +libs="-lpng16" +all_libs="-lpng16 -lm -lz -lm " +I_opts="-I${includedir}" +L_opts="-L${libdir}" +R_opts="" +cppflags="" +ccopts="" +ldopts="" + +usage() +{ + cat < + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.37" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 37 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10637 /* 1.6.37 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_37; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK1808/libpng/include/libpng16/pngconf.h b/libs/platform/RK1808/libpng/include/libpng16/pngconf.h new file mode 100644 index 0000000..927a769 --- /dev/null +++ b/libs/platform/RK1808/libpng/include/libpng16/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.37 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK1808/libpng/include/libpng16/pnglibconf.h b/libs/platform/RK1808/libpng/include/libpng16/pnglibconf.h new file mode 100644 index 0000000..7f6a817 --- /dev/null +++ b/libs/platform/RK1808/libpng/include/libpng16/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.37 */ + +/* Copyright (c) 2018-2019 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK1808/libpng/include/png.h b/libs/platform/RK1808/libpng/include/png.h new file mode 100644 index 0000000..139eb0d --- /dev/null +++ b/libs/platform/RK1808/libpng/include/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.37 - April 14, 2019 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.37, April 2019: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.37 16 10637 16.so.16.37[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.37" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 37 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10637 /* 1.6.37 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_37; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK1808/libpng/include/pngconf.h b/libs/platform/RK1808/libpng/include/pngconf.h new file mode 100644 index 0000000..927a769 --- /dev/null +++ b/libs/platform/RK1808/libpng/include/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.37 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK1808/libpng/include/pnglibconf.h b/libs/platform/RK1808/libpng/include/pnglibconf.h new file mode 100644 index 0000000..7f6a817 --- /dev/null +++ b/libs/platform/RK1808/libpng/include/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.37 */ + +/* Copyright (c) 2018-2019 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng.a b/libs/platform/RK1808/libpng/lib/Linux/libpng.a new file mode 100644 index 0000000..eef42bb Binary files /dev/null and b/libs/platform/RK1808/libpng/lib/Linux/libpng.a differ diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng.la b/libs/platform/RK1808/libpng/lib/Linux/libpng.la new file mode 100644 index 0000000..0156394 --- /dev/null +++ b/libs/platform/RK1808/libpng/lib/Linux/libpng.la @@ -0,0 +1,41 @@ +# libpng16.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libpng16.so.16' + +# Names of this library. +library_names='libpng16.so.16.37.0 libpng16.so.16 libpng16.so' + +# The name of the static archive. +old_library='libpng16.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rk1808/lib/ -lz -lm' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libpng16. +current=53 +age=37 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='//home/xz/Documents/testing/compile_test/lpng1637/rk1808/lib' diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng.so b/libs/platform/RK1808/libpng/lib/Linux/libpng.so new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK1808/libpng/lib/Linux/libpng.so differ diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng16.a b/libs/platform/RK1808/libpng/lib/Linux/libpng16.a new file mode 100644 index 0000000..eef42bb Binary files /dev/null and b/libs/platform/RK1808/libpng/lib/Linux/libpng16.a differ diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng16.la b/libs/platform/RK1808/libpng/lib/Linux/libpng16.la new file mode 100644 index 0000000..0156394 --- /dev/null +++ b/libs/platform/RK1808/libpng/lib/Linux/libpng16.la @@ -0,0 +1,41 @@ +# libpng16.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libpng16.so.16' + +# Names of this library. +library_names='libpng16.so.16.37.0 libpng16.so.16 libpng16.so' + +# The name of the static archive. +old_library='libpng16.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rk1808/lib/ -lz -lm' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libpng16. +current=53 +age=37 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='//home/xz/Documents/testing/compile_test/lpng1637/rk1808/lib' diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng16.so b/libs/platform/RK1808/libpng/lib/Linux/libpng16.so new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK1808/libpng/lib/Linux/libpng16.so differ diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng16.so.16 b/libs/platform/RK1808/libpng/lib/Linux/libpng16.so.16 new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK1808/libpng/lib/Linux/libpng16.so.16 differ diff --git a/libs/platform/RK1808/libpng/lib/Linux/libpng16.so.16.37.0 b/libs/platform/RK1808/libpng/lib/Linux/libpng16.so.16.37.0 new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK1808/libpng/lib/Linux/libpng16.so.16.37.0 differ diff --git a/libs/platform/RK1808/libpng/lib/Linux/pkgconfig/libpng.pc b/libs/platform/RK1808/libpng/lib/Linux/pkgconfig/libpng.pc new file mode 100644 index 0000000..fa2b2de --- /dev/null +++ b/libs/platform/RK1808/libpng/lib/Linux/pkgconfig/libpng.pc @@ -0,0 +1,12 @@ +prefix=//home/xz/Documents/testing/compile_test/lpng1637/rk1808 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/libpng16 + +Name: libpng +Description: Loads and saves PNG files +Version: 1.6.37 +Requires: zlib +Libs: -L${libdir} -lpng16 +Libs.private: -lm -lz -lm +Cflags: -I${includedir} diff --git a/libs/platform/RK1808/libpng/lib/Linux/pkgconfig/libpng16.pc b/libs/platform/RK1808/libpng/lib/Linux/pkgconfig/libpng16.pc new file mode 100644 index 0000000..fa2b2de --- /dev/null +++ b/libs/platform/RK1808/libpng/lib/Linux/pkgconfig/libpng16.pc @@ -0,0 +1,12 @@ +prefix=//home/xz/Documents/testing/compile_test/lpng1637/rk1808 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/libpng16 + +Name: libpng +Description: Loads and saves PNG files +Version: 1.6.37 +Requires: zlib +Libs: -L${libdir} -lpng16 +Libs.private: -lm -lz -lm +Cflags: -I${includedir} diff --git a/libs/platform/RK1808/libpng/share/man/man3/libpng.3 b/libs/platform/RK1808/libpng/share/man/man3/libpng.3 new file mode 100644 index 0000000..f374235 --- /dev/null +++ b/libs/platform/RK1808/libpng/share/man/man3/libpng.3 @@ -0,0 +1,6052 @@ +.TH LIBPNG 3 "April 14, 2019" +.SH NAME +libpng \- Portable Network Graphics (PNG) Reference Library 1.6.37 + +.SH SYNOPSIS +\fB#include \fP + +\fBpng_uint_32 png_access_version_number (void);\fP + +\fBvoid png_benign_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fBvoid png_build_grayscale_palette (int \fP\fIbit_depth\fP\fB, png_colorp \fIpalette\fP\fB);\fP + +\fBpng_voidp png_calloc (png_structp \fP\fIpng_ptr\fP\fB, png_alloc_size_t \fIsize\fP\fB);\fP + +\fBvoid png_chunk_benign_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fBvoid png_chunk_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fBvoid png_chunk_warning (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fBvoid png_convert_from_struct_tm (png_timep \fP\fIptime\fP\fB, struct tm FAR * \fIttime\fP\fB);\fP + +\fBvoid png_convert_from_time_t (png_timep \fP\fIptime\fP\fB, time_t \fIttime\fP\fB);\fP + +\fBpng_charp png_convert_to_rfc1123 (png_structp \fP\fIpng_ptr\fP\fB, png_timep \fIptime\fP\fB);\fP + +\fBpng_infop png_create_info_struct (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_structp png_create_read_struct (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarn_fn\fP\fB);\fP + +\fBpng_structp png_create_read_struct_2 (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fP\fIwarn_fn\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fBpng_structp png_create_write_struct (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarn_fn\fP\fB);\fP + +\fBpng_structp png_create_write_struct_2 (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fP\fIwarn_fn\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fBvoid png_data_freer (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIfreer\fP\fB, png_uint_32 \fImask\fP\fB);\fP + +\fBvoid png_destroy_info_struct (png_structp \fP\fIpng_ptr\fP\fB, png_infopp \fIinfo_ptr_ptr\fP\fB);\fP + +\fBvoid png_destroy_read_struct (png_structpp \fP\fIpng_ptr_ptr\fP\fB, png_infopp \fP\fIinfo_ptr_ptr\fP\fB, png_infopp \fIend_info_ptr_ptr\fP\fB);\fP + +\fBvoid png_destroy_write_struct (png_structpp \fP\fIpng_ptr_ptr\fP\fB, png_infopp \fIinfo_ptr_ptr\fP\fB);\fP + +\fBvoid png_err (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fBvoid png_free (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fIptr\fP\fB);\fP + +\fBvoid png_free_chunk_list (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_free_default (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fIptr\fP\fB);\fP + +\fBvoid png_free_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fInum\fP\fB);\fP + +\fBpng_byte png_get_bit_depth (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_bKGD (png_const_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_16p \fI*background\fP\fB);\fP + +\fBpng_byte png_get_channels (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_cHRM (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, double \fP\fI*white_x\fP\fB, double \fP\fI*white_y\fP\fB, double \fP\fI*red_x\fP\fB, double \fP\fI*red_y\fP\fB, double \fP\fI*green_x\fP\fB, double \fP\fI*green_y\fP\fB, double \fP\fI*blue_x\fP\fB, double \fI*blue_y\fP\fB);\fP + +\fBpng_uint_32 png_get_cHRM_fixed (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*white_x\fP\fB, png_uint_32 \fP\fI*white_y\fP\fB, png_uint_32 \fP\fI*red_x\fP\fB, png_uint_32 \fP\fI*red_y\fP\fB, png_uint_32 \fP\fI*green_x\fP\fB, png_uint_32 \fP\fI*green_y\fP\fB, png_uint_32 \fP\fI*blue_x\fP\fB, png_uint_32 \fI*blue_y\fP\fB);\fP + +\fBpng_uint_32 png_get_cHRM_XYZ (png_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, double \fP\fI*red_X\fP\fB, double \fP\fI*red_Y\fP\fB, double \fP\fI*red_Z\fP\fB, double \fP\fI*green_X\fP\fB, double \fP\fI*green_Y\fP\fB, double \fP\fI*green_Z\fP\fB, double \fP\fI*blue_X\fP\fB, double \fP\fI*blue_Y\fP\fB, double \fI*blue_Z\fP\fB);\fP + +\fBpng_uint_32 png_get_cHRM_XYZ_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_fixed_point \fP\fI*int_red_X\fP\fB, png_fixed_point \fP\fI*int_red_Y\fP\fB, png_fixed_point \fP\fI*int_red_Z\fP\fB, png_fixed_point \fP\fI*int_green_X\fP\fB, png_fixed_point \fP\fI*int_green_Y\fP\fB, png_fixed_point \fP\fI*int_green_Z\fP\fB, png_fixed_point \fP\fI*int_blue_X\fP\fB, png_fixed_point \fP\fI*int_blue_Y\fP\fB, png_fixed_point \fI*int_blue_Z\fP\fB);\fP + +\fBpng_uint_32 png_get_chunk_cache_max (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_alloc_size_t png_get_chunk_malloc_max (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_color_type (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_compression_buffer_size (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_compression_type (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_byte png_get_copyright (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_current_row_number \fI(png_const_structp\fP\fB);\fP + +\fBpng_byte png_get_current_pass_number \fI(png_const_structp\fP\fB);\fP + +\fBpng_voidp png_get_error_ptr (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_filter_type (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_gAMA (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, double \fI*file_gamma\fP\fB);\fP + +\fBpng_uint_32 png_get_gAMA_fixed (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fI*int_file_gamma\fP\fB);\fP + +\fBpng_byte png_get_header_ver (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_header_version (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_eXIf (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fI*exif\fP\fB);\fP + +\fBpng_uint_32 png_get_eXIf_1 (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_unit_32 \fP\fI*num_exif\fP\fB, png_bytep \fI*exif\fP\fB);\fP + +\fBpng_uint_32 png_get_hIST (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fI*hist\fP\fB);\fP + +\fBpng_uint_32 png_get_iCCP (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_charpp \fP\fIname\fP\fB, int \fP\fI*compression_type\fP\fB, png_bytepp \fP\fIprofile\fP\fB, png_uint_32 \fI*proflen\fP\fB);\fP + +\fBpng_uint_32 png_get_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*width\fP\fB, png_uint_32 \fP\fI*height\fP\fB, int \fP\fI*bit_depth\fP\fB, int \fP\fI*color_type\fP\fB, int \fP\fI*interlace_type\fP\fB, int \fP\fI*compression_type\fP\fB, int \fI*filter_type\fP\fB);\fP + +\fBpng_uint_32 png_get_image_height (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_image_width (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_int_32 (png_bytep \fIbuf\fP\fB);\fP + +\fBpng_byte png_get_interlace_type (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_io_chunk_type (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_voidp png_get_io_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_io_state (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_libpng_ver (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBint png_get_palette_max(png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_voidp png_get_mem_ptr (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_oFFs (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*offset_x\fP\fB, png_uint_32 \fP\fI*offset_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fBpng_uint_32 png_get_pCAL (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fI*purpose\fP\fB, png_int_32 \fP\fI*X0\fP\fB, png_int_32 \fP\fI*X1\fP\fB, int \fP\fI*type\fP\fB, int \fP\fI*nparams\fP\fB, png_charp \fP\fI*units\fP\fB, png_charpp \fI*params\fP\fB);\fP + +\fBpng_uint_32 png_get_pHYs (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*res_x\fP\fB, png_uint_32 \fP\fI*res_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fBfloat png_get_pixel_aspect_ratio (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_pHYs_dpi (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*res_x\fP\fB, png_uint_32 \fP\fI*res_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fBpng_fixed_point png_get_pixel_aspect_ratio_fixed (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_pixels_per_inch (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_pixels_per_meter (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_voidp png_get_progressive_ptr (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_PLTE (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_colorp \fP\fI*palette\fP\fB, int \fI*num_palette\fP\fB);\fP + +\fBpng_byte png_get_rgb_to_gray_status (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_rowbytes (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_bytepp png_get_rows (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_sBIT (png_const_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_8p \fI*sig_bit\fP\fB);\fP + +\fBvoid png_get_sCAL (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, int* \fP\fIunit\fP\fB, double* \fP\fIwidth\fP\fB, double* \fIheight\fP\fB);\fP + +\fBvoid png_get_sCAL_fixed (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, int* \fP\fIunit\fP\fB, png_fixed_pointp \fP\fIwidth\fP\fB, png_fixed_pointp \fIheight\fP\fB);\fP + +\fBvoid png_get_sCAL_s (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, int* \fP\fIunit\fP\fB, png_charpp \fP\fIwidth\fP\fB, png_charpp \fIheight\fP\fB);\fP + +\fBpng_bytep png_get_signature (png_const_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_sPLT (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_spalette_p \fI*splt_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_sRGB (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, int \fI*file_srgb_intent\fP\fB);\fP + +\fBpng_uint_32 png_get_text (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fI*text_ptr\fP\fB, int \fI*num_text\fP\fB);\fP + +\fBpng_uint_32 png_get_tIME (png_const_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_timep \fI*mod_time\fP\fB);\fP + +\fBpng_uint_32 png_get_tRNS (png_const_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fI*trans_alpha\fP\fB, int \fP\fI*num_trans\fP\fB, png_color_16p \fI*trans_color\fP\fB);\fP + +\fB/* This function is really an inline macro. \fI*/ + +\fBpng_uint_16 png_get_uint_16 (png_bytep \fIbuf\fP\fB);\fP + +\fBpng_uint_32 png_get_uint_31 (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIbuf\fP\fB);\fP + +\fB/* This function is really an inline macro. \fI*/ + +\fBpng_uint_32 png_get_uint_32 (png_bytep \fIbuf\fP\fB);\fP + +\fBpng_uint_32 png_get_unknown_chunks (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_unknown_chunkpp \fIunknowns\fP\fB);\fP + +\fBpng_voidp png_get_user_chunk_ptr (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_user_height_max (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_voidp png_get_user_transform_ptr (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_user_width_max (png_const_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_valid (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIflag\fP\fB);\fP + +\fBfloat png_get_x_offset_inches (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_fixed_point png_get_x_offset_inches_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_x_offset_microns (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_x_offset_pixels (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_x_pixels_per_inch (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_x_pixels_per_meter (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBfloat png_get_y_offset_inches (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_fixed_point png_get_y_offset_inches_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_y_offset_microns (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_y_offset_pixels (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_y_pixels_per_inch (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_y_pixels_per_meter (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fIinfo_ptr\fP\fB);\fP + +\fBint png_handle_as_unknown (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIchunk_name\fP\fB);\fP + +\fBint png_image_begin_read_from_file (png_imagep \fP\fIimage\fP\fB, const char \fI*file_name\fP\fB);\fP + +\fBint png_image_begin_read_from_stdio (png_imagep \fP\fIimage\fP\fB, FILE* \fIfile\fP\fB);\fP + +\fBint, png_image_begin_read_from_memory (png_imagep \fP\fIimage\fP\fB, png_const_voidp \fP\fImemory\fP\fB, size_t \fIsize\fP\fB);\fP + +\fBint png_image_finish_read (png_imagep \fP\fIimage\fP\fB, png_colorp \fP\fIbackground\fP\fB, void \fP\fI*buffer\fP\fB, png_int_32 \fP\fIrow_stride\fP\fB, void \fI*colormap\fP\fB);\fP + +\fBvoid png_image_free (png_imagep \fIimage\fP\fB);\fP + +\fBint png_image_write_to_file (png_imagep \fP\fIimage\fP\fB, const char \fP\fI*file\fP\fB, int \fP\fIconvert_to_8bit\fP\fB, const void \fP\fI*buffer\fP\fB, png_int_32 \fP\fIrow_stride\fP\fB, void \fI*colormap\fP\fB);\fP + +\fBint png_image_write_to_memory (png_imagep \fP\fIimage\fP\fB, void \fP\fI*memory\fP\fB, png_alloc_size_t * PNG_RESTRICT \fP\fImemory_bytes\fP\fB, int \fP\fIconvert_to_8_bit\fP\fB, const void \fP\fI*buffer\fP\fB, png_int_32 \fP\fIrow_stride\fP\fB, const void \fI*colormap\fP\fB);\fP + +\fBint png_image_write_to_stdio (png_imagep \fP\fIimage\fP\fB, FILE \fP\fI*file\fP\fB, int \fP\fIconvert_to_8_bit\fP\fB, const void \fP\fI*buffer\fP\fB, png_int_32 \fP\fIrow_stride\fP\fB, void \fI*colormap\fP\fB);\fP + +\fBvoid png_info_init_3 (png_infopp \fP\fIinfo_ptr\fP\fB, size_t \fIpng_info_struct_size\fP\fB);\fP + +\fBvoid png_init_io (png_structp \fP\fIpng_ptr\fP\fB, FILE \fI*fp\fP\fB);\fP + +\fBvoid png_longjmp (png_structp \fP\fIpng_ptr\fP\fB, int \fIval\fP\fB);\fP + +\fBpng_voidp png_malloc (png_structp \fP\fIpng_ptr\fP\fB, png_alloc_size_t \fIsize\fP\fB);\fP + +\fBpng_voidp png_malloc_default (png_structp \fP\fIpng_ptr\fP\fB, png_alloc_size_t \fIsize\fP\fB);\fP + +\fBpng_voidp png_malloc_warn (png_structp \fP\fIpng_ptr\fP\fB, png_alloc_size_t \fIsize\fP\fB);\fP + +\fBpng_uint_32 png_permit_mng_features (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fImng_features_permitted\fP\fB);\fP + +\fBvoid png_process_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fIbuffer\fP\fB, size_t \fIbuffer_size\fP\fB);\fP + +\fBsize_t png_process_data_pause (png_structp \fP\fIpng_ptr\fP\fB, int \fIsave\fP\fB);\fP + +\fBpng_uint_32 png_process_data_skip (png_structp \fP\fIpng_ptr\fP\fB);\fP + +\fBvoid png_progressive_combine_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIold_row\fP\fB, png_bytep \fInew_row\fP\fB);\fP + +\fBvoid png_read_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_read_image (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fIimage\fP\fB);\fP + +\fBvoid png_read_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_read_png (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fItransforms\fP\fB, png_voidp \fIparams\fP\fB);\fP + +\fBvoid png_read_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIrow\fP\fB, png_bytep \fIdisplay_row\fP\fB);\fP + +\fBvoid png_read_rows (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fP\fIrow\fP\fB, png_bytepp \fP\fIdisplay_row\fP\fB, png_uint_32 \fInum_rows\fP\fB);\fP + +\fBvoid png_read_update_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBint png_reset_zstream (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_save_int_32 (png_bytep \fP\fIbuf\fP\fB, png_int_32 \fIi\fP\fB);\fP + +\fBvoid png_save_uint_16 (png_bytep \fP\fIbuf\fP\fB, unsigned int \fIi\fP\fB);\fP + +\fBvoid png_save_uint_32 (png_bytep \fP\fIbuf\fP\fB, png_uint_32 \fIi\fP\fB);\fP + +\fBvoid png_set_add_alpha (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, int \fIflags\fP\fB);\fP + +\fBvoid png_set_alpha_mode (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fImode\fP\fB, double \fIoutput_gamma\fP\fB);\fP + +\fBvoid png_set_alpha_mode_fixed (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fImode\fP\fB, png_fixed_point \fIoutput_gamma\fP\fB);\fP + +\fBvoid png_set_background (png_structp \fP\fIpng_ptr\fP\fB, png_color_16p \fP\fIbackground_color\fP\fB, int \fP\fIbackground_gamma_code\fP\fB, int \fP\fIneed_expand\fP\fB, double \fIbackground_gamma\fP\fB);\fP + +\fBvoid png_set_background_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_color_16p \fP\fIbackground_color\fP\fB, int \fP\fIbackground_gamma_code\fP\fB, int \fP\fIneed_expand\fP\fB, png_uint_32 \fIbackground_gamma\fP\fB);\fP + +\fBvoid png_set_benign_errors (png_structp \fP\fIpng_ptr\fP\fB, int \fIallowed\fP\fB);\fP + +\fBvoid png_set_bgr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_bKGD (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_16p \fIbackground\fP\fB);\fP + +\fBvoid png_set_check_for_invalid_index (png_structrp \fP\fIpng_ptr\fP\fB, int \fIallowed\fP\fB);\fP + +\fBvoid png_set_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fIwhite_x\fP\fB, double \fP\fIwhite_y\fP\fB, double \fP\fIred_x\fP\fB, double \fP\fIred_y\fP\fB, double \fP\fIgreen_x\fP\fB, double \fP\fIgreen_y\fP\fB, double \fP\fIblue_x\fP\fB, double \fIblue_y\fP\fB);\fP + +\fBvoid png_set_cHRM_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIwhite_x\fP\fB, png_uint_32 \fP\fIwhite_y\fP\fB, png_uint_32 \fP\fIred_x\fP\fB, png_uint_32 \fP\fIred_y\fP\fB, png_uint_32 \fP\fIgreen_x\fP\fB, png_uint_32 \fP\fIgreen_y\fP\fB, png_uint_32 \fP\fIblue_x\fP\fB, png_uint_32 \fIblue_y\fP\fB);\fP + +\fBvoid png_set_cHRM_XYZ (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fIred_X\fP\fB, double \fP\fIred_Y\fP\fB, double \fP\fIred_Z\fP\fB, double \fP\fIgreen_X\fP\fB, double \fP\fIgreen_Y\fP\fB, double \fP\fIgreen_Z\fP\fB, double \fP\fIblue_X\fP\fB, double \fP\fIblue_Y\fP\fB, double \fIblue_Z\fP\fB);\fP + +\fBvoid png_set_cHRM_XYZ_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_fixed_point \fP\fIint_red_X\fP\fB, png_fixed_point \fP\fIint_red_Y\fP\fB, png_fixed_point \fP\fIint_red_Z\fP\fB, png_fixed_point \fP\fIint_green_X\fP\fB, png_fixed_point \fP\fIint_green_Y\fP\fB, png_fixed_point \fP\fIint_green_Z\fP\fB, png_fixed_point \fP\fIint_blue_X\fP\fB, png_fixed_point \fP\fIint_blue_Y\fP\fB, png_fixed_point \fIint_blue_Z\fP\fB);\fP + +\fBvoid png_set_chunk_cache_max (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIuser_chunk_cache_max\fP\fB);\fP + +\fBvoid png_set_compression_level (png_structp \fP\fIpng_ptr\fP\fB, int \fIlevel\fP\fB);\fP + +\fBvoid png_set_compression_mem_level (png_structp \fP\fIpng_ptr\fP\fB, int \fImem_level\fP\fB);\fP + +\fBvoid png_set_compression_method (png_structp \fP\fIpng_ptr\fP\fB, int \fImethod\fP\fB);\fP + +\fBvoid png_set_compression_strategy (png_structp \fP\fIpng_ptr\fP\fB, int \fIstrategy\fP\fB);\fP + +\fBvoid png_set_compression_window_bits (png_structp \fP\fIpng_ptr\fP\fB, int \fIwindow_bits\fP\fB);\fP + +\fBvoid png_set_crc_action (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIcrit_action\fP\fB, int \fIancil_action\fP\fB);\fP + +\fBvoid png_set_error_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarning_fn\fP\fB);\fP + +\fBvoid png_set_expand (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_expand_16 (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_expand_gray_1_2_4_to_8 (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_filler (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, int \fIflags\fP\fB);\fP + +\fBvoid png_set_filter (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fImethod\fP\fB, int \fIfilters\fP\fB);\fP + +\fBvoid png_set_filter_heuristics (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIheuristic_method\fP\fB, int \fP\fInum_weights\fP\fB, png_doublep \fP\fIfilter_weights\fP\fB, png_doublep \fIfilter_costs\fP\fB);\fP + +\fBvoid png_set_filter_heuristics_fixed (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIheuristic_method\fP\fB, int \fP\fInum_weights\fP\fB, png_fixed_point_p \fP\fIfilter_weights\fP\fB, png_fixed_point_p \fIfilter_costs\fP\fB);\fP + +\fBvoid png_set_flush (png_structp \fP\fIpng_ptr\fP\fB, int \fInrows\fP\fB);\fP + +\fBvoid png_set_gamma (png_structp \fP\fIpng_ptr\fP\fB, double \fP\fIscreen_gamma\fP\fB, double \fIdefault_file_gamma\fP\fB);\fP + +\fBvoid png_set_gamma_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIscreen_gamma\fP\fB, png_uint_32 \fIdefault_file_gamma\fP\fB);\fP + +\fBvoid png_set_gAMA (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fIfile_gamma\fP\fB);\fP + +\fBvoid png_set_gAMA_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIfile_gamma\fP\fB);\fP + +\fBvoid png_set_gray_1_2_4_to_8 (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_gray_to_rgb (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_eXIf (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fIexif\fP\fB);\fP + +\fBvoid png_set_eXIf_1 (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fInum_exif\fP\fB, png_bytep \fIexif\fP\fB);\fP + +\fBvoid png_set_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fIhist\fP\fB);\fP + +\fBvoid png_set_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_const_charp \fP\fIname\fP\fB, int \fP\fIcompression_type\fP\fB, png_const_bytep \fP\fIprofile\fP\fB, png_uint_32 \fIproflen\fP\fB);\fP + +\fBint png_set_interlace_handling (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_invalid (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fImask\fP\fB);\fP + +\fBvoid png_set_invert_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_invert_mono (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIwidth\fP\fB, png_uint_32 \fP\fIheight\fP\fB, int \fP\fIbit_depth\fP\fB, int \fP\fIcolor_type\fP\fB, int \fP\fIinterlace_type\fP\fB, int \fP\fIcompression_type\fP\fB, int \fIfilter_type\fP\fB);\fP + +\fBvoid png_set_keep_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIkeep\fP\fB, png_bytep \fP\fIchunk_list\fP\fB, int \fInum_chunks\fP\fB);\fP + +\fBjmp_buf* png_set_longjmp_fn (png_structp \fP\fIpng_ptr\fP\fB, png_longjmp_ptr \fP\fIlongjmp_fn\fP\fB, size_t \fIjmp_buf_size\fP\fB);\fP + +\fBvoid png_set_chunk_malloc_max (png_structp \fP\fIpng_ptr\fP\fB, png_alloc_size_t \fIuser_chunk_cache_max\fP\fB);\fP + +\fBvoid png_set_compression_buffer_size (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fBvoid png_set_mem_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fBvoid png_set_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIoffset_x\fP\fB, png_uint_32 \fP\fIoffset_y\fP\fB, int \fIunit_type\fP\fB);\fP + +\fBint png_set_option(png_structrp \fP\fIpng_ptr\fP\fB, int \fP\fIoption\fP\fB, int \fIonoff\fP\fB);\fP + +\fBvoid png_set_packing (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_packswap (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_palette_to_rgb (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIpurpose\fP\fB, png_int_32 \fP\fIX0\fP\fB, png_int_32 \fP\fIX1\fP\fB, int \fP\fItype\fP\fB, int \fP\fInparams\fP\fB, png_charp \fP\fIunits\fP\fB, png_charpp \fIparams\fP\fB);\fP + +\fBvoid png_set_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIres_x\fP\fB, png_uint_32 \fP\fIres_y\fP\fB, int \fIunit_type\fP\fB);\fP + +\fBvoid png_set_progressive_read_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIprogressive_ptr\fP\fB, png_progressive_info_ptr \fP\fIinfo_fn\fP\fB, png_progressive_row_ptr \fP\fIrow_fn\fP\fB, png_progressive_end_ptr \fIend_fn\fP\fB);\fP + +\fBvoid png_set_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fInum_palette\fP\fB);\fP + +\fBvoid png_set_quantize (png_structp \fP\fIpng_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fP\fInum_palette\fP\fB, int \fP\fImaximum_colors\fP\fB, png_uint_16p \fP\fIhistogram\fP\fB, int \fIfull_quantize\fP\fB);\fP + +\fBvoid png_set_read_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIio_ptr\fP\fB, png_rw_ptr \fIread_data_fn\fP\fB);\fP + +\fBvoid png_set_read_status_fn (png_structp \fP\fIpng_ptr\fP\fB, png_read_status_ptr \fIread_row_fn\fP\fB);\fP + +\fBvoid png_set_read_user_chunk_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIuser_chunk_ptr\fP\fB, png_user_chunk_ptr \fIread_user_chunk_fn\fP\fB);\fP + +\fBvoid png_set_read_user_transform_fn (png_structp \fP\fIpng_ptr\fP\fB, png_user_transform_ptr \fIread_user_transform_fn\fP\fB);\fP + +\fBvoid png_set_rgb_to_gray (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIerror_action\fP\fB, double \fP\fIred\fP\fB, double \fIgreen\fP\fB);\fP + +\fBvoid png_set_rgb_to_gray_fixed (png_structp \fP\fIpng_ptr\fP\fB, int error_action png_uint_32 \fP\fIred\fP\fB, png_uint_32 \fIgreen\fP\fB);\fP + +\fBvoid png_set_rows (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytepp \fIrow_pointers\fP\fB);\fP + +\fBvoid png_set_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_8p \fIsig_bit\fP\fB);\fP + +\fBvoid png_set_sCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIunit\fP\fB, double \fP\fIwidth\fP\fB, double \fIheight\fP\fB);\fP + +\fBvoid png_set_sCAL_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIunit\fP\fB, png_fixed_point \fP\fIwidth\fP\fB, png_fixed_point \fIheight\fP\fB);\fP + +\fBvoid png_set_sCAL_s (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIunit\fP\fB, png_charp \fP\fIwidth\fP\fB, png_charp \fIheight\fP\fB);\fP + +\fBvoid png_set_scale_16 (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_shift (png_structp \fP\fIpng_ptr\fP\fB, png_color_8p \fItrue_bits\fP\fB);\fP + +\fBvoid png_set_sig_bytes (png_structp \fP\fIpng_ptr\fP\fB, int \fInum_bytes\fP\fB);\fP + +\fBvoid png_set_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_spalette_p \fP\fIsplt_ptr\fP\fB, int \fInum_spalettes\fP\fB);\fP + +\fBvoid png_set_sRGB (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fIsrgb_intent\fP\fB);\fP + +\fBvoid png_set_sRGB_gAMA_and_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fIsrgb_intent\fP\fB);\fP + +\fBvoid png_set_strip_16 (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_strip_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_strip_error_numbers (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIstrip_mode\fP\fB);\fP + +\fBvoid png_set_swap (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_swap_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_text (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fItext_ptr\fP\fB, int \fInum_text\fP\fB);\fP + +\fBvoid png_set_text_compression_level (png_structp \fP\fIpng_ptr\fP\fB, int \fIlevel\fP\fB);\fP + +\fBvoid png_set_text_compression_mem_level (png_structp \fP\fIpng_ptr\fP\fB, int \fImem_level\fP\fB);\fP + +\fBvoid png_set_text_compression_strategy (png_structp \fP\fIpng_ptr\fP\fB, int \fIstrategy\fP\fB);\fP + +\fBvoid png_set_text_compression_window_bits (png_structp \fP\fIpng_ptr\fP\fB, int \fIwindow_bits\fP\fB);\fP + +\fBvoid png_set_text_compression_method (png_structp \fP\fIpng_ptr\fP\fB, int \fImethod\fP\fB);\fP + +\fBvoid png_set_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_timep \fImod_time\fP\fB);\fP + +\fBvoid png_set_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fItrans_alpha\fP\fB, int \fP\fInum_trans\fP\fB, png_color_16p \fItrans_color\fP\fB);\fP + +\fBvoid png_set_tRNS_to_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_set_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_unknown_chunkp \fP\fIunknowns\fP\fB, int \fP\fInum\fP\fB, int \fIlocation\fP\fB);\fP + +\fBvoid png_set_unknown_chunk_location (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIchunk\fP\fB, int \fIlocation\fP\fB);\fP + +\fBvoid png_set_user_limits (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIuser_width_max\fP\fB, png_uint_32 \fIuser_height_max\fP\fB);\fP + +\fBvoid png_set_user_transform_info (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIuser_transform_ptr\fP\fB, int \fP\fIuser_transform_depth\fP\fB, int \fIuser_transform_channels\fP\fB);\fP + +\fBvoid png_set_write_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIio_ptr\fP\fB, png_rw_ptr \fP\fIwrite_data_fn\fP\fB, png_flush_ptr \fIoutput_flush_fn\fP\fB);\fP + +\fBvoid png_set_write_status_fn (png_structp \fP\fIpng_ptr\fP\fB, png_write_status_ptr \fIwrite_row_fn\fP\fB);\fP + +\fBvoid png_set_write_user_transform_fn (png_structp \fP\fIpng_ptr\fP\fB, png_user_transform_ptr \fIwrite_user_transform_fn\fP\fB);\fP + +\fBint png_sig_cmp (png_bytep \fP\fIsig\fP\fB, size_t \fP\fIstart\fP\fB, size_t \fInum_to_check\fP\fB);\fP + +\fBvoid png_start_read_image (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_warning (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fBvoid png_write_chunk (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIchunk_name\fP\fB, png_bytep \fP\fIdata\fP\fB, size_t \fIlength\fP\fB);\fP + +\fBvoid png_write_chunk_data (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIdata\fP\fB, size_t \fIlength\fP\fB);\fP + +\fBvoid png_write_chunk_end (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_chunk_start (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIchunk_name\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_write_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_write_flush (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_image (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fIimage\fP\fB);\fP + +\fBvoid png_write_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_write_info_before_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_write_png (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fItransforms\fP\fB, png_voidp \fIparams\fP\fB);\fP + +\fBvoid png_write_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_write_rows (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fP\fIrow\fP\fB, png_uint_32 \fInum_rows\fP\fB);\fP + +\fBvoid png_write_sig (png_structp \fIpng_ptr\fP\fB);\fP + +.SH DESCRIPTION +The +.I libpng +library supports encoding, decoding, and various manipulations of +the Portable Network Graphics (PNG) format image files. It uses the +.IR zlib(3) +compression library. +Following is a copy of the libpng-manual.txt file that accompanies libpng. + +.SH LIBPNG.TXT +libpng-manual.txt - A description on how to use and modify libpng + + Copyright (c) 2018-2019 Cosmin Truta + Copyright (c) 1998-2018 Glenn Randers-Pehrson + + This document is released under the libpng license. + For conditions of distribution and use, see the disclaimer + and license in png.h + + Based on: + + libpng version 1.6.36, December 2018, through 1.6.37 - April 2019 + Updated and distributed by Cosmin Truta + Copyright (c) 2018-2019 Cosmin Truta + + libpng versions 0.97, January 1998, through 1.6.35 - July 2018 + Updated and distributed by Glenn Randers-Pehrson + Copyright (c) 1998-2018 Glenn Randers-Pehrson + + libpng 1.0 beta 6 - version 0.96 - May 28, 1997 + Updated and distributed by Andreas Dilger + Copyright (c) 1996, 1997 Andreas Dilger + + libpng 1.0 beta 2 - version 0.88 - January 26, 1996 + For conditions of distribution and use, see copyright + notice in png.h. Copyright (c) 1995, 1996 Guy Eric + Schalnat, Group 42, Inc. + + Updated/rewritten per request in the libpng FAQ + Copyright (c) 1995, 1996 Frank J. T. Wojcik + December 18, 1995 & January 20, 1996 + + TABLE OF CONTENTS + + I. Introduction + II. Structures + III. Reading + IV. Writing + V. Simplified API + VI. Modifying/Customizing libpng + VII. MNG support + VIII. Changes to Libpng from version 0.88 + IX. Changes to Libpng from version 1.0.x to 1.2.x + X. Changes to Libpng from version 1.0.x/1.2.x to 1.4.x + XI. Changes to Libpng from version 1.4.x to 1.5.x + XII. Changes to Libpng from version 1.5.x to 1.6.x + XIII. Detecting libpng + XIV. Source code repository + XV. Coding style + +.SH I. Introduction + +This file describes how to use and modify the PNG reference library +(known as libpng) for your own use. In addition to this +file, example.c is a good starting point for using the library, as +it is heavily commented and should include everything most people +will need. We assume that libpng is already installed; see the +INSTALL file for instructions on how to configure and install libpng. + +For examples of libpng usage, see the files "example.c", "pngtest.c", +and the files in the "contrib" directory, all of which are included in +the libpng distribution. + +Libpng was written as a companion to the PNG specification, as a way +of reducing the amount of time and effort it takes to support the PNG +file format in application programs. + +The PNG specification (second edition), November 2003, is available as +a W3C Recommendation and as an ISO Standard (ISO/IEC 15948:2004 (E)) at +. +The W3C and ISO documents have identical technical content. + +The PNG-1.2 specification is available at +. +It is technically equivalent +to the PNG specification (second edition) but has some additional material. + +The PNG-1.0 specification is available as RFC 2083 at + and as a +W3C Recommendation at . + +Some additional chunks are described in the special-purpose public chunks +documents at + +Other information +about PNG, and the latest version of libpng, can be found at the PNG home +page, . + +Most users will not have to modify the library significantly; advanced +users may want to modify it more. All attempts were made to make it as +complete as possible, while keeping the code easy to understand. +Currently, this library only supports C. Support for other languages +is being considered. + +Libpng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy +to use. The ultimate goal of libpng is to promote the acceptance of +the PNG file format in whatever way possible. While there is still +work to be done (see the TODO file), libpng should cover the +majority of the needs of its users. + +Libpng uses zlib for its compression and decompression of PNG files. +Further information about zlib, and the latest version of zlib, can +be found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than PNG files, and can be used without libpng. +See the documentation delivered with zlib for more details. +You can usually find the source files for the zlib utility wherever you +find the libpng source files. + +Libpng is thread safe, provided the threads are using different +instances of the structures. Each thread should have its own +png_struct and png_info instances, and thus its own image. +Libpng does not protect itself against two threads using the +same instance of a structure. + +.SH II. Structures + +There are two main structures that are important to libpng, png_struct +and png_info. Both are internal structures that are no longer exposed +in the libpng interface (as of libpng 1.5.0). + +The png_info structure is designed to provide information about the +PNG file. At one time, the fields of png_info were intended to be +directly accessible to the user. However, this tended to cause problems +with applications using dynamically loaded libraries, and as a result +a set of interface functions for png_info (the png_get_*() and png_set_*() +functions) was developed, and direct access to the png_info fields was +deprecated.. + +The png_struct structure is the object used by the library to decode a +single image. As of 1.5.0 this structure is also not exposed. + +Almost all libpng APIs require a pointer to a png_struct as the first argument. +Many (in particular the png_set and png_get APIs) also require a pointer +to png_info as the second argument. Some application visible macros +defined in png.h designed for basic data access (reading and writing +integers in the PNG format) don't take a png_info pointer, but it's almost +always safe to assume that a (png_struct*) has to be passed to call an API +function. + +You can have more than one png_info structure associated with an image, +as illustrated in pngtest.c, one for information valid prior to the +IDAT chunks and another (called "end_info" below) for things after them. + +The png.h header file is an invaluable reference for programming with libpng. +And while I'm on the topic, make sure you include the libpng header file: + +#include + +and also (as of libpng-1.5.0) the zlib header file, if you need it: + +#include + +.SS Types + +The png.h header file defines a number of integral types used by the +APIs. Most of these are fairly obvious; for example types corresponding +to integers of particular sizes and types for passing color values. + +One exception is how non-integral numbers are handled. For application +convenience most APIs that take such numbers have C (double) arguments; +however, internally PNG, and libpng, use 32 bit signed integers and encode +the value by multiplying by 100,000. As of libpng 1.5.0 a convenience +macro PNG_FP_1 is defined in png.h along with a type (png_fixed_point) +which is simply (png_int_32). + +All APIs that take (double) arguments also have a matching API that +takes the corresponding fixed point integer arguments. The fixed point +API has the same name as the floating point one with "_fixed" appended. +The actual range of values permitted in the APIs is frequently less than +the full range of (png_fixed_point) (\-21474 to +21474). When APIs require +a non-negative argument the type is recorded as png_uint_32 above. Consult +the header file and the text below for more information. + +Special care must be take with sCAL chunk handling because the chunk itself +uses non-integral values encoded as strings containing decimal floating point +numbers. See the comments in the header file. + +.SS Configuration + +The main header file function declarations are frequently protected by C +preprocessing directives of the form: + + #ifdef PNG_feature_SUPPORTED + declare-function + #endif + ... + #ifdef PNG_feature_SUPPORTED + use-function + #endif + +The library can be built without support for these APIs, although a +standard build will have all implemented APIs. Application programs +should check the feature macros before using an API for maximum +portability. From libpng 1.5.0 the feature macros set during the build +of libpng are recorded in the header file "pnglibconf.h" and this file +is always included by png.h. + +If you don't need to change the library configuration from the default, skip to +the next section ("Reading"). + +Notice that some of the makefiles in the 'scripts' directory and (in 1.5.0) all +of the build project files in the 'projects' directory simply copy +scripts/pnglibconf.h.prebuilt to pnglibconf.h. This means that these build +systems do not permit easy auto-configuration of the library - they only +support the default configuration. + +The easiest way to make minor changes to the libpng configuration when +auto-configuration is supported is to add definitions to the command line +using (typically) CPPFLAGS. For example: + +CPPFLAGS=\-DPNG_NO_FLOATING_ARITHMETIC + +will change the internal libpng math implementation for gamma correction and +other arithmetic calculations to fixed point, avoiding the need for fast +floating point support. The result can be seen in the generated pnglibconf.h - +make sure it contains the changed feature macro setting. + +If you need to make more extensive configuration changes - more than one or two +feature macro settings - you can either add \-DPNG_USER_CONFIG to the build +command line and put a list of feature macro settings in pngusr.h or you can set +DFA_XTRA (a makefile variable) to a file containing the same information in the +form of 'option' settings. + +A. Changing pnglibconf.h + +A variety of methods exist to build libpng. Not all of these support +reconfiguration of pnglibconf.h. To reconfigure pnglibconf.h it must either be +rebuilt from scripts/pnglibconf.dfa using awk or it must be edited by hand. + +Hand editing is achieved by copying scripts/pnglibconf.h.prebuilt to +pnglibconf.h and changing the lines defining the supported features, paying +very close attention to the 'option' information in scripts/pnglibconf.dfa +that describes those features and their requirements. This is easy to get +wrong. + +B. Configuration using DFA_XTRA + +Rebuilding from pnglibconf.dfa is easy if a functioning 'awk', or a later +variant such as 'nawk' or 'gawk', is available. The configure build will +automatically find an appropriate awk and build pnglibconf.h. +The scripts/pnglibconf.mak file contains a set of make rules for doing the +same thing if configure is not used, and many of the makefiles in the scripts +directory use this approach. + +When rebuilding simply write a new file containing changed options and set +DFA_XTRA to the name of this file. This causes the build to append the new file +to the end of scripts/pnglibconf.dfa. The pngusr.dfa file should contain lines +of the following forms: + +everything = off + +This turns all optional features off. Include it at the start of pngusr.dfa to +make it easier to build a minimal configuration. You will need to turn at least +some features on afterward to enable either reading or writing code, or both. + +option feature on +option feature off + +Enable or disable a single feature. This will automatically enable other +features required by a feature that is turned on or disable other features that +require a feature which is turned off. Conflicting settings will cause an error +message to be emitted by awk. + +setting feature default value + +Changes the default value of setting 'feature' to 'value'. There are a small +number of settings listed at the top of pnglibconf.h, they are documented in the +source code. Most of these values have performance implications for the library +but most of them have no visible effect on the API. Some can also be overridden +from the API. + +This method of building a customized pnglibconf.h is illustrated in +contrib/pngminim/*. See the "$(PNGCONF):" target in the makefile and +pngusr.dfa in these directories. + +C. Configuration using PNG_USER_CONFIG + +If \-DPNG_USER_CONFIG is added to the CPPFLAGS when pnglibconf.h is built, +the file pngusr.h will automatically be included before the options in +scripts/pnglibconf.dfa are processed. Your pngusr.h file should contain only +macro definitions turning features on or off or setting settings. + +Apart from the global setting "everything = off" all the options listed above +can be set using macros in pngusr.h: + +#define PNG_feature_SUPPORTED + +is equivalent to: + +option feature on + +#define PNG_NO_feature + +is equivalent to: + +option feature off + +#define PNG_feature value + +is equivalent to: + +setting feature default value + +Notice that in both cases, pngusr.dfa and pngusr.h, the contents of the +pngusr file you supply override the contents of scripts/pnglibconf.dfa + +If confusing or incomprehensible behavior results it is possible to +examine the intermediate file pnglibconf.dfn to find the full set of +dependency information for each setting and option. Simply locate the +feature in the file and read the C comments that precede it. + +This method is also illustrated in the contrib/pngminim/* makefiles and +pngusr.h. + +.SH III. Reading + +We'll now walk you through the possible functions to call when reading +in a PNG file sequentially, briefly explaining the syntax and purpose +of each one. See example.c and png.h for more detail. While +progressive reading is covered in the next section, you will still +need some of the functions discussed in this section to read a PNG +file. + +.SS Setup + +You will want to do the I/O initialization(*) before you get into libpng, +so if it doesn't work, you don't have much to undo. Of course, you +will also want to insure that you are, in fact, dealing with a PNG +file. Libpng provides a simple check to see if a file is a PNG file. +To use it, pass in the first 1 to 8 bytes of the file to the function +png_sig_cmp(), and it will return 0 (false) if the bytes match the +corresponding bytes of the PNG signature, or nonzero (true) otherwise. +Of course, the more bytes you pass in, the greater the accuracy of the +prediction. + +If you are intending to keep the file pointer open for use in libpng, +you must ensure you don't read more than 8 bytes from the beginning +of the file, and you also have to make a call to png_set_sig_bytes() +with the number of bytes you read from the beginning. Libpng will +then only check the bytes (if any) that your program didn't read. + +(*): If you are not using the standard I/O functions, you will need +to replace them with custom functions. See the discussion under +Customizing libpng. + + FILE *fp = fopen(file_name, "rb"); + if (!fp) + { + return ERROR; + } + + if (fread(header, 1, number, fp) != number) + { + return ERROR; + } + + is_png = !png_sig_cmp(header, 0, number); + if (!is_png) + { + return NOT_PNG; + } + +Next, png_struct and png_info need to be allocated and initialized. In +order to ensure that the size of these structures is correct even with a +dynamically linked libpng, there are functions to initialize and +allocate the structures. We also pass the library version, optional +pointers to error handling functions, and a pointer to a data struct for +use by the error functions, if necessary (the pointer and functions can +be NULL if the default error handlers are to be used). See the section +on Changes to Libpng below regarding the old initialization functions. +The structure allocation functions quietly return NULL if they fail to +create the structure, so your application should check for that. + + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + + if (!png_ptr) + return ERROR; + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, (png_infopp)NULL); + return ERROR; + } + +If you want to use your own memory allocation routines, +use a libpng that was built with PNG_USER_MEM_SUPPORTED defined, and use +png_create_read_struct_2() instead of png_create_read_struct(): + + png_structp png_ptr = png_create_read_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +The error handling routines passed to png_create_read_struct() +and the memory alloc/free routines passed to png_create_struct_2() +are only necessary if you are not using the libpng supplied error +handling and memory alloc/free functions. + +When libpng encounters an error, it expects to longjmp back +to your routine. Therefore, you will need to call setjmp and pass +your png_jmpbuf(png_ptr). If you read the file from different +routines, you will need to update the longjmp buffer every time you enter +a new routine that will call a png_*() function. + +See your documentation of setjmp/longjmp for your compiler for more +information on setjmp/longjmp. See the discussion on libpng error +handling in the Customizing Libpng section below for more information +on the libpng error handling. If an error occurs, and libpng longjmp's +back to your setjmp, you will want to call png_destroy_read_struct() to +free any memory. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + fclose(fp); + return ERROR; + } + +Pass (png_infopp)NULL instead of &end_info if you didn't create +an end_info structure. + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_NO_SETJMP, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +You can #define PNG_ABORT() to a function that does something +more useful than abort(), as long as your function does not +return. + +Now you need to set up the input code. The default for libpng is to +use the C function fread(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. If you wish to handle reading data in another +way, you need not call the png_init_io() function, but you must then +implement the libpng I/O methods discussed in the Customizing Libpng +section below. + + png_init_io(png_ptr, fp); + +If you had previously opened the file and read any of the signature from +the beginning in order to see if this was a PNG file, you need to let +libpng know that there are some bytes missing from the start of the file. + + png_set_sig_bytes(png_ptr, number); + +You can change the zlib compression buffer size to be used while +reading compressed data with + + png_set_compression_buffer_size(png_ptr, buffer_size); + +where the default size is 8192 bytes. Note that the buffer size +is changed immediately and the buffer is reallocated immediately, +instead of setting a flag to be acted upon later. + +If you want CRC errors to be handled in a different manner than +the default, use + + png_set_crc_action(png_ptr, crit_action, ancil_action); + +The values for png_set_crc_action() say how libpng is to handle CRC errors in +ancillary and critical chunks, and whether to use the data contained +therein. Starting with libpng-1.6.26, this also governs how an ADLER32 error +is handled while reading the IDAT chunk. Note that it is impossible to +"discard" data in a critical chunk. + +Choices for (int) crit_action are + PNG_CRC_DEFAULT 0 error/quit + PNG_CRC_ERROR_QUIT 1 error/quit + PNG_CRC_WARN_USE 3 warn/use data + PNG_CRC_QUIET_USE 4 quiet/use data + PNG_CRC_NO_CHANGE 5 use the current value + +Choices for (int) ancil_action are + PNG_CRC_DEFAULT 0 error/quit + PNG_CRC_ERROR_QUIT 1 error/quit + PNG_CRC_WARN_DISCARD 2 warn/discard data + PNG_CRC_WARN_USE 3 warn/use data + PNG_CRC_QUIET_USE 4 quiet/use data + PNG_CRC_NO_CHANGE 5 use the current value + +When the setting for crit_action is PNG_CRC_QUIET_USE, the CRC and ADLER32 +checksums are not only ignored, but they are not evaluated. + +.SS Setting up callback code + +You can set up a callback function to handle any unknown chunks in the +input stream. You must supply the function + + read_chunk_callback(png_structp png_ptr, + png_unknown_chunkp chunk); + { + /* The unknown chunk structure contains your + chunk data, along with similar data for any other + unknown chunks: */ + + png_byte name[5]; + png_byte *data; + size_t size; + + /* Note that libpng has already taken care of + the CRC handling */ + + /* put your code here. Search for your chunk in the + unknown chunk structure, process it, and return one + of the following: */ + + return \-n; /* chunk had an error */ + return 0; /* did not recognize */ + return n; /* success */ + } + +(You can give your function another name that you like instead of +"read_chunk_callback") + +To inform libpng about your function, use + + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, + read_chunk_callback); + +This names not only the callback function, but also a user pointer that +you can retrieve with + + png_get_user_chunk_ptr(png_ptr); + +If you call the png_set_read_user_chunk_fn() function, then all unknown +chunks which the callback does not handle will be saved when read. You can +cause them to be discarded by returning '1' ("handled") instead of '0'. This +behavior will change in libpng 1.7 and the default handling set by the +png_set_keep_unknown_chunks() function, described below, will be used when the +callback returns 0. If you want the existing behavior you should set the global +default to PNG_HANDLE_CHUNK_IF_SAFE now; this is compatible with all current +versions of libpng and with 1.7. Libpng 1.6 issues a warning if you keep the +default, or PNG_HANDLE_CHUNK_NEVER, and the callback returns 0. + +At this point, you can set up a callback function that will be +called after each row has been read, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void read_row_callback(png_structp png_ptr, + png_uint_32 row, int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "read_row_callback") + +To inform libpng about your function, use + + png_set_read_status_fn(png_ptr, read_row_callback); + +When this function is called the row has already been completely processed and +the 'row' and 'pass' refer to the next row to be handled. For the +non-interlaced case the row that was just handled is simply one less than the +passed in row number, and pass will always be 0. For the interlaced case the +same applies unless the row value is 0, in which case the row just handled was +the last one from one of the preceding passes. Because interlacing may skip a +pass you cannot be sure that the preceding pass is just 'pass\-1'; if you really +need to know what the last pass is record (row,pass) from the callback and use +the last recorded value each time. + +As with the user transform you can find the output row using the +PNG_ROW_FROM_PASS_ROW macro. + +.SS Unknown-chunk handling + +Now you get to set the way the library processes unknown chunks in the +input PNG stream. Both known and unknown chunks will be read. Normal +behavior is that known chunks will be parsed into information in +various info_ptr members while unknown chunks will be discarded. This +behavior can be wasteful if your application will never use some known +chunk types. To change this, you can call: + + png_set_keep_unknown_chunks(png_ptr, keep, + chunk_list, num_chunks); + + keep - 0: default unknown chunk handling + 1: ignore; do not keep + 2: keep only if safe-to-copy + 3: keep even if unsafe-to-copy + + You can use these definitions: + PNG_HANDLE_CHUNK_AS_DEFAULT 0 + PNG_HANDLE_CHUNK_NEVER 1 + PNG_HANDLE_CHUNK_IF_SAFE 2 + PNG_HANDLE_CHUNK_ALWAYS 3 + + chunk_list - list of chunks affected (a byte string, + five bytes per chunk, NULL or '\0' if + num_chunks is positive; ignored if + numchunks <= 0). + + num_chunks - number of chunks affected; if 0, all + unknown chunks are affected. If positive, + only the chunks in the list are affected, + and if negative all unknown chunks and + all known chunks except for the IHDR, + PLTE, tRNS, IDAT, and IEND chunks are + affected. + +Unknown chunks declared in this way will be saved as raw data onto a +list of png_unknown_chunk structures. If a chunk that is normally +known to libpng is named in the list, it will be handled as unknown, +according to the "keep" directive. If a chunk is named in successive +instances of png_set_keep_unknown_chunks(), the final instance will +take precedence. The IHDR and IEND chunks should not be named in +chunk_list; if they are, libpng will process them normally anyway. +If you know that your application will never make use of some particular +chunks, use PNG_HANDLE_CHUNK_NEVER (or 1) as demonstrated below. + +Here is an example of the usage of png_set_keep_unknown_chunks(), +where the private "vpAg" chunk will later be processed by a user chunk +callback function: + + png_byte vpAg[5]={118, 112, 65, 103, (png_byte) '\0'}; + + #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + png_byte unused_chunks[]= + { + 104, 73, 83, 84, (png_byte) '\0', /* hIST */ + 105, 84, 88, 116, (png_byte) '\0', /* iTXt */ + 112, 67, 65, 76, (png_byte) '\0', /* pCAL */ + 115, 67, 65, 76, (png_byte) '\0', /* sCAL */ + 115, 80, 76, 84, (png_byte) '\0', /* sPLT */ + 116, 73, 77, 69, (png_byte) '\0', /* tIME */ + }; + #endif + + ... + + #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* ignore all unknown chunks + * (use global setting "2" for libpng16 and earlier): + */ + png_set_keep_unknown_chunks(read_ptr, 2, NULL, 0); + + /* except for vpAg: */ + png_set_keep_unknown_chunks(read_ptr, 2, vpAg, 1); + + /* also ignore unused known chunks: */ + png_set_keep_unknown_chunks(read_ptr, 1, unused_chunks, + (int)(sizeof unused_chunks)/5); + #endif + +.SS User limits + +The PNG specification allows the width and height of an image to be as +large as 2^(31\-1 (0x7fffffff), or about 2.147 billion rows and columns. +For safety, libpng imposes a default limit of 1 million rows and columns. +Larger images will be rejected immediately with a png_error() call. If +you wish to change these limits, you can use + + png_set_user_limits(png_ptr, width_max, height_max); + +to set your own limits (libpng may reject some very wide images +anyway because of potential buffer overflow conditions). + +You should put this statement after you create the PNG structure and +before calling png_read_info(), png_read_png(), or png_process_data(). + +When writing a PNG datastream, put this statement before calling +png_write_info() or png_write_png(). + +If you need to retrieve the limits that are being applied, use + + width_max = png_get_user_width_max(png_ptr); + height_max = png_get_user_height_max(png_ptr); + +The PNG specification sets no limit on the number of ancillary chunks +allowed in a PNG datastream. By default, libpng imposes a limit of +a total of 1000 sPLT, tEXt, iTXt, zTXt, and unknown chunks to be stored. +If you have set up both info_ptr and end_info_ptr, the limit applies +separately to each. You can change the limit on the total number of such +chunks that will be stored, with + + png_set_chunk_cache_max(png_ptr, user_chunk_cache_max); + +where 0x7fffffffL means unlimited. You can retrieve this limit with + + chunk_cache_max = png_get_chunk_cache_max(png_ptr); + +Libpng imposes a limit of 8 Megabytes (8,000,000 bytes) on the amount of +memory that any chunk other than IDAT can occupy, originally or when +decompressed (prior to libpng-1.6.32 the limit was only applied to compressed +chunks after decompression). You can change this limit with + + png_set_chunk_malloc_max(png_ptr, user_chunk_malloc_max); + +and you can retrieve the limit with + + chunk_malloc_max = png_get_chunk_malloc_max(png_ptr); + +Any chunks that would cause either of these limits to be exceeded will +be ignored. + +.SS Information about your system + +If you intend to display the PNG or to incorporate it in other image data you +need to tell libpng information about your display or drawing surface so that +libpng can convert the values in the image to match the display. + +From libpng-1.5.4 this information can be set before reading the PNG file +header. In earlier versions png_set_gamma() existed but behaved incorrectly if +called before the PNG file header had been read and png_set_alpha_mode() did not +exist. + +If you need to support versions prior to libpng-1.5.4 test the version number +as illustrated below using "PNG_LIBPNG_VER >= 10504" and follow the procedures +described in the appropriate manual page. + +You give libpng the encoding expected by your system expressed as a 'gamma' +value. You can also specify a default encoding for the PNG file in +case the required information is missing from the file. By default libpng +assumes that the PNG data matches your system, to keep this default call: + + png_set_gamma(png_ptr, screen_gamma, output_gamma); + +or you can use the fixed point equivalent: + + png_set_gamma_fixed(png_ptr, PNG_FP_1*screen_gamma, + PNG_FP_1*output_gamma); + +If you don't know the gamma for your system it is probably 2.2 - a good +approximation to the IEC standard for display systems (sRGB). If images are +too contrasty or washed out you got the value wrong - check your system +documentation! + +Many systems permit the system gamma to be changed via a lookup table in the +display driver, a few systems, including older Macs, change the response by +default. As of 1.5.4 three special values are available to handle common +situations: + + PNG_DEFAULT_sRGB: Indicates that the system conforms to the + IEC 61966-2-1 standard. This matches almost + all systems. + PNG_GAMMA_MAC_18: Indicates that the system is an older + (pre Mac OS 10.6) Apple Macintosh system with + the default settings. + PNG_GAMMA_LINEAR: Just the fixed point value for 1.0 - indicates + that the system expects data with no gamma + encoding. + +You would use the linear (unencoded) value if you need to process the pixel +values further because this avoids the need to decode and re-encode each +component value whenever arithmetic is performed. A lot of graphics software +uses linear values for this reason, often with higher precision component values +to preserve overall accuracy. + + +The output_gamma value expresses how to decode the output values, not how +they are encoded. The values used correspond to the normal numbers used to +describe the overall gamma of a computer display system; for example 2.2 for +an sRGB conformant system. The values are scaled by 100000 in the _fixed +version of the API (so 220000 for sRGB.) + +The inverse of the value is always used to provide a default for the PNG file +encoding if it has no gAMA chunk and if png_set_gamma() has not been called +to override the PNG gamma information. + +When the ALPHA_OPTIMIZED mode is selected the output gamma is used to encode +opaque pixels however pixels with lower alpha values are not encoded, +regardless of the output gamma setting. + +When the standard Porter Duff handling is requested with mode 1 the output +encoding is set to be linear and the output_gamma value is only relevant +as a default for input data that has no gamma information. The linear output +encoding will be overridden if png_set_gamma() is called - the results may be +highly unexpected! + +The following numbers are derived from the sRGB standard and the research +behind it. sRGB is defined to be approximated by a PNG gAMA chunk value of +0.45455 (1/2.2) for PNG. The value implicitly includes any viewing +correction required to take account of any differences in the color +environment of the original scene and the intended display environment; the +value expresses how to *decode* the image for display, not how the original +data was *encoded*. + +sRGB provides a peg for the PNG standard by defining a viewing environment. +sRGB itself, and earlier TV standards, actually use a more complex transform +(a linear portion then a gamma 2.4 power law) than PNG can express. (PNG is +limited to simple power laws.) By saying that an image for direct display on +an sRGB conformant system should be stored with a gAMA chunk value of 45455 +(11.3.3.2 and 11.3.3.5 of the ISO PNG specification) the PNG specification +makes it possible to derive values for other display systems and +environments. + +The Mac value is deduced from the sRGB based on an assumption that the actual +extra viewing correction used in early Mac display systems was implemented as +a power 1.45 lookup table. + +Any system where a programmable lookup table is used or where the behavior of +the final display device characteristics can be changed requires system +specific code to obtain the current characteristic. However this can be +difficult and most PNG gamma correction only requires an approximate value. + +By default, if png_set_alpha_mode() is not called, libpng assumes that all +values are unencoded, linear, values and that the output device also has a +linear characteristic. This is only very rarely correct - it is invariably +better to call png_set_alpha_mode() with PNG_DEFAULT_sRGB than rely on the +default if you don't know what the right answer is! + +The special value PNG_GAMMA_MAC_18 indicates an older Mac system (pre Mac OS +10.6) which used a correction table to implement a somewhat lower gamma on an +otherwise sRGB system. + +Both these values are reserved (not simple gamma values) in order to allow +more precise correction internally in the future. + +NOTE: the values can be passed to either the fixed or floating +point APIs, but the floating point API will also accept floating point +values. + +The second thing you may need to tell libpng about is how your system handles +alpha channel information. Some, but not all, PNG files contain an alpha +channel. To display these files correctly you need to compose the data onto a +suitable background, as described in the PNG specification. + +Libpng only supports composing onto a single color (using png_set_background; +see below). Otherwise you must do the composition yourself and, in this case, +you may need to call png_set_alpha_mode: + + #if PNG_LIBPNG_VER >= 10504 + png_set_alpha_mode(png_ptr, mode, screen_gamma); + #else + png_set_gamma(png_ptr, screen_gamma, 1.0/screen_gamma); + #endif + +The screen_gamma value is the same as the argument to png_set_gamma; however, +how it affects the output depends on the mode. png_set_alpha_mode() sets the +file gamma default to 1/screen_gamma, so normally you don't need to call +png_set_gamma. If you need different defaults call png_set_gamma() before +png_set_alpha_mode() - if you call it after it will override the settings made +by png_set_alpha_mode(). + +The mode is as follows: + + PNG_ALPHA_PNG: The data is encoded according to the PNG +specification. Red, green and blue, or gray, components are +gamma encoded color values and are not premultiplied by the +alpha value. The alpha value is a linear measure of the +contribution of the pixel to the corresponding final output pixel. + +You should normally use this format if you intend to perform +color correction on the color values; most, maybe all, color +correction software has no handling for the alpha channel and, +anyway, the math to handle pre-multiplied component values is +unnecessarily complex. + +Before you do any arithmetic on the component values you need +to remove the gamma encoding and multiply out the alpha +channel. See the PNG specification for more detail. It is +important to note that when an image with an alpha channel is +scaled, linear encoded, pre-multiplied component values must +be used! + +The remaining modes assume you don't need to do any further color correction or +that if you do, your color correction software knows all about alpha (it +probably doesn't!). They 'associate' the alpha with the color information by +storing color channel values that have been scaled by the alpha. The +advantage is that the color channels can be resampled (the image can be +scaled) in this form. The disadvantage is that normal practice is to store +linear, not (gamma) encoded, values and this requires 16-bit channels for +still images rather than the 8-bit channels that are just about sufficient if +gamma encoding is used. In addition all non-transparent pixel values, +including completely opaque ones, must be gamma encoded to produce the final +image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes +described below (the latter being the two common names for associated alpha +color channels). Note that PNG files always contain non-associated color +channels; png_set_alpha_mode() with one of the modes causes the decoder to +convert the pixels to an associated form before returning them to your +application. + +Since it is not necessary to perform arithmetic on opaque color values so +long as they are not to be resampled and are in the final color space it is +possible to optimize the handling of alpha by storing the opaque pixels in +the PNG format (adjusted for the output color space) while storing partially +opaque pixels in the standard, linear, format. The accuracy required for +standard alpha composition is relatively low, because the pixels are +isolated, therefore typically the accuracy loss in storing 8-bit linear +values is acceptable. (This is not true if the alpha channel is used to +simulate transparency over large areas - use 16 bits or the PNG mode in +this case!) This is the 'OPTIMIZED' mode. For this mode a pixel is +treated as opaque only if the alpha value is equal to the maximum value. + + PNG_ALPHA_STANDARD: The data libpng produces is encoded in the +standard way assumed by most correctly written graphics software. +The gamma encoding will be removed by libpng and the +linear component values will be pre-multiplied by the +alpha channel. + +With this format the final image must be re-encoded to +match the display gamma before the image is displayed. +If your system doesn't do that, yet still seems to +perform arithmetic on the pixels without decoding them, +it is broken - check out the modes below. + +With PNG_ALPHA_STANDARD libpng always produces linear +component values, whatever screen_gamma you supply. The +screen_gamma value is, however, used as a default for +the file gamma if the PNG file has no gamma information. + +If you call png_set_gamma() after png_set_alpha_mode() you +will override the linear encoding. Instead the +pre-multiplied pixel values will be gamma encoded but +the alpha channel will still be linear. This may +actually match the requirements of some broken software, +but it is unlikely. + +While linear 8-bit data is often used it has +insufficient precision for any image with a reasonable +dynamic range. To avoid problems, and if your software +supports it, use png_set_expand_16() to force all +components to 16 bits. + + PNG_ALPHA_OPTIMIZED: This mode is the same as PNG_ALPHA_STANDARD +except that completely opaque pixels are gamma encoded according to +the screen_gamma value. Pixels with alpha less than 1.0 +will still have linear components. + +Use this format if you have control over your +compositing software and so don't do other arithmetic +(such as scaling) on the data you get from libpng. Your +compositing software can simply copy opaque pixels to +the output but still has linear values for the +non-opaque pixels. + +In normal compositing, where the alpha channel encodes +partial pixel coverage (as opposed to broad area +translucency), the inaccuracies of the 8-bit +representation of non-opaque pixels are irrelevant. + +You can also try this format if your software is broken; +it might look better. + + PNG_ALPHA_BROKEN: This is PNG_ALPHA_STANDARD; however, all component +values, including the alpha channel are gamma encoded. This is +broken because, in practice, no implementation that uses this choice +correctly undoes the encoding before handling alpha composition. Use this +choice only if other serious errors in the software or hardware you use +mandate it. In most cases of broken software or hardware the bug in the +final display manifests as a subtle halo around composited parts of the +image. You may not even perceive this as a halo; the composited part of +the image may simply appear separate from the background, as though it had +been cut out of paper and pasted on afterward. + +If you don't have to deal with bugs in software or hardware, or if you can fix +them, there are three recommended ways of using png_set_alpha_mode(): + + png_set_alpha_mode(png_ptr, PNG_ALPHA_PNG, + screen_gamma); + +You can do color correction on the result (libpng does not currently +support color correction internally). When you handle the alpha channel +you need to undo the gamma encoding and multiply out the alpha. + + png_set_alpha_mode(png_ptr, PNG_ALPHA_STANDARD, + screen_gamma); + png_set_expand_16(png_ptr); + +If you are using the high level interface, don't call png_set_expand_16(); +instead pass PNG_TRANSFORM_EXPAND_16 to the interface. + +With this mode you can't do color correction, but you can do arithmetic, +including composition and scaling, on the data without further processing. + + png_set_alpha_mode(png_ptr, PNG_ALPHA_OPTIMIZED, + screen_gamma); + +You can avoid the expansion to 16-bit components with this mode, but you +lose the ability to scale the image or perform other linear arithmetic. +All you can do is compose the result onto a matching output. Since this +mode is libpng-specific you also need to write your own composition +software. + +The following are examples of calls to png_set_alpha_mode to achieve the +required overall gamma correction and, where necessary, alpha +premultiplication. + + png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + +Choices for the alpha_mode are + + PNG_ALPHA_PNG 0 /* according to the PNG standard */ + PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ + PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ + PNG_ALPHA_PREMULTIPLIED 1 /* as above */ + PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ + PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_ALPHA_PNG is the default libpng handling of the alpha channel. It is not +pre-multiplied into the color components. In addition the call states +that the output is for a sRGB system and causes all PNG files without gAMA +chunks to be assumed to be encoded using sRGB. + + png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + +In this case the output is assumed to be something like an sRGB conformant +display preceded by a power-law lookup table of power 1.45. This is how +early Mac systems behaved. + + png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + +This is the classic Jim Blinn approach and will work in academic +environments where everything is done by the book. It has the shortcoming +of assuming that input PNG data with no gamma information is linear - this +is unlikely to be correct unless the PNG files were generated locally. +Most of the time the output precision will be so low as to show +significant banding in dark areas of the image. + + png_set_expand_16(pp); + png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + +This is a somewhat more realistic Jim Blinn inspired approach. PNG files +are assumed to have the sRGB encoding if not marked with a gamma value and +the output is always 16 bits per component. This permits accurate scaling +and processing of the data. If you know that your input PNG files were +generated locally you might need to replace PNG_DEFAULT_sRGB with the +correct value for your system. + + png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + +If you just need to composite the PNG image onto an existing background +and if you control the code that does this you can use the optimization +setting. In this case you just copy completely opaque pixels to the +output. For pixels that are not completely transparent (you just skip +those) you do the composition math using png_composite or png_composite_16 +below then encode the resultant 8-bit or 16-bit values to match the output +encoding. + + Other cases + +If neither the PNG nor the standard linear encoding work for you because +of the software or hardware you use then you have a big problem. The PNG +case will probably result in halos around the image. The linear encoding +will probably result in a washed out, too bright, image (it's actually too +contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably +substantially reduce the halos. Alternatively try: + + png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + +This option will also reduce the halos, but there will be slight dark +halos round the opaque parts of the image where the background is light. +In the OPTIMIZED mode the halos will be light halos where the background +is dark. Take your pick - the halos are unavoidable unless you can get +your hardware/software fixed! (The OPTIMIZED approach is slightly +faster.) + +When the default gamma of PNG files doesn't match the output gamma. +If you have PNG files with no gamma information png_set_alpha_mode allows +you to provide a default gamma, but it also sets the output gamma to the +matching value. If you know your PNG files have a gamma that doesn't +match the output you can take advantage of the fact that +png_set_alpha_mode always sets the output gamma but only sets the PNG +default if it is not already set: + + png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + +The first call sets both the default and the output gamma values, the +second call overrides the output gamma without changing the default. This +is easier than achieving the same effect with png_set_gamma. You must use +PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will +fire if more than one call to png_set_alpha_mode and png_set_background is +made in the same read operation, however multiple calls with PNG_ALPHA_PNG +are ignored. + +If you don't need, or can't handle, the alpha channel you can call +png_set_background() to remove it by compositing against a fixed color. Don't +call png_set_strip_alpha() to do this - it will leave spurious pixel values in +transparent parts of this image. + + png_set_background(png_ptr, &background_color, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1); + +The background_color is an RGB or grayscale value according to the data format +libpng will produce for you. Because you don't yet know the format of the PNG +file, if you call png_set_background at this point you must arrange for the +format produced by libpng to always have 8-bit or 16-bit components and then +store the color as an 8-bit or 16-bit color as appropriate. The color contains +separate gray and RGB component values, so you can let libpng produce gray or +RGB output according to the input format, but low bit depth grayscale images +must always be converted to at least 8-bit format. (Even though low bit depth +grayscale images can't have an alpha channel they can have a transparent +color!) + +You set the transforms you need later, either as flags to the high level +interface or libpng API calls for the low level interface. For reference the +settings and API calls required are: + +8-bit values: + PNG_TRANSFORM_SCALE_16 | PNG_EXPAND + png_set_expand(png_ptr); png_set_scale_16(png_ptr); + + If you must get exactly the same inaccurate results + produced by default in versions prior to libpng-1.5.4, + use PNG_TRANSFORM_STRIP_16 and png_set_strip_16(png_ptr) + instead. + +16-bit values: + PNG_TRANSFORM_EXPAND_16 + png_set_expand_16(png_ptr); + +In either case palette image data will be expanded to RGB. If you just want +color data you can add PNG_TRANSFORM_GRAY_TO_RGB or png_set_gray_to_rgb(png_ptr) +to the list. + +Calling png_set_background before the PNG file header is read will not work +prior to libpng-1.5.4. Because the failure may result in unexpected warnings or +errors it is therefore much safer to call png_set_background after the head has +been read. Unfortunately this means that prior to libpng-1.5.4 it cannot be +used with the high level interface. + +.SS The high-level read interface + +At this point there are two ways to proceed; through the high-level +read interface, or through a sequence of low-level read operations. +You can use the high-level interface if (a) you are willing to read +the entire image into memory, and (b) the input transformations +you want to do are limited to the following set: + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_SCALE_16 Strip 16-bit samples to + 8-bit accurately + PNG_TRANSFORM_STRIP_16 Chop 16-bit samples to + 8-bit less accurately + PNG_TRANSFORM_STRIP_ALPHA Discard the alpha channel + PNG_TRANSFORM_PACKING Expand 1, 2 and 4-bit + samples to bytes + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_EXPAND Perform set_expand() + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + PNG_TRANSFORM_GRAY_TO_RGB Expand grayscale samples + to RGB (or GA to RGBA) + PNG_TRANSFORM_EXPAND_16 Expand samples to 16 bits + +(This excludes setting a background color, doing gamma transformation, +quantizing, and setting filler.) If this is the case, simply do this: + + png_read_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the bitwise OR of some +set of transformation flags. This call is equivalent to png_read_info(), +followed the set of transformations indicated by the transform mask, +then png_read_image(), and finally png_read_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future input transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_read_png(). + +After you have called png_read_png(), you can retrieve the image data +with + + row_pointers = png_get_rows(png_ptr, info_ptr); + +where row_pointers is an array of pointers to the pixel data for each row: + + png_bytep row_pointers[height]; + +If you know your image size and pixel size ahead of time, you can allocate +row_pointers prior to calling png_read_png() with + + if (height > PNG_UINT_32_MAX/(sizeof (png_byte))) + png_error (png_ptr, + "Image is too tall to process in memory"); + + if (width > PNG_UINT_32_MAX/pixel_size) + png_error (png_ptr, + "Image is too wide to process in memory"); + + row_pointers = png_malloc(png_ptr, + height*(sizeof (png_bytep))); + + for (int i=0; i PNG_SIZE_MAX/(width*pixel_size)) { + png_error(png_ptr,"image_data buffer would be too large"); + } + + png_bytep buffer=png_malloc(png_ptr,height*width*pixel_size); + + for (int i=0; i) and +png_get_(png_ptr, info_ptr, ...) functions return non-zero if the +data has been read, or zero if it is missing. The parameters to the +png_get_ are set directly if they are simple data types, or a +pointer into the info_ptr is returned for any complex types. + +The colorspace data from gAMA, cHRM, sRGB, iCCP, and sBIT chunks +is simply returned to give the application information about how the +image was encoded. Libpng itself only does transformations using the file +gamma when combining semitransparent pixels with the background color, and, +since libpng-1.6.0, when converting between 8-bit sRGB and 16-bit linear pixels +within the simplified API. Libpng also uses the file gamma when converting +RGB to gray, beginning with libpng-1.0.5, if the application calls +png_set_rgb_to_gray()). + + png_get_PLTE(png_ptr, info_ptr, &palette, + &num_palette); + + palette - the palette for the file + (array of png_color) + + num_palette - number of entries in the palette + + png_get_gAMA(png_ptr, info_ptr, &file_gamma); + png_get_gAMA_fixed(png_ptr, info_ptr, &int_file_gamma); + + file_gamma - the gamma at which the file is + written (PNG_INFO_gAMA) + + int_file_gamma - 100,000 times the gamma at which the + file is written + + png_get_cHRM(png_ptr, info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y) + png_get_cHRM_XYZ(png_ptr, info_ptr, &red_X, &red_Y, &red_Z, + &green_X, &green_Y, &green_Z, &blue_X, &blue_Y, + &blue_Z) + png_get_cHRM_fixed(png_ptr, info_ptr, &int_white_x, + &int_white_y, &int_red_x, &int_red_y, + &int_green_x, &int_green_y, &int_blue_x, + &int_blue_y) + png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &int_red_X, &int_red_Y, + &int_red_Z, &int_green_X, &int_green_Y, + &int_green_Z, &int_blue_X, &int_blue_Y, + &int_blue_Z) + + {white,red,green,blue}_{x,y} + A color space encoding specified using the + chromaticities of the end points and the + white point. (PNG_INFO_cHRM) + + {red,green,blue}_{X,Y,Z} + A color space encoding specified using the + encoding end points - the CIE tristimulus + specification of the intended color of the red, + green and blue channels in the PNG RGB data. + The white point is simply the sum of the three + end points. (PNG_INFO_cHRM) + + png_get_sRGB(png_ptr, info_ptr, &srgb_intent); + + srgb_intent - the rendering intent (PNG_INFO_sRGB) + The presence of the sRGB chunk + means that the pixel data is in the + sRGB color space. This chunk also + implies specific values of gAMA and + cHRM. + + png_get_iCCP(png_ptr, info_ptr, &name, + &compression_type, &profile, &proflen); + + name - The profile name. + + compression_type - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + + profile - International Color Consortium color + profile data. May contain NULs. + + proflen - length of profile data in bytes. + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, + red, green, and blue channels, + whichever are appropriate for the + given color type (png_color_16) + + png_get_tRNS(png_ptr, info_ptr, &trans_alpha, + &num_trans, &trans_color); + + trans_alpha - array of alpha (transparency) + entries for palette (PNG_INFO_tRNS) + + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + trans_color - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + + png_get_eXIf_1(png_ptr, info_ptr, &num_exif, &exif); + (PNG_INFO_eXIf) + + exif - Exif profile (array of png_byte) + + png_get_hIST(png_ptr, info_ptr, &hist); + (PNG_INFO_hIST) + + hist - histogram of palette (array of + png_uint_16) + + png_get_tIME(png_ptr, info_ptr, &mod_time); + + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_get_bKGD(png_ptr, info_ptr, &background); + + background - background color (of type + png_color_16p) (PNG_VALID_bKGD) + valid 16-bit red, green and blue + values, regardless of color_type + + num_comments = png_get_text(png_ptr, info_ptr, + &text_ptr, &num_text); + + num_comments - number of comments + + text_ptr - array of png_text holding image + comments + + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + + text_ptr[i].text - text comments for current + keyword. Can be empty. + + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + + text_ptr[i].lang - language of comment (empty + string for unknown). + + text_ptr[i].lang_key - keyword in UTF-8 + (empty string for unknown). + + Note that the itxt_length, lang, and lang_key + members of the text_ptr structure only exist when the + library is built with iTXt chunk support. Prior to + libpng-1.4.0 the library was built by default without + iTXt support. Also note that when iTXt is supported, + they contain NULL pointers when the "compression" + field contains PNG_TEXT_COMPRESSION_NONE or + PNG_TEXT_COMPRESSION_zTXt. + + num_text - number of comments (same as + num_comments; you can put NULL here + to avoid the duplication) + + Note while png_set_text() will accept text, language, + and translated keywords that can be NULL pointers, the + structure returned by png_get_text will always contain + regular zero-terminated C strings. They might be + empty strings but they will never be NULL pointers. + + num_spalettes = png_get_sPLT(png_ptr, info_ptr, + &palette_ptr); + + num_spalettes - number of sPLT chunks read. + + palette_ptr - array of palette structures holding + contents of one or more sPLT chunks + read. + + png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, + &unit_type); + + offset_x - positive offset from the left edge + of the screen (can be negative) + + offset_y - positive offset from the top edge + of the screen (can be negative) + + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, + &unit_type); + + res_x - pixels/unit physical resolution in + x direction + + res_y - pixels/unit physical resolution in + x direction + + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_get_sCAL(png_ptr, info_ptr, &unit, &width, + &height) + + unit - physical scale units (an integer) + + width - width of a pixel in physical scale units + + height - height of a pixel in physical scale units + (width and height are doubles) + + png_get_sCAL_s(png_ptr, info_ptr, &unit, &width, + &height) + + unit - physical scale units (an integer) + + width - width of a pixel in physical scale units + (expressed as a string) + + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + num_unknown_chunks = png_get_unknown_chunks(png_ptr, + info_ptr, &unknowns) + + unknowns - array of png_unknown_chunk + structures holding unknown chunks + + unknowns[i].name - name of unknown chunk + + unknowns[i].data - data of unknown chunk + + unknowns[i].size - size of unknown chunk's data + + unknowns[i].location - position of chunk in file + + The value of "i" corresponds to the order in which the + chunks were read from the PNG file or inserted with the + png_set_unknown_chunks() function. + + The value of "location" is a bitwise "or" of + + PNG_HAVE_IHDR (0x01) + PNG_HAVE_PLTE (0x02) + PNG_AFTER_IDAT (0x08) + +The data from the pHYs chunk can be retrieved in several convenient +forms: + + res_x = png_get_x_pixels_per_meter(png_ptr, + info_ptr) + + res_y = png_get_y_pixels_per_meter(png_ptr, + info_ptr) + + res_x_and_y = png_get_pixels_per_meter(png_ptr, + info_ptr) + + res_x = png_get_x_pixels_per_inch(png_ptr, + info_ptr) + + res_y = png_get_y_pixels_per_inch(png_ptr, + info_ptr) + + res_x_and_y = png_get_pixels_per_inch(png_ptr, + info_ptr) + + aspect_ratio = png_get_pixel_aspect_ratio(png_ptr, + info_ptr) + + Each of these returns 0 [signifying "unknown"] if + the data is not present or if res_x is 0; + res_x_and_y is 0 if res_x != res_y + + Note that because of the way the resolutions are + stored internally, the inch conversions won't + come out to exactly even number. For example, + 72 dpi is stored as 0.28346 pixels/meter, and + when this is retrieved it is 71.9988 dpi, so + be sure to round the returned value appropriately + if you want to display a reasonable-looking result. + +The data from the oFFs chunk can be retrieved in several convenient +forms: + + x_offset = png_get_x_offset_microns(png_ptr, info_ptr); + + y_offset = png_get_y_offset_microns(png_ptr, info_ptr); + + x_offset = png_get_x_offset_inches(png_ptr, info_ptr); + + y_offset = png_get_y_offset_inches(png_ptr, info_ptr); + + Each of these returns 0 [signifying "unknown" if both + x and y are 0] if the data is not present or if the + chunk is present but the unit is the pixel. The + remark about inexact inch conversions applies here + as well, because a value in inches can't always be + converted to microns and back without some loss + of precision. + +For more information, see the +PNG specification for chunk contents. Be careful with trusting +rowbytes, as some of the transformations could increase the space +needed to hold a row (expand, filler, gray_to_rgb, etc.). +See png_read_update_info(), below. + +A quick word about text_ptr and num_text. PNG stores comments in +keyword/text pairs, one pair per chunk, with no limit on the number +of text chunks, and a 2^31 byte limit on their size. While there are +suggested keywords, there is no requirement to restrict the use to these +strings. It is strongly suggested that keywords and text be sensible +to humans (that's the point), so don't use abbreviations. Non-printing +symbols are not allowed. See the PNG specification for more details. +There is also no requirement to have text after the keyword. + +Keywords should be limited to 79 Latin-1 characters without leading or +trailing spaces, but non-consecutive spaces are allowed within the +keyword. It is possible to have the same keyword any number of times. +The text_ptr is an array of png_text structures, each holding a +pointer to a language string, a pointer to a keyword and a pointer to +a text string. The text string, language code, and translated +keyword may be empty or NULL pointers. The keyword/text +pairs are put into the array in the order that they are received. +However, some or all of the text chunks may be after the image, so, to +make sure you have read all the text chunks, don't mess with these +until after you read the stuff after the image. This will be +mentioned again below in the discussion that goes with png_read_end(). + +.SS Input transformations + +After you've read the header information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. + +Transformations you request are ignored if they don't have any meaning for a +particular input data format. However some transformations can have an effect +as a result of a previous transformation. If you specify a contradictory set of +transformations, for example both adding and removing the alpha channel, you +cannot predict the final result. + +The color used for the transparency values should be supplied in the same +format/depth as the current image data. It is stored in the same format/depth +as the image data in a tRNS chunk, so this is what libpng expects for this data. + +The color used for the background value depends on the need_expand argument as +described below. + +Data will be decoded into the supplied row buffers packed into bytes +unless the library has been told to transform it into another format. +For example, 4 bit/pixel paletted or grayscale data will be returned +2 pixels/byte with the leftmost pixel in the high-order bits of the byte, +unless png_set_packing() is called. 8-bit RGB data will be stored +in RGB RGB RGB format unless png_set_filler() or png_set_add_alpha() +is called to insert filler bytes, either before or after each RGB triplet. + +16-bit RGB data will be returned RRGGBB RRGGBB, with the most significant +byte of the color value first, unless png_set_scale_16() is called to +transform it to regular RGB RGB triplets, or png_set_filler() or +png_set_add alpha() is called to insert two filler bytes, either before +or after each RRGGBB triplet. Similarly, 8-bit or 16-bit grayscale data can +be modified with png_set_filler(), png_set_add_alpha(), png_set_strip_16(), +or png_set_scale_16(). + +The following code transforms grayscale images of less than 8 to 8 bits, +changes paletted images to RGB, and adds a full alpha channel if there is +transparency information in a tRNS chunk. This is most useful on +grayscale images with bit depths of 2 or 4 or if there is a multiple-image +viewing application that wishes to treat all images in the same way. + + 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); + +The first two functions are actually aliases for png_set_expand(), added +in libpng version 1.0.4, with the function names expanded to improve code +readability. In some future version they may actually do different +things. + +As of libpng version 1.2.9, png_set_expand_gray_1_2_4_to_8() was +added. It expands the sample depth without changing tRNS to alpha. + +As of libpng version 1.5.2, png_set_expand_16() was added. It behaves as +png_set_expand(); however, the resultant channels have 16 bits rather than 8. +Use this when the output color or gray channels are made linear to avoid fairly +severe accuracy loss. + + if (bit_depth < 16) + png_set_expand_16(png_ptr); + +PNG can have files with 16 bits per channel. If you only can handle +8 bits per channel, this will strip the pixels down to 8-bit. + + if (bit_depth == 16) +#if PNG_LIBPNG_VER >= 10504 + png_set_scale_16(png_ptr); +#else + png_set_strip_16(png_ptr); +#endif + +(The more accurate "png_set_scale_16()" API became available in libpng version +1.5.4). + +If you need to process the alpha channel on the image separately from the image +data (for example if you convert it to a bitmap mask) it is possible to have +libpng strip the channel leaving just RGB or gray data: + + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + +If you strip the alpha channel you need to find some other way of dealing with +the information. If, instead, you want to convert the image to an opaque +version with no alpha channel use png_set_background; see below. + +As of libpng version 1.5.2, almost all useful expansions are supported, the +major ommissions are conversion of grayscale to indexed images (which can be +done trivially in the application) and conversion of indexed to grayscale (which +can be done by a trivial manipulation of the palette.) + +In the following table, the 01 means grayscale with depth<8, 31 means +indexed with depth<8, other numerals represent the color type, "T" means +the tRNS chunk is present, A means an alpha channel is present, and O +means tRNS or alpha is present but all pixels in the image are opaque. + + FROM 01 31 0 0T 0O 2 2T 2O 3 3T 3O 4A 4O 6A 6O + TO + 01 - [G] - - - - - - - - - - - - - + 31 [Q] Q [Q] [Q] [Q] Q Q Q Q Q Q [Q] [Q] Q Q + 0 1 G + . . G G G G G G B B GB GB + 0T lt Gt t + . Gt G G Gt G G Bt Bt GBt GBt + 0O lt Gt t . + Gt Gt G Gt Gt G Bt Bt GBt GBt + 2 C P C C C + . . C - - CB CB B B + 2T Ct - Ct C C t + t - - - CBt CBt Bt Bt + 2O Ct - Ct C C t t + - - - CBt CBt Bt Bt + 3 [Q] p [Q] [Q] [Q] Q Q Q + . . [Q] [Q] Q Q + 3T [Qt] p [Qt][Q] [Q] Qt Qt Qt t + t [Qt][Qt] Qt Qt + 3O [Qt] p [Qt][Q] [Q] Qt Qt Qt t t + [Qt][Qt] Qt Qt + 4A lA G A T T GA GT GT GA GT GT + BA G GBA + 4O lA GBA A T T GA GT GT GA GT GT BA + GBA G + 6A CA PA CA C C A T tT PA P P C CBA + BA + 6O CA PBA CA C C A tT T PA P P CBA C BA + + +Within the matrix, + "+" identifies entries where 'from' and 'to' are the same. + "-" means the transformation is not supported. + "." means nothing is necessary (a tRNS chunk can just be ignored). + "t" means the transformation is obtained by png_set_tRNS. + "A" means the transformation is obtained by png_set_add_alpha(). + "X" means the transformation is obtained by png_set_expand(). + "1" means the transformation is obtained by + png_set_expand_gray_1_2_4_to_8() (and by png_set_expand() + if there is no transparency in the original or the final + format). + "C" means the transformation is obtained by png_set_gray_to_rgb(). + "G" means the transformation is obtained by png_set_rgb_to_gray(). + "P" means the transformation is obtained by + png_set_expand_palette_to_rgb(). + "p" means the transformation is obtained by png_set_packing(). + "Q" means the transformation is obtained by png_set_quantize(). + "T" means the transformation is obtained by + png_set_tRNS_to_alpha(). + "B" means the transformation is obtained by + png_set_background(), or png_strip_alpha(). + +When an entry has multiple transforms listed all are required to cause the +right overall transformation. When two transforms are separated by a comma +either will do the job. When transforms are enclosed in [] the transform should +do the job but this is currently unimplemented - a different format will result +if the suggested transformations are used. + +In PNG files, the alpha channel in an image +is the level of opacity. If you need the alpha channel in an image to +be the level of transparency instead of opacity, you can invert the +alpha channel (or the tRNS chunk data) after it's read, so that 0 is +fully opaque and 255 (in 8-bit or paletted images) or 65535 (in 16-bit +images) is fully transparent, with + + png_set_invert_alpha(png_ptr); + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit +files. This code expands to 1 pixel per byte without changing the +values of the pixels: + + if (bit_depth < 8) + png_set_packing(png_ptr); + +PNG files have possible bit depths of 1, 2, 4, 8, and 16. All pixels +stored in a PNG image have been "scaled" or "shifted" up to the next +higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] +to 8 bits/sample in the range [0, 255]). However, it is also possible +to convert the PNG pixel data back to the original bit depth of the +image. This call reduces the pixels back down to the original bit depth: + + png_color_8p sig_bit; + + if (png_get_sBIT(png_ptr, info_ptr, &sig_bit)) + png_set_shift(png_ptr, sig_bit); + +PNG files store 3-color pixels in red, green, blue order. This code +changes the storage of the pixels to blue, green, red: + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_bgr(png_ptr); + +PNG files store RGB pixels packed into 3 or 6 bytes. This code expands them +into 4 or 8 bytes for windowing systems that need them in this format: + + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE); + +where "filler" is the 8-bit or 16-bit number to fill with, and the location +is either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether +you want the filler before the RGB or after. When filling an 8-bit pixel, +the least significant 8 bits of the number are used, if a 16-bit number is +supplied. This transformation does not affect images that already have full +alpha channels. To add an opaque alpha channel, use filler=0xffff and +PNG_FILLER_AFTER which will generate RGBA pixels. + +Note that png_set_filler() does not change the color type. If you want +to do that, you can add a true alpha channel with + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY) + png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER); + +where "filler" contains the alpha value to assign to each pixel. +The png_set_add_alpha() function was added in libpng-1.2.7. + +If you are reading an image with an alpha channel, and you need the +data as ARGB instead of the normal PNG format RGBA: + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_swap_alpha(png_ptr); + +For some uses, you may want a grayscale image to be represented as +RGB. This code will do that conversion: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + +Conversely, you can convert an RGB or RGBA image to grayscale or grayscale +with alpha. + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_rgb_to_gray(png_ptr, error_action, + double red_weight, double green_weight); + + error_action = 1: silently do the conversion + + error_action = 2: issue a warning if the original + image has any pixel where + red != green or red != blue + + error_action = 3: issue an error and abort the + conversion if the original + image has any pixel where + red != green or red != blue + + red_weight: weight of red component + + green_weight: weight of green component + If either weight is negative, default + weights are used. + +In the corresponding fixed point API the red_weight and green_weight values are +simply scaled by 100,000: + + png_set_rgb_to_gray(png_ptr, error_action, + png_fixed_point red_weight, + png_fixed_point green_weight); + +If you have set error_action = 1 or 2, you can +later check whether the image really was gray, after processing +the image rows, with the png_get_rgb_to_gray_status(png_ptr) function. +It will return a png_byte that is zero if the image was gray or +1 if there were any non-gray pixels. Background and sBIT data +will be silently converted to grayscale, using the green channel +data for sBIT, regardless of the error_action setting. + +The default values come from the PNG file cHRM chunk if present; otherwise, the +defaults correspond to the ITU-R recommendation 709, and also the sRGB color +space, as recommended in the Charles Poynton's Colour FAQ, +Copyright (c) 2006-11-28 Charles Poynton, in section 9: + + + + Y = 0.2126 * R + 0.7152 * G + 0.0722 * B + +Previous versions of this document, 1998 through 2002, recommended a slightly +different formula: + + Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + +Libpng uses an integer approximation: + + Y = (6968 * R + 23434 * G + 2366 * B)/32768 + +The calculation is done in a linear colorspace, if the image gamma +can be determined. + +The png_set_background() function has been described already; it tells libpng to +composite images with alpha or simple transparency against the supplied +background color. For compatibility with versions of libpng earlier than +libpng-1.5.4 it is recommended that you call the function after reading the file +header, even if you don't want to use the color in a bKGD chunk, if one exists. + +If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid), +you may use this color, or supply another color more suitable for +the current display (e.g., the background color from a web page). You +need to tell libpng how the color is represented, both the format of the +component values in the color (the number of bits) and the gamma encoding of the +color. The function takes two arguments, background_gamma_mode and need_expand +to convey this information; however, only two combinations are likely to be +useful: + + png_color_16 my_background; + png_color_16p image_background; + + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1/*needs to be expanded*/, 1); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*do not expand*/, 1); + +The second call was described above - my_background is in the format of the +final, display, output produced by libpng. Because you now know the format of +the PNG it is possible to avoid the need to choose either 8-bit or 16-bit +output and to retain palette images (the palette colors will be modified +appropriately and the tRNS chunk removed.) However, if you are doing this, +take great care not to ask for transformations without checking first that +they apply! + +In the first call the background color has the original bit depth and color type +of the PNG file. So, for palette images the color is supplied as a palette +index and for low bit greyscale images the color is a reduced bit value in +image_background->gray. + +If you didn't call png_set_gamma() before reading the file header, for example +if you need your code to remain compatible with older versions of libpng prior +to libpng-1.5.4, this is the place to call it. + +Do not call it if you called png_set_alpha_mode(); doing so will damage the +settings put in place by png_set_alpha_mode(). (If png_set_alpha_mode() is +supported then you can certainly do png_set_gamma() before reading the PNG +header.) + +This API unconditionally sets the screen and file gamma values, so it will +override the value in the PNG file unless it is called before the PNG file +reading starts. For this reason you must always call it with the PNG file +value when you call it in this position: + + if (png_get_gAMA(png_ptr, info_ptr, &file_gamma)) + png_set_gamma(png_ptr, screen_gamma, file_gamma); + + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + +If you need to reduce an RGB file to a paletted file, or if a paletted +file has more entries than will fit on your screen, png_set_quantize() +will do that. Note that this is a simple match quantization that merely +finds the closest color available. This should work fairly well with +optimized palettes, but fairly badly with linear color cubes. If you +pass a palette that is larger than maximum_colors, the file will +reduce the number of colors in the palette so it will fit into +maximum_colors. If there is a histogram, libpng will use it to make +more intelligent choices when reducing the palette. If there is no +histogram, it may not do as good a job. + + if (color_type & PNG_COLOR_MASK_COLOR) + { + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_PLTE)) + { + png_uint_16p histogram = NULL; + + png_get_hIST(png_ptr, info_ptr, + &histogram); + png_set_quantize(png_ptr, palette, num_palette, + max_screen_colors, histogram, 1); + } + + else + { + png_color std_color_cube[MAX_SCREEN_COLORS] = + { ... colors ... }; + + png_set_quantize(png_ptr, std_color_cube, + MAX_SCREEN_COLORS, MAX_SCREEN_COLORS, + NULL,0); + } + } + +PNG files describe monochrome as black being zero and white being one. +The following code will reverse this (make black be one and white be +zero): + + if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) + png_set_invert_mono(png_ptr); + +This function can also be used to invert grayscale and gray-alpha images: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_invert_mono(png_ptr); + +PNG files store 16-bit pixels in network byte order (big-endian, +ie. most significant bits first). This code changes the storage to the +other way (little-endian, i.e. least significant bits first, the +way PCs store them): + + if (bit_depth == 16) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_read_user_transform_fn(png_ptr, + read_transform_fn); + +You must supply the function + + void read_transform_fn(png_structp png_ptr, png_row_infop + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +after all of the other transformations have been processed. Take care with +interlaced images if you do the interlace yourself - the width of the row is the +width in 'row_info', not the overall image width. + +If supported, libpng provides two information routines that you can use to find +where you are in processing the image: + + png_get_current_pass_number(png_structp png_ptr); + png_get_current_row_number(png_structp png_ptr); + +Don't try using these outside a transform callback - firstly they are only +supported if user transforms are supported, secondly they may well return +unexpected results unless the row is actually being processed at the moment they +are called. + +With interlaced +images the value returned is the row in the input sub-image image. Use +PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to +find the output pixel (x,y) given an interlaced sub-image pixel (row,col,pass). + +The discussion of interlace handling above contains more information on how to +use these values. + +You can also set up a pointer to a user structure for use by your +callback function, and you can inform libpng that your transform +function will change the number of channels or bit depth with the +function + + png_set_user_transform_info(png_ptr, user_ptr, + user_depth, user_channels); + +The user's application, not libpng, is responsible for allocating and +freeing any memory required for the user structure. + +You can retrieve the pointer via the function +png_get_user_transform_ptr(). For example: + + voidp read_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +The last thing to handle is interlacing; this is covered in detail below, +but you must call the function here if you want libpng to handle expansion +of the interlaced image. + + number_of_passes = png_set_interlace_handling(png_ptr); + +After setting the transformations, libpng can update your png_info +structure to reflect any transformations you've requested with this +call. + + png_read_update_info(png_ptr, info_ptr); + +This is most useful to update the info structure's rowbytes +field so you can use it to allocate your image memory. This function +will also update your palette with the correct screen_gamma and +background if these have been given with the calls above. You may +only call png_read_update_info() once with a particular info_ptr. + +After you call png_read_update_info(), you can allocate any +memory you need to hold the image. The row data is simply +raw byte data for all forms of images. As the actual allocation +varies among applications, no example will be given. If you +are allocating one large chunk, you will need to build an +array of pointers to each row, as it will be needed for some +of the functions below. + +Be sure that your platform can allocate the buffer that you'll need. +libpng internally checks for oversize width, but you'll need to +do your own check for number_of_rows*width*pixel_size if you are using +a multiple-row buffer: + + /* Guard against integer overflow */ + if (number_of_rows > PNG_SIZE_MAX/(width*pixel_size)) { + png_error(png_ptr,"image_data buffer would be too large"); + } + +Remember: Before you call png_read_update_info(), the png_get_*() +functions return the values corresponding to the original PNG image. +After you call png_read_update_info the values refer to the image +that libpng will output. Consequently you must call all the png_set_ +functions before you call png_read_update_info(). This is particularly +important for png_set_interlace_handling() - if you are going to call +png_read_update_info() you must call png_set_interlace_handling() before +it unless you want to receive interlaced output. + +.SS Reading image data + +After you've allocated memory, you can read the image data. +The simplest way to do this is in one function call. If you are +allocating enough memory to hold the whole image, you can just +call png_read_image() and libpng will read in all the image data +and put it in the memory area supplied. You will need to pass in +an array of pointers to each row. + +This function automatically handles interlacing, so you don't +need to call png_set_interlace_handling() (unless you call +png_read_update_info()) or call this function multiple times, or any +of that other stuff necessary with png_read_rows(). + + png_read_image(png_ptr, row_pointers); + +where row_pointers is: + + png_bytep row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to read in the whole image at once, you can +use png_read_rows() instead. If there is no interlacing (check +interlace_type == PNG_INTERLACE_NONE), this is simple: + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +where row_pointers is the same as in the png_read_image() call. + +If you are doing this just one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + png_read_row(png_ptr, row_pointer, NULL); + +If the file is interlaced (interlace_type != 0 in the IHDR chunk), things +get somewhat harder. The only current (PNG Specification version 1.2) +interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7); +a somewhat complicated 2D interlace scheme, known as Adam7, that +breaks down an image into seven smaller images of varying size, based +on an 8x8 grid. This number is defined (from libpng 1.5) as +PNG_INTERLACE_ADAM7_PASSES in png.h + +libpng can fill out those images or it can give them to you "as is". +It is almost always better to have libpng handle the interlacing for you. +If you want the images filled out, there are two ways to do that. The one +mentioned in the PNG specification is to expand each pixel to cover +those pixels that have not been read yet (the "rectangle" method). +This results in a blocky image for the first pass, which gradually +smooths out as more pixels are read. The other method is the "sparkle" +method, where pixels are drawn only in their final locations, with the +rest of the image remaining whatever colors they were initialized to +before the start of the read. The first method usually looks better, +but tends to be slower, as there are more pixels to put in the rows. + +If, as is likely, you want libpng to expand the images, call this before +calling png_start_read_image() or png_read_update_info(): + + if (interlace_type == PNG_INTERLACE_ADAM7) + number_of_passes + = png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this is seven, +but may change if another interlace type is added. This function can be +called even if the file is not interlaced, where it will return one pass. +You then need to read the whole image 'number_of_passes' times. Each time +will distribute the pixels from the current pass to the correct place in +the output image, so you need to supply the same rows to png_read_rows in +each pass. + +If you are not going to display the image after each pass, but are +going to wait until the entire image is read in, use the sparkle +effect. This effect is faster and the end result of either method +is exactly the same. If you are planning on displaying the image +after each pass, the "rectangle" effect is generally considered the +better looking one. + +If you only want the "sparkle" effect, just call png_read_row() or +png_read_rows() as +normal, with the third parameter NULL. Make sure you make pass over +the image number_of_passes times, and you don't change the data in the +rows between calls. You can change the locations of the data, just +not the data. Each pass only writes the pixels appropriate for that +pass, and assumes the data from previous passes is still valid. + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + or + png_read_row(png_ptr, row_pointers, NULL); + +If you only want the first effect (the rectangles), do the same as +before except pass the row buffer in the third parameter, and leave +the second parameter NULL. + + png_read_rows(png_ptr, NULL, row_pointers, + number_of_rows); + or + png_read_row(png_ptr, NULL, row_pointers); + +If you don't want libpng to handle the interlacing details, just call +png_read_rows() PNG_INTERLACE_ADAM7_PASSES times to read in all the images. +Each of the images is a valid image by itself; however, you will almost +certainly need to distribute the pixels from each sub-image to the +correct place. This is where everything gets very tricky. + +If you want to retrieve the separate images you must pass the correct +number of rows to each successive call of png_read_rows(). The calculation +gets pretty complicated for small images, where some sub-images may +not even exist because either their width or height ends up zero. +libpng provides two macros to help you in 1.5 and later versions: + + png_uint_32 width = PNG_PASS_COLS(image_width, pass_number); + png_uint_32 height = PNG_PASS_ROWS(image_height, pass_number); + +Respectively these tell you the width and height of the sub-image +corresponding to the numbered pass. 'pass' is in in the range 0 to 6 - +this can be confusing because the specification refers to the same passes +as 1 to 7! Be careful, you must check both the width and height before +calling png_read_rows() and not call it for that pass if either is zero. + +You can, of course, read each sub-image row by row. If you want to +produce optimal code to make a pixel-by-pixel transformation of an +interlaced image this is the best approach; read each row of each pass, +transform it, and write it out to a new interlaced image. + +If you want to de-interlace the image yourself libpng provides further +macros to help that tell you where to place the pixels in the output image. +Because the interlacing scheme is rectangular - sub-image pixels are always +arranged on a rectangular grid - all you need to know for each pass is the +starting column and row in the output image of the first pixel plus the +spacing between each pixel. As of libpng 1.5 there are four macros to +retrieve this information: + + png_uint_32 x = PNG_PASS_START_COL(pass); + png_uint_32 y = PNG_PASS_START_ROW(pass); + png_uint_32 xStep = 1U << PNG_PASS_COL_SHIFT(pass); + png_uint_32 yStep = 1U << PNG_PASS_ROW_SHIFT(pass); + +These allow you to write the obvious loop: + + png_uint_32 input_y = 0; + png_uint_32 output_y = PNG_PASS_START_ROW(pass); + + while (output_y < output_image_height) + { + png_uint_32 input_x = 0; + png_uint_32 output_x = PNG_PASS_START_COL(pass); + + while (output_x < output_image_width) + { + image[output_y][output_x] = + subimage[pass][input_y][input_x++]; + + output_x += xStep; + } + + ++input_y; + output_y += yStep; + } + +Notice that the steps between successive output rows and columns are +returned as shifts. This is possible because the pixels in the subimages +are always a power of 2 apart - 1, 2, 4 or 8 pixels - in the original +image. In practice you may need to directly calculate the output coordinate +given an input coordinate. libpng provides two further macros for this +purpose: + + png_uint_32 output_x = PNG_COL_FROM_PASS_COL(input_x, pass); + png_uint_32 output_y = PNG_ROW_FROM_PASS_ROW(input_y, pass); + +Finally a pair of macros are provided to tell you if a particular image +row or column appears in a given pass: + + int col_in_pass = PNG_COL_IN_INTERLACE_PASS(output_x, pass); + int row_in_pass = PNG_ROW_IN_INTERLACE_PASS(output_y, pass); + +Bear in mind that you will probably also need to check the width and height +of the pass in addition to the above to be sure the pass even exists! + +With any luck you are convinced by now that you don't want to do your own +interlace handling. In reality normally the only good reason for doing this +is if you are processing PNG files on a pixel-by-pixel basis and don't want +to load the whole file into memory when it is interlaced. + +libpng includes a test program, pngvalid, that illustrates reading and +writing of interlaced images. If you can't get interlacing to work in your +code and don't want to leave it to libpng (the recommended approach), see +how pngvalid.c does it. + +.SS Finishing a sequential read + +After you are finished reading the image through the +low-level interface, you can finish reading the file. + +If you want to use a different crc action for handling CRC errors in +chunks after the image data, you can call png_set_crc_action() +again at this point. + +If you are interested in comments or time, which may be stored either +before or after the image data, you should pass the separate png_info +struct if you want to keep the comments from before and after the image +separate. + + png_infop end_info = png_create_info_struct(png_ptr); + + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return ERROR; + } + + png_read_end(png_ptr, end_info); + +If you are not interested, you should still call png_read_end() +but you can pass NULL, avoiding the need to create an end_info structure. +If you do this, libpng will not process any chunks after IDAT other than +skipping over them and perhaps (depending on whether you have called +png_set_crc_action) checking their CRCs while looking for the IEND chunk. + + png_read_end(png_ptr, (png_infop)NULL); + +If you don't call png_read_end(), then your file pointer will be +left pointing to the first chunk after the last IDAT, which is probably +not what you want if you expect to read something beyond the end of +the PNG datastream. + +When you are done, you can free all memory allocated by libpng like this: + + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + +or, if you didn't create an end_info structure, + + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + + mask - identifies data to be freed, a mask + containing the bitwise OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + + seq - sequence number of item to be freed + (\-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those cases do nothing. +The "seq" parameter is ignored if only one item of the selected data +type, such as PLTE, is allowed. If "seq" is not \-1, and multiple items +are allowed for the data type identified in the mask, such as text or +sPLT, only the n'th item in the structure is freed, where n is "seq". + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_calloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + + mask - which data elements are affected + same choices as in png_free_data() + +This function only affects data that has already been allocated. +You can call this function after reading the PNG data but before calling +any png_set_*() functions, to control whether the user or the png_set_*() +function is responsible for freeing any existing data that might be present, +and again after the png_set_*() functions to control whether the user +or png_destroy_*() is supposed to free the data. When the user assumes +responsibility for libpng-allocated data, the application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_calloc() to allocate it. + +If you allocated your row_pointers in a single block, as suggested above in +the description of the high level read interface, you must not transfer +responsibility for freeing it to the png_set_rows or png_read_destroy function, +because they would also try to free the individual row_pointers[i]. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. + +The png_free_data() function will turn off the "valid" flag for anything +it frees. If you need to turn the flag off for a chunk that was freed by +your application instead of by libpng, you can use + + png_set_invalid(png_ptr, info_ptr, mask); + + mask - identifies the chunks to be made invalid, + containing the bitwise OR of one or + more of + PNG_INFO_gAMA, PNG_INFO_sBIT, + PNG_INFO_cHRM, PNG_INFO_PLTE, + PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_eXIf, + PNG_INFO_hIST, PNG_INFO_pHYs, + PNG_INFO_oFFs, PNG_INFO_tIME, + PNG_INFO_pCAL, PNG_INFO_sRGB, + PNG_INFO_iCCP, PNG_INFO_sPLT, + PNG_INFO_sCAL, PNG_INFO_IDAT + +For a more compact example of reading a PNG image, see the file example.c. + +.SS Reading PNG files progressively + +The progressive reader is slightly different from the non-progressive +reader. Instead of calling png_read_info(), png_read_rows(), and +png_read_end(), you make one call to png_process_data(), which calls +callbacks when it has the info, a row, or the end of the image. You +set up these callbacks with png_set_progressive_read_fn(). You don't +have to worry about the input/output functions of libpng, as you are +giving the library the data directly in png_process_data(). I will +assume that you have read the section on reading PNG files above, +so I will only highlight the differences (although I will show +all of the code). + +png_structp png_ptr; +png_infop info_ptr; + + /* An example code fragment of how you would + initialize the progressive reader in your + application. */ + int + initialize_png_reader() + { + png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + + if (!png_ptr) + return ERROR; + + info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, (png_infopp)NULL); + return ERROR; + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return ERROR; + } + + /* This one's new. You can provide functions + to be called when the header info is valid, + when each row is completed, and when the image + is finished. If you aren't using all functions, + you can specify NULL parameters. Even when all + three functions are NULL, you need to call + png_set_progressive_read_fn(). You can use + any struct as the user_ptr (cast to a void pointer + for the function call), and retrieve the pointer + from inside the callbacks using the function + + png_get_progressive_ptr(png_ptr); + + which will return a void pointer, which you have + to cast appropriately. + */ + png_set_progressive_read_fn(png_ptr, (void *)user_ptr, + info_callback, row_callback, end_callback); + + return 0; + } + + /* A code fragment that you call as you receive blocks + of data */ + int + process_data(png_bytep buffer, png_uint_32 length) + { + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return ERROR; + } + + /* This one's new also. Simply give it a chunk + of data from the file stream (in order, of + course). On machines with segmented memory + models machines, don't give it any more than + 64K. The library seems to run fine with sizes + of 4K. Although you can give it much less if + necessary (I assume you can give it chunks of + 1 byte, I haven't tried less than 256 bytes + yet). When this function returns, you may + want to display any rows that were generated + in the row callback if you don't already do + so there. + */ + png_process_data(png_ptr, info_ptr, buffer, length); + + /* At this point you can call png_process_data_skip if + you want to handle data the library will skip yourself; + it simply returns the number of bytes to skip (and stops + libpng skipping that number of bytes on the next + png_process_data call). + return 0; + } + + /* This function is called (as set by + png_set_progressive_read_fn() above) when enough data + has been supplied so all of the header has been + read. + */ + void + info_callback(png_structp png_ptr, png_infop info) + { + /* Do any setup here, including setting any of + the transformations mentioned in the Reading + PNG files section. For now, you _must_ call + either png_start_read_image() or + png_read_update_info() after all the + transformations are set (even if you don't set + any). You may start getting rows before + png_process_data() returns, so this is your + last chance to prepare for that. + + This is where you turn on interlace handling, + assuming you don't want to do it yourself. + + If you need to you can stop the processing of + your original input data at this point by calling + png_process_data_pause. This returns the number + of unprocessed bytes from the last png_process_data + call - it is up to you to ensure that the next call + sees these bytes again. If you don't want to bother + with this you can get libpng to cache the unread + bytes by setting the 'save' parameter (see png.h) but + then libpng will have to copy the data internally. + */ + } + + /* This function is called when each row of image + data is complete */ + void + row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) + { + /* If the image is interlaced, and you turned + on the interlace handler, this function will + be called for every row in every pass. Some + of these rows will not be changed from the + previous pass. When the row is not changed, + the new_row variable will be NULL. The rows + and passes are called in order, so you don't + really need the row_num and pass, but I'm + supplying them because it may make your life + easier. + + If you did not turn on interlace handling then + the callback is called for each row of each + sub-image when the image is interlaced. In this + case 'row_num' is the row in the sub-image, not + the row in the output image as it is in all other + cases. + + For the non-NULL rows of interlaced images when + you have switched on libpng interlace handling, + you must call png_progressive_combine_row() + passing in the row and the old row. You can + call this function for NULL rows (it will just + return) and for non-interlaced images (it just + does the memcpy for you) if it will make the + code easier. Thus, you can just do this for + all cases if you switch on interlace handling; + */ + + png_progressive_combine_row(png_ptr, old_row, + new_row); + + /* where old_row is what was displayed + previously for the row. Note that the first + pass (pass == 0, really) will completely cover + the old row, so the rows do not have to be + initialized. After the first pass (and only + for interlaced images), you will have to pass + the current row, and the function will combine + the old row and the new row. + + You can also call png_process_data_pause in this + callback - see above. + */ + } + + void + end_callback(png_structp png_ptr, png_infop info) + { + /* This function is called after the whole image + has been read, including any chunks after the + image (up to and including the IEND). You + will usually have the same info chunk as you + had in the header, although some data may have + been added to the comments and time fields. + + Most people won't do much here, perhaps setting + a flag that marks the image as finished. + */ + } + + + +.SH IV. Writing + +Much of this is very similar to reading. However, everything of +importance is repeated here, so you won't have to constantly look +back up in the reading section to understand writing. + +.SS Setup + +You will want to do the I/O initialization before you get into libpng, +so if it doesn't work, you don't have anything to undo. If you are not +using the standard I/O functions, you will need to replace them with +custom writing functions. See the discussion under Customizing libpng. + + FILE *fp = fopen(file_name, "wb"); + + if (!fp) + return ERROR; + +Next, png_struct and png_info need to be allocated and initialized. +As these can be both relatively large, you may not want to store these +on the stack, unless you have stack space to spare. Of course, you +will want to check if they return NULL. If you are also reading, +you won't want to name your read structure and your write structure +both "png_ptr"; you can call them anything you like, such as +"read_ptr" and "write_ptr". Look at pngtest.c, for example. + + png_structp png_ptr = png_create_write_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + + if (!png_ptr) + return ERROR; + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, + (png_infopp)NULL); + return ERROR; + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_write_struct_2() instead of png_create_write_struct(): + + png_structp png_ptr = png_create_write_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +After you have these structures, you will need to set up the +error handling. When libpng encounters an error, it expects to +longjmp() back to your routine. Therefore, you will need to call +setjmp() and pass the png_jmpbuf(png_ptr). If you +write the file from different routines, you will need to update +the png_jmpbuf(png_ptr) every time you enter a new routine that will +call a png_*() function. See your documentation of setjmp/longjmp +for your compiler for more information on setjmp/longjmp. See +the discussion on libpng error handling in the Customizing Libpng +section below for more information on the libpng error handling. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return ERROR; + } + ... + return; + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_NO_SETJMP, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +You can #define PNG_ABORT() to a function that does something +more useful than abort(), as long as your function does not +return. + +Checking for invalid palette index on write was added at libpng +1.5.10. If a pixel contains an invalid (out-of-range) index libpng issues +a benign error. This is enabled by default because this condition is an +error according to the PNG specification, Clause 11.3.2, but the error can +be ignored in each png_ptr with + + png_set_check_for_invalid_index(png_ptr, 0); + +If the error is ignored, or if png_benign_error() treats it as a warning, +any invalid pixels are written as-is by the encoder, resulting in an +invalid PNG datastream as output. In this case the application is +responsible for ensuring that the pixel indexes are in range when it writes +a PLTE chunk with fewer entries than the bit depth would allow. + +Now you need to set up the output code. The default for libpng is to +use the C function fwrite(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. Again, if you wish to handle writing data in +another way, see the discussion on libpng I/O handling in the Customizing +Libpng section below. + + png_init_io(png_ptr, fp); + +If you are embedding your PNG into a datastream such as MNG, and don't +want libpng to write the 8-byte signature, or if you have already +written the signature in your application, use + + png_set_sig_bytes(png_ptr, 8); + +to inform libpng that it should not write a signature. + +.SS Write callbacks + +At this point, you can set up a callback function that will be +called after each row has been written, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void write_row_callback(png_structp png_ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "write_row_callback") + +To inform libpng about your function, use + + png_set_write_status_fn(png_ptr, write_row_callback); + +When this function is called the row has already been completely processed and +it has also been written out. The 'row' and 'pass' refer to the next row to be +handled. For the +non-interlaced case the row that was just handled is simply one less than the +passed in row number, and pass will always be 0. For the interlaced case the +same applies unless the row value is 0, in which case the row just handled was +the last one from one of the preceding passes. Because interlacing may skip a +pass you cannot be sure that the preceding pass is just 'pass\-1', if you really +need to know what the last pass is record (row,pass) from the callback and use +the last recorded value each time. + +As with the user transform you can find the output row using the +PNG_ROW_FROM_PASS_ROW macro. + +You now have the option of modifying how the compression library will +run. The following functions are mainly for testing, but may be useful +in some cases, like if you need to write PNG files extremely fast and +are willing to give up some compression, or if you want to get the +maximum possible compression at the expense of slower writing. If you +have no special needs in this area, let the library do what it wants by +not calling this function at all, as it has been tuned to deliver a good +speed/compression ratio. The second parameter to png_set_filter() is +the filter method, for which the only valid values are 0 (as of the +July 1999 PNG specification, version 1.2) or 64 (if you are writing +a PNG datastream that is to be embedded in a MNG datastream). The third +parameter is a flag that indicates which filter type(s) are to be tested +for each scanline. See the PNG specification for details on the specific +filter types. + + + /* turn on or off filtering, and/or choose + specific filters. You can use either a single + PNG_FILTER_VALUE_NAME or the bitwise OR of one + or more PNG_FILTER_NAME masks. + */ + png_set_filter(png_ptr, 0, + PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | + PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | + PNG_FILTER_UP | PNG_FILTER_VALUE_UP | + PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG | + PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH| + PNG_ALL_FILTERS | PNG_FAST_FILTERS); + +If an application wants to start and stop using particular filters during +compression, it should start out with all of the filters (to ensure that +the previous row of pixels will be stored in case it's needed later), +and then add and remove them after the start of compression. + +If you are writing a PNG datastream that is to be embedded in a MNG +datastream, the second parameter can be either 0 or 64. + +The png_set_compression_*() functions interface to the zlib compression +library, and should mostly be ignored unless you really know what you are +doing. The only generally useful call is png_set_compression_level() +which changes how much time zlib spends on trying to compress the image +data. See the Compression Library (zlib.h and algorithm.txt, distributed +with zlib) for details on the compression levels. + + #include zlib.h + + /* Set the zlib compression level */ + png_set_compression_level(png_ptr, + Z_BEST_COMPRESSION); + + /* Set other zlib parameters for compressing IDAT */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr, + Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192) + + /* Set zlib parameters for text compression + * If you don't call these, the parameters + * fall back on those defined for IDAT chunks + */ + png_set_text_compression_mem_level(png_ptr, 8); + png_set_text_compression_strategy(png_ptr, + Z_DEFAULT_STRATEGY); + png_set_text_compression_window_bits(png_ptr, 15); + png_set_text_compression_method(png_ptr, 8); + +.SS Setting the contents of info for output + +You now need to fill in the png_info structure with all the data you +wish to write before the actual image. Note that the only thing you +are allowed to write after the image is the text chunks and the time +chunk (as of PNG Specification 1.2, anyway). See png_write_end() and +the latest PNG specification for more information on that. If you +wish to write them before the image, fill them in now, and flag that +data as being valid. If you want to wait until after the data, don't +fill them until png_write_end(). For all the fields in png_info and +their data types, see png.h. For explanations of what the fields +contain, see the PNG specification. + +Some of the more important parts of the png_info are: + + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, interlace_type, + compression_type, filter_method) + + width - holds the width of the image + in pixels (up to 2^31). + + height - holds the height of the image + in pixels (up to 2^31). + + bit_depth - holds the bit depth of one of the + image channels. + (valid values are 1, 2, 4, 8, 16 + and depend also on the + color_type. See also significant + bits (sBIT) below). + + color_type - describes which color/alpha + channels are present. + PNG_COLOR_TYPE_GRAY + (bit depths 1, 2, 4, 8, 16) + PNG_COLOR_TYPE_GRAY_ALPHA + (bit depths 8, 16) + PNG_COLOR_TYPE_PALETTE + (bit depths 1, 2, 4, 8) + PNG_COLOR_TYPE_RGB + (bit_depths 8, 16) + PNG_COLOR_TYPE_RGB_ALPHA + (bit_depths 8, 16) + + PNG_COLOR_MASK_PALETTE + PNG_COLOR_MASK_COLOR + PNG_COLOR_MASK_ALPHA + + interlace_type - PNG_INTERLACE_NONE or + PNG_INTERLACE_ADAM7 + + compression_type - (must be + PNG_COMPRESSION_TYPE_DEFAULT) + + filter_method - (must be PNG_FILTER_TYPE_DEFAULT + or, if you are writing a PNG to + be embedded in a MNG datastream, + can also be + PNG_INTRAPIXEL_DIFFERENCING) + +If you call png_set_IHDR(), the call must appear before any of the +other png_set_*() functions, because they might require access to some of +the IHDR settings. The remaining png_set_*() functions can be called +in any order. + +If you wish, you can reset the compression_type, interlace_type, or +filter_method later by calling png_set_IHDR() again; if you do this, the +width, height, bit_depth, and color_type must be the same in each call. + + png_set_PLTE(png_ptr, info_ptr, palette, + num_palette); + + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + + png_set_gAMA(png_ptr, info_ptr, file_gamma); + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); + + file_gamma - the gamma at which the image was + created (PNG_INFO_gAMA) + + int_file_gamma - 100,000 times the gamma at which + the image was created + + png_set_cHRM(png_ptr, info_ptr, white_x, white_y, red_x, red_y, + green_x, green_y, blue_x, blue_y) + png_set_cHRM_XYZ(png_ptr, info_ptr, red_X, red_Y, red_Z, green_X, + green_Y, green_Z, blue_X, blue_Y, blue_Z) + png_set_cHRM_fixed(png_ptr, info_ptr, int_white_x, int_white_y, + int_red_x, int_red_y, int_green_x, int_green_y, + int_blue_x, int_blue_y) + png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, int_red_X, int_red_Y, + int_red_Z, int_green_X, int_green_Y, int_green_Z, + int_blue_X, int_blue_Y, int_blue_Z) + + {white,red,green,blue}_{x,y} + A color space encoding specified using the chromaticities + of the end points and the white point. + + {red,green,blue}_{X,Y,Z} + A color space encoding specified using the encoding end + points - the CIE tristimulus specification of the intended + color of the red, green and blue channels in the PNG RGB + data. The white point is simply the sum of the three end + points. + + png_set_sRGB(png_ptr, info_ptr, srgb_intent); + + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of + the sRGB chunk means that the pixel + data is in the sRGB color space. + This chunk also implies specific + values of gAMA and cHRM. Rendering + intent is the CSS-1 property that + has been defined by the International + Color Consortium + (http://www.color.org). + It can be one of + PNG_sRGB_INTENT_SATURATION, + PNG_sRGB_INTENT_PERCEPTUAL, + PNG_sRGB_INTENT_ABSOLUTE, or + PNG_sRGB_INTENT_RELATIVE. + + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, + srgb_intent); + + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of the + sRGB chunk means that the pixel + data is in the sRGB color space. + This function also causes gAMA and + cHRM chunks with the specific values + that are consistent with sRGB to be + written. + + png_set_iCCP(png_ptr, info_ptr, name, compression_type, + profile, proflen); + + name - The profile name. + + compression_type - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + + profile - International Color Consortium color + profile data. May contain NULs. + + proflen - length of profile data in bytes. + + png_set_sBIT(png_ptr, info_ptr, sig_bit); + + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, red, + green, and blue channels, whichever are + appropriate for the given color type + (png_color_16) + + png_set_tRNS(png_ptr, info_ptr, trans_alpha, + num_trans, trans_color); + + trans_alpha - array of alpha (transparency) + entries for palette (PNG_INFO_tRNS) + + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + trans_color - graylevel or color sample values + (in order red, green, blue) of the + single transparent color for + non-paletted images (PNG_INFO_tRNS) + + png_set_eXIf_1(png_ptr, info_ptr, num_exif, exif); + + exif - Exif profile (array of + png_byte) (PNG_INFO_eXIf) + + png_set_hIST(png_ptr, info_ptr, hist); + + hist - histogram of palette (array of + png_uint_16) (PNG_INFO_hIST) + + png_set_tIME(png_ptr, info_ptr, mod_time); + + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_set_bKGD(png_ptr, info_ptr, background); + + background - background color (of type + png_color_16p) (PNG_VALID_bKGD) + + png_set_text(png_ptr, info_ptr, text_ptr, num_text); + + text_ptr - array of png_text holding image + comments + + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be NULL or empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (NULL or + empty for unknown). + text_ptr[i].translated_keyword - keyword in UTF-8 (NULL + or empty for unknown). + + Note that the itxt_length, lang, and lang_key + members of the text_ptr structure only exist when the + library is built with iTXt chunk support. Prior to + libpng-1.4.0 the library was built by default without + iTXt support. Also note that when iTXt is supported, + they contain NULL pointers when the "compression" + field contains PNG_TEXT_COMPRESSION_NONE or + PNG_TEXT_COMPRESSION_zTXt. + + num_text - number of comments + + png_set_sPLT(png_ptr, info_ptr, &palette_ptr, + num_spalettes); + + palette_ptr - array of png_sPLT_struct structures + to be added to the list of palettes + in the info structure. + num_spalettes - number of palette structures to be + added. + + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, + unit_type); + + offset_x - positive offset from the left + edge of the screen + + offset_y - positive offset from the top + edge of the screen + + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, + unit_type); + + res_x - pixels/unit physical resolution + in x direction + + res_y - pixels/unit physical resolution + in y direction + + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_set_sCAL(png_ptr, info_ptr, unit, width, height) + + unit - physical scale units (an integer) + + width - width of a pixel in physical scale units + + height - height of a pixel in physical scale units + (width and height are doubles) + + png_set_sCAL_s(png_ptr, info_ptr, unit, width, height) + + unit - physical scale units (an integer) + + width - width of a pixel in physical scale units + expressed as a string + + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + png_set_unknown_chunks(png_ptr, info_ptr, &unknowns, + num_unknowns) + + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position to write chunk in file + 0: do not write chunk + PNG_HAVE_IHDR: before PLTE + PNG_HAVE_PLTE: before IDAT + PNG_AFTER_IDAT: after IDAT + +The "location" member is set automatically according to +what part of the output file has already been written. +You can change its value after calling png_set_unknown_chunks() +as demonstrated in pngtest.c. Within each of the "locations", +the chunks are sequenced according to their position in the +structure (that is, the value of "i", which is the order in which +the chunk was either read from the input file or defined with +png_set_unknown_chunks). + +A quick word about text and num_text. text is an array of png_text +structures. num_text is the number of valid structures in the array. +Each png_text structure holds a language code, a keyword, a text value, +and a compression type. + +The compression types have the same valid numbers as the compression +types of the image data. Currently, the only valid number is zero. +However, you can store text either compressed or uncompressed, unlike +images, which always have to be compressed. So if you don't want the +text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE. +Because tEXt and zTXt chunks don't have a language field, if you +specify PNG_TEXT_COMPRESSION_NONE or PNG_TEXT_COMPRESSION_zTXt +any language code or translated keyword will not be written out. + +Until text gets around a few hundred bytes, it is not worth compressing it. +After the text has been written out to the file, the compression type +is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR, +so that it isn't written out again at the end (in case you are calling +png_write_end() with the same struct). + +The keywords that are given in the PNG Specification are: + + Title Short (one line) title or + caption for image + + Author Name of image's creator + + Description Description of image (possibly long) + + Copyright Copyright notice + + Creation Time Time of original image creation + (usually RFC 1123 format, see below) + + Software Software used to create the image + + Disclaimer Legal disclaimer + + Warning Warning of nature of content + + Source Device used to create the image + + Comment Miscellaneous comment; conversion + from other image format + +The keyword-text pairs work like this. Keywords should be short +simple descriptions of what the comment is about. Some typical +keywords are found in the PNG specification, as is some recommendations +on keywords. You can repeat keywords in a file. You can even write +some text before the image and some after. For example, you may want +to put a description of the image before the image, but leave the +disclaimer until after, so viewers working over modem connections +don't have to wait for the disclaimer to go over the modem before +they start seeing the image. Finally, keywords should be full +words, not abbreviations. Keywords and text are in the ISO 8859-1 +(Latin-1) character set (a superset of regular ASCII) and can not +contain NUL characters, and should not contain control or other +unprintable characters. To make the comments widely readable, stick +with basic ASCII, and avoid machine specific character set extensions +like the IBM-PC character set. The keyword must be present, but +you can leave off the text string on non-compressed pairs. +Compressed pairs must have a text string, as only the text string +is compressed anyway, so the compression would be meaningless. + +PNG supports modification time via the png_time structure. Two +conversion routines are provided, png_convert_from_time_t() for +time_t and png_convert_from_struct_tm() for struct tm. The +time_t routine uses gmtime(). You don't have to use either of +these, but if you wish to fill in the png_time structure directly, +you should provide the time in universal time (GMT) if possible +instead of your local time. Note that the year number is the full +year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and +that months start with 1. + +If you want to store the time of the original image creation, you should +use a plain tEXt chunk with the "Creation Time" keyword. This is +necessary because the "creation time" of a PNG image is somewhat vague, +depending on whether you mean the PNG file, the time the image was +created in a non-PNG format, a still photo from which the image was +scanned, or possibly the subject matter itself. In order to facilitate +machine-readable dates, it is recommended that the "Creation Time" +tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"), +although this isn't a requirement. Unlike the tIME chunk, the +"Creation Time" tEXt chunk is not expected to be automatically changed +by the software. To facilitate the use of RFC 1123 dates, a function +png_convert_to_rfc1123_buffer(buffer, png_timep) is provided to +convert from PNG time to an RFC 1123 format string. The caller must provide +a writeable buffer of at least 29 bytes. + +.SS Writing unknown chunks + +You can use the png_set_unknown_chunks function to queue up private chunks +for writing. You give it a chunk name, location, raw data, and a size. You +also must use png_set_keep_unknown_chunks() to ensure that libpng will +handle them. That's all there is to it. The chunks will be written by the +next following png_write_info_before_PLTE, png_write_info, or png_write_end +function, depending upon the specified location. Any chunks previously +read into the info structure's unknown-chunk list will also be written out +in a sequence that satisfies the PNG specification's ordering rules. + +Here is an example of writing two private chunks, prVt and miNE: + + #ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + /* Set unknown chunk data */ + png_unknown_chunk unk_chunk[2]; + strcpy((char *) unk_chunk[0].name, "prVt"; + unk_chunk[0].data = (unsigned char *) "PRIVATE DATA"; + unk_chunk[0].size = strlen(unk_chunk[0].data)+1; + unk_chunk[0].location = PNG_HAVE_IHDR; + strcpy((char *) unk_chunk[1].name, "miNE"; + unk_chunk[1].data = (unsigned char *) "MY CHUNK DATA"; + unk_chunk[1].size = strlen(unk_chunk[0].data)+1; + unk_chunk[1].location = PNG_AFTER_IDAT; + png_set_unknown_chunks(write_ptr, write_info_ptr, + unk_chunk, 2); + /* Needed because miNE is not safe-to-copy */ + png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, + (png_bytep) "miNE", 1); + # if PNG_LIBPNG_VER < 10600 + /* Deal with unknown chunk location bug in 1.5.x and earlier */ + png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); + png_set_unknown_chunk_location(png, info, 1, PNG_AFTER_IDAT); + # endif + # if PNG_LIBPNG_VER < 10500 + /* PNG_AFTER_IDAT writes two copies of the chunk prior to libpng-1.5.0, + * one before IDAT and another after IDAT, so don't use it; only use + * PNG_HAVE_IHDR location. This call resets the location previously + * set by assignment and png_set_unknown_chunk_location() for chunk 1. + */ + png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); + # endif + #endif + +.SS The high-level write interface + +At this point there are two ways to proceed; through the high-level +write interface, or through a sequence of low-level write operations. +You can use the high-level interface if your image data is present +in the info structure. All defined output +transformations are permitted, enabled by the following masks. + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samples + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + PNG_TRANSFORM_STRIP_FILLER Strip out filler + bytes (deprecated). + PNG_TRANSFORM_STRIP_FILLER_BEFORE Strip out leading + filler bytes + PNG_TRANSFORM_STRIP_FILLER_AFTER Strip out trailing + filler bytes + +If you have valid image data in the info structure (you can use +png_set_rows() to put image data in the info structure), simply do this: + + png_write_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the bitwise OR of some set of +transformation flags. This call is equivalent to png_write_info(), +followed the set of transformations indicated by the transform mask, +then png_write_image(), and finally png_write_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future output transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_write_png(). + +.SS The low-level write interface + +If you are going the low-level route instead, you are now ready to +write all the file information up to the actual image data. You do +this with a call to png_write_info(). + + png_write_info(png_ptr, info_ptr); + +Note that there is one transformation you may need to do before +png_write_info(). In PNG files, the alpha channel in an image is the +level of opacity. If your data is supplied as a level of transparency, +you can invert the alpha channel before you write it, so that 0 is +fully transparent and 255 (in 8-bit or paletted images) or 65535 +(in 16-bit images) is fully opaque, with + + png_set_invert_alpha(png_ptr); + +This must appear before png_write_info() instead of later with the +other transformations because in the case of paletted images the tRNS +chunk data has to be inverted before the tRNS chunk is written. If +your image is not a paletted image, the tRNS data (which in such cases +represents a single color to be rendered as transparent) won't need to +be changed, and you can safely do this transformation after your +png_write_info() call. + +If you need to write a private chunk that you want to appear before +the PLTE chunk when PLTE is present, you can write the PNG info in +two steps, and insert code to write your own chunk between them: + + png_write_info_before_PLTE(png_ptr, info_ptr); + png_set_unknown_chunks(png_ptr, info_ptr, ...); + png_write_info(png_ptr, info_ptr); + +After you've written the file information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +PNG files store RGB pixels packed into 3 or 6 bytes. This code tells +the library to strip input data that has 4 or 8 bytes per pixel down +to 3 or 6 bytes (or strip 2 or 4-byte grayscale+filler data to 1 or 2 +bytes per pixel). + + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + +where the 0 is unused, and the location is either PNG_FILLER_BEFORE or +PNG_FILLER_AFTER, depending upon whether the filler byte in the pixel +is stored XRGB or RGBX. + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit files. +If the data is supplied at 1 pixel per byte, use this code, which will +correctly pack the pixels into a single byte: + + png_set_packing(png_ptr); + +PNG files reduce possible bit depths to 1, 2, 4, 8, and 16. If your +data is of another bit depth, you can write an sBIT chunk into the +file so that decoders can recover the original data if desired. + + /* Set the true bit depth of the image data */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit.red = true_bit_depth; + sig_bit.green = true_bit_depth; + sig_bit.blue = true_bit_depth; + } + + else + { + sig_bit.gray = true_bit_depth; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + sig_bit.alpha = true_bit_depth; + } + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +If the data is stored in the row buffer in a bit depth other than +one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG), +this will scale the values to appear to be the correct bit depth as +is required by PNG. + + png_set_shift(png_ptr, &sig_bit); + +PNG files store 16-bit pixels in network byte order (big-endian, +ie. most significant bits first). This code would be used if they are +supplied the other way (little-endian, i.e. least significant bits +first, the way PCs store them): + + if (bit_depth > 8) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +PNG files store 3 color pixels in red, green, blue order. This code +would be used if they are supplied as blue, green, red: + + png_set_bgr(png_ptr); + +PNG files describe monochrome as black being zero and white being +one. This code would be used if the pixels are supplied with this reversed +(black being one and white being zero): + + png_set_invert_mono(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_write_user_transform_fn(png_ptr, + write_transform_fn); + +You must supply the function + + void write_transform_fn(png_structp png_ptr, png_row_infop + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +before any of the other transformations are processed. If supported +libpng also supplies an information routine that may be called from +your callback: + + png_get_current_row_number(png_ptr); + png_get_current_pass_number(png_ptr); + +This returns the current row passed to the transform. With interlaced +images the value returned is the row in the input sub-image image. Use +PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to +find the output pixel (x,y) given an interlaced sub-image pixel (row,col,pass). + +The discussion of interlace handling above contains more information on how to +use these values. + +You can also set up a pointer to a user structure for use by your +callback function. + + png_set_user_transform_info(png_ptr, user_ptr, 0, 0); + +The user_channels and user_depth parameters of this function are ignored +when writing; you can set them to zero as shown. + +You can retrieve the pointer via the function png_get_user_transform_ptr(). +For example: + + voidp write_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +It is possible to have libpng flush any pending output, either manually, +or automatically after a certain number of lines have been written. To +flush the output stream a single time call: + + png_write_flush(png_ptr); + +and to have libpng flush the output stream periodically after a certain +number of scanlines have been written, call: + + png_set_flush(png_ptr, nrows); + +Note that the distance between rows is from the last time png_write_flush() +was called, or the first row of the image if it has never been called. +So if you write 50 lines, and then png_set_flush 25, it will flush the +output on the next scanline, and every 25 lines thereafter, unless +png_write_flush() is called before 25 more lines have been written. +If nrows is too small (less than about 10 lines for a 640 pixel wide +RGB image) the image compression may decrease noticeably (although this +may be acceptable for real-time applications). Infrequent flushing will +only degrade the compression performance by a few percent over images +that do not use flushing. + +.SS Writing the image data + +That's it for the transformations. Now you can write the image data. +The simplest way to do this is in one function call. If you have the +whole image in memory, you can just call png_write_image() and libpng +will write the image. You will need to pass in an array of pointers to +each row. This function automatically handles interlacing, so you don't +need to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_write_rows(). + + png_write_image(png_ptr, row_pointers); + +where row_pointers is: + + png_byte *row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to write the whole image at once, you can +use png_write_rows() instead. If the file is not interlaced, +this is simple: + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +row_pointers is the same as in the png_write_image() call. + +If you are just writing one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + + png_write_row(png_ptr, row_pointer); + +When the file is interlaced, things can get a good deal more complicated. +The only currently (as of the PNG Specification version 1.2, dated July +1999) defined interlacing scheme for PNG files is the "Adam7" interlace +scheme, that breaks down an image into seven smaller images of varying +size. libpng will build these images for you, or you can do them +yourself. If you want to build them yourself, see the PNG specification +for details of which pixels to write when. + +If you don't want libpng to handle the interlacing details, just +use png_set_interlace_handling() and call png_write_rows() the +correct number of times to write all the sub-images +(png_set_interlace_handling() returns the number of sub-images.) + +If you want libpng to build the sub-images, call this before you start +writing any rows: + + number_of_passes = png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this is seven, +but may change if another interlace type is added. + +Then write the complete image number_of_passes times. + + png_write_rows(png_ptr, row_pointers, number_of_rows); + +Think carefully before you write an interlaced image. Typically code that +reads such images reads all the image data into memory, uncompressed, before +doing any processing. Only code that can display an image on the fly can +take advantage of the interlacing and even then the image has to be exactly +the correct size for the output device, because scaling an image requires +adjacent pixels and these are not available until all the passes have been +read. + +If you do write an interlaced image you will hardly ever need to handle +the interlacing yourself. Call png_set_interlace_handling() and use the +approach described above. + +The only time it is conceivable that you will really need to write an +interlaced image pass-by-pass is when you have read one pass by pass and +made some pixel-by-pixel transformation to it, as described in the read +code above. In this case use the PNG_PASS_ROWS and PNG_PASS_COLS macros +to determine the size of each sub-image in turn and simply write the rows +you obtained from the read code. + +.SS Finishing a sequential write + +After you are finished writing the image, you should finish writing +the file. If you are interested in writing comments or time, you should +pass an appropriately filled png_info pointer. If you are not interested, +you can pass NULL. + + png_write_end(png_ptr, info_ptr); + +When you are done, you can free all memory used by libpng like this: + + png_destroy_write_struct(&png_ptr, &info_ptr); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + + mask - identifies data to be freed, a mask + containing the bitwise OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + + seq - sequence number of item to be freed + (\-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those cases do nothing. +The "seq" parameter is ignored if only one item of the selected data +type, such as PLTE, is allowed. If "seq" is not \-1, and multiple items +are allowed for the data type identified in the mask, such as text or +sPLT, only the n'th item in the structure is freed, where n is "seq". + +If you allocated data such as a palette that you passed in to libpng +with png_set_*, you must not free it until just before the call to +png_destroy_write_struct(). + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_calloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + + mask - which data elements are affected + same choices as in png_free_data() + +For example, to transfer responsibility for some data from a read structure +to a write structure, you could use + + png_data_freer(read_ptr, read_info_ptr, + PNG_USER_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + + png_data_freer(write_ptr, write_info_ptr, + PNG_DESTROY_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + +thereby briefly reassigning responsibility for freeing to the user but +immediately afterwards reassigning it once more to the write_destroy +function. Having done this, it would then be safe to destroy the read +structure and continue to use the PLTE, tRNS, and hIST data in the write +structure. + +This function only affects data that has already been allocated. +You can call this function before calling after the png_set_*() functions +to control whether the user or png_destroy_*() is supposed to free the data. +When the user assumes responsibility for libpng-allocated data, the +application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_calloc() to allocate it. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. +For a more compact example of writing a PNG image, see the file example.c. + +.SH V. Simplified API + +The simplified API, which became available in libpng-1.6.0, hides the details +of both libpng and the PNG file format itself. +It allows PNG files to be read into a very limited number of +in-memory bitmap formats or to be written from the same formats. If these +formats do not accommodate your needs then you can, and should, use the more +sophisticated APIs above - these support a wide variety of in-memory formats +and a wide variety of sophisticated transformations to those formats as well +as a wide variety of APIs to manipulate ancillary information. + +To read a PNG file using the simplified API: + + 1) Declare a 'png_image' structure (see below) on the stack, set the + version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + (this is REQUIRED, your program may crash if you don't do it.) + + 2) Call the appropriate png_image_begin_read... function. + + 3) Set the png_image 'format' member to the required sample format. + + 4) Allocate a buffer for the image and, if required, the color-map. + + 5) Call png_image_finish_read to read the image and, if required, the + color-map into your buffers. + +There are no restrictions on the format of the PNG input itself; all valid +color types, bit depths, and interlace methods are acceptable, and the +input image is transformed as necessary to the requested in-memory format +during the png_image_finish_read() step. The only caveat is that if you +request a color-mapped image from a PNG that is full-color or makes +complex use of an alpha channel the transformation is extremely lossy and the +result may look terrible. + +To write a PNG file using the simplified API: + + 1) Declare a 'png_image' structure on the stack and memset() + it to all zero. + + 2) Initialize the members of the structure that describe the + image, setting the 'format' member to the format of the + image samples. + + 3) Call the appropriate png_image_write... function with a + pointer to the image and, if necessary, the color-map to write + the PNG data. + +png_image is a structure that describes the in-memory format of an image +when it is being read or defines the in-memory format of an image that you +need to write. The "png_image" structure contains the following members: + + png_controlp opaque Initialize to NULL, free with png_image_free + png_uint_32 version Set to PNG_IMAGE_VERSION + png_uint_32 width Image width in pixels (columns) + png_uint_32 height Image height in pixels (rows) + png_uint_32 format Image format as defined below + png_uint_32 flags A bit mask containing informational flags + png_uint_32 colormap_entries; Number of entries in the color-map + png_uint_32 warning_or_error; + char message[64]; + +In the event of an error or warning the "warning_or_error" +field will be set to a non-zero value and the 'message' field will contain +a '\0' terminated string with the libpng error or warning message. If both +warnings and an error were encountered, only the error is recorded. If there +are multiple warnings, only the first one is recorded. + +The upper 30 bits of the "warning_or_error" value are reserved; the low two +bits contain a two bit code such that a value more than 1 indicates a failure +in the API just called: + + 0 - no warning or error + 1 - warning + 2 - error + 3 - error preceded by warning + +The pixels (samples) of the image have one to four channels whose components +have original values in the range 0 to 1.0: + + 1: A single gray or luminance channel (G). + 2: A gray/luminance channel and an alpha channel (GA). + 3: Three red, green, blue color channels (RGB). + 4: Three color channels and an alpha channel (RGBA). + +The channels are encoded in one of two ways: + + a) As a small integer, value 0..255, contained in a single byte. For the +alpha channel the original value is simply value/255. For the color or +luminance channels the value is encoded according to the sRGB specification +and matches the 8-bit format expected by typical display devices. + +The color/gray channels are not scaled (pre-multiplied) by the alpha +channel and are suitable for passing to color management software. + + b) As a value in the range 0..65535, contained in a 2-byte integer, in +the native byte order of the platform on which the application is running. +All channels can be converted to the original value by dividing by 65535; all +channels are linear. Color channels use the RGB encoding (RGB end-points) of +the sRGB specification. This encoding is identified by the +PNG_FORMAT_FLAG_LINEAR flag below. + +When the simplified API needs to convert between sRGB and linear colorspaces, +the actual sRGB transfer curve defined in the sRGB specification (see the +article at https://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 +approximation used elsewhere in libpng. + +When an alpha channel is present it is expected to denote pixel coverage +of the color or luminance channels and is returned as an associated alpha +channel: the color/gray channels are scaled (pre-multiplied) by the alpha +value. + +The samples are either contained directly in the image data, between 1 and 8 +bytes per pixel according to the encoding, or are held in a color-map indexed +by bytes in the image data. In the case of a color-map the color-map entries +are individual samples, encoded as above, and the image data has one byte per +pixel to select the relevant sample from the color-map. + +PNG_FORMAT_* + +The #defines to be used in png_image::format. Each #define identifies a +particular layout of channel data and, if present, alpha values. There are +separate defines for each of the two component encodings. + +A format is built up using single bit flag values. All combinations are +valid. Formats can be built up from the flag values or you can use one of +the predefined values below. When testing formats always use the FORMAT_FLAG +macros to test for individual features - future versions of the library may +add new flags. + +When reading or writing color-mapped images the format should be set to the +format of the entries in the color-map then png_image_{read,write}_colormap +called to read or write the color-map and set the format correctly for the +image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + +NOTE: libpng can be built with particular features disabled. If you see +compiler errors because the definition of one of the following flags has been +compiled out it is because libpng does not have the required support. It is +possible, however, for the libpng configuration to enable the format on just +read or just write; in that case you may see an error at run time. +You can guard against this by checking for the definition of the +appropriate "_SUPPORTED" macro, one of: + + PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + + PNG_FORMAT_FLAG_ALPHA format with an alpha channel + PNG_FORMAT_FLAG_COLOR color format: otherwise grayscale + PNG_FORMAT_FLAG_LINEAR 2-byte channels else 1-byte + PNG_FORMAT_FLAG_COLORMAP image data is color-mapped + PNG_FORMAT_FLAG_BGR BGR colors, else order is RGB + PNG_FORMAT_FLAG_AFIRST alpha channel comes first + +Supported formats are as follows. Future versions of libpng may support more +formats; for compatibility with older versions simply check if the format +macro is defined using #ifdef. These defines describe the in-memory layout +of the components of the pixels of the image. + +First the single byte (sRGB) formats: + + PNG_FORMAT_GRAY + PNG_FORMAT_GA + PNG_FORMAT_AG + PNG_FORMAT_RGB + PNG_FORMAT_BGR + PNG_FORMAT_RGBA + PNG_FORMAT_ARGB + PNG_FORMAT_BGRA + PNG_FORMAT_ABGR + +Then the linear 2-byte formats. When naming these "Y" is used to +indicate a luminance (gray) channel. The component order within the pixel +is always the same - there is no provision for swapping the order of the +components in the linear format. The components are 16-bit integers in +the native byte order for your platform, and there is no provision for +swapping the bytes to a different endian condition. + + PNG_FORMAT_LINEAR_Y + PNG_FORMAT_LINEAR_Y_ALPHA + PNG_FORMAT_LINEAR_RGB + PNG_FORMAT_LINEAR_RGB_ALPHA + +With color-mapped formats the image data is one byte for each pixel. The byte +is an index into the color-map which is formatted as above. To obtain a +color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP +to one of the above definitions, or you can use one of the definitions below. + + PNG_FORMAT_RGB_COLORMAP + PNG_FORMAT_BGR_COLORMAP + PNG_FORMAT_RGBA_COLORMAP + PNG_FORMAT_ARGB_COLORMAP + PNG_FORMAT_BGRA_COLORMAP + PNG_FORMAT_ABGR_COLORMAP + +PNG_IMAGE macros + +These are convenience macros to derive information from a png_image +structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the +actual image sample values - either the entries in the color-map or the +pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values +for the pixels and will always return 1 for color-mapped formats. The +remaining macros return information about the rows in the image and the +complete image. + +NOTE: All the macros that take a png_image::format parameter are compile time +constants if the format parameter is, itself, a constant. Therefore these +macros can be used in array declarations and case labels where required. +Similarly the macros are also pre-processor constants (sizeof is not used) so +they can be used in #if tests. + + PNG_IMAGE_SAMPLE_CHANNELS(fmt) + Returns the total number of channels in a given format: 1..4 + + PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt) + Returns the size in bytes of a single component of a pixel or color-map + entry (as appropriate) in the image: 1 or 2. + + PNG_IMAGE_SAMPLE_SIZE(fmt) + This is the size of the sample data for one sample. If the image is + color-mapped it is the size of one color-map entry (and image pixels are + one byte in size), otherwise it is the size of one image pixel. + + PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt) + The maximum size of the color-map required by the format expressed in a + count of components. This can be used to compile-time allocate a + color-map: + + png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + + png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + + Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + information from one of the png_image_begin_read_ APIs and dynamically + allocate the required memory. + + PNG_IMAGE_COLORMAP_SIZE(fmt) + The size of the color-map required by the format; this is the size of the + color-map buffer passed to the png_image_{read,write}_colormap APIs. It is + a fixed number determined by the format so can easily be allocated on the + stack if necessary. + +Corresponding information about the pixels + + PNG_IMAGE_PIXEL_CHANNELS(fmt) + The number of separate channels (components) in a pixel; 1 for a + color-mapped image. + + PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + The size, in bytes, of each component in a pixel; 1 for a color-mapped + image. + + PNG_IMAGE_PIXEL_SIZE(fmt) + The size, in bytes, of a complete pixel; 1 for a color-mapped image. + +Information about the whole row, or whole image + + PNG_IMAGE_ROW_STRIDE(image) + Returns the total number of components in a single row of the image; this + is the minimum 'row stride', the minimum count of components between each + row. For a color-mapped image this is the minimum number of bytes in a + row. + + If you need the stride measured in bytes, row_stride_bytes is + PNG_IMAGE_ROW_STRIDE(image) * PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt) + plus any padding bytes that your application might need, for example + to start the next row on a 4-byte boundary. + + PNG_IMAGE_BUFFER_SIZE(image, row_stride) + Return the size, in bytes, of an image buffer given a png_image and a row + stride - the number of components to leave space for in each row. + + PNG_IMAGE_SIZE(image) + Return the size, in bytes, of the image in memory given just a png_image; + the row stride is the minimum stride required for the image. + + PNG_IMAGE_COLORMAP_SIZE(image) + Return the size, in bytes, of the color-map of this image. If the image + format is not a color-map format this will return a size sufficient for + 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + you don't want to allocate a color-map in this case. + +PNG_IMAGE_FLAG_* + +Flags containing additional information about the image are held in +the 'flags' field of png_image. + + PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB == 0x01 + This indicates that the RGB values of the in-memory bitmap do not + correspond to the red, green and blue end-points defined by sRGB. + + PNG_IMAGE_FLAG_FAST == 0x02 + On write emphasise speed over compression; the resultant PNG file will be + larger but will be produced significantly faster, particular for large + images. Do not use this option for images which will be distributed, only + used it when producing intermediate files that will be read back in + repeatedly. For a typical 24-bit image the option will double the read + speed at the cost of increasing the image size by 25%, however for many + more compressible images the PNG file can be 10 times larger with only a + slight speed gain. + + PNG_IMAGE_FLAG_16BIT_sRGB == 0x04 + On read if the image is a 16-bit per component image and there is no gAMA + or sRGB chunk assume that the components are sRGB encoded. Notice that + images output by the simplified API always have gamma information; setting + this flag only affects the interpretation of 16-bit images from an + external source. It is recommended that the application expose this flag + to the user; the user can normally easily recognize the difference between + linear and sRGB encoding. This flag has no effect on write - the data + passed to the write APIs must have the correct encoding (as defined + above.) + + If the flag is not set (the default) input 16-bit per component data is + assumed to be linear. + + NOTE: the flag can only be set after the png_image_begin_read_ call, + because that call initializes the 'flags' field. + +READ APIs + + The png_image passed to the read APIs must have been initialized by setting + the png_controlp field 'opaque' to NULL (or, better, memset the whole thing.) + + int png_image_begin_read_from_file( png_imagep image, + const char *file_name) + + The named file is opened for read and the image header + is filled in from the PNG header in the file. + + int png_image_begin_read_from_stdio (png_imagep image, + FILE* file) + + The PNG header is read from the stdio FILE object. + + int png_image_begin_read_from_memory(png_imagep image, + png_const_voidp memory, size_t size) + + The PNG header is read from the given memory buffer. + + int png_image_finish_read(png_imagep image, + png_colorp background, void *buffer, + png_int_32 row_stride, void *colormap)); + + Finish reading the image into the supplied buffer and + clean up the png_image structure. + + row_stride is the step, in png_byte or png_uint_16 units + as appropriate, between adjacent rows. A positive stride + indicates that the top-most row is first in the buffer - + the normal top-down arrangement. A negative stride + indicates that the bottom-most row is first in the buffer. + + background need only be supplied if an alpha channel must + be removed from a png_byte format and the removal is to be + done by compositing on a solid color; otherwise it may be + NULL and any composition will be done directly onto the + buffer. The value is an sRGB color to use for the + background, for grayscale output the green channel is used. + + For linear output removing the alpha channel is always done + by compositing on black. + + void png_image_free(png_imagep image) + + Free any data allocated by libpng in image->opaque, + setting the pointer to NULL. May be called at any time + after the structure is initialized. + +When the simplified API needs to convert between sRGB and linear colorspaces, +the actual sRGB transfer curve defined in the sRGB specification (see the +article at https://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 +approximation used elsewhere in libpng. + +WRITE APIS + +For write you must initialize a png_image structure to describe the image to +be written: + + version: must be set to PNG_IMAGE_VERSION + opaque: must be initialized to NULL + width: image width in pixels + height: image height in rows + format: the format of the data you wish to write + flags: set to 0 unless one of the defined flags applies; set + PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images + where the RGB values do not correspond to the colors in sRGB. + colormap_entries: set to the number of entries in the color-map (0 to 256) + + int png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + + Write the image to the named file. + + int png_image_write_to_memory (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, + int convert_to_8_bit, const void *buffer, ptrdiff_t row_stride, + const void *colormap)); + + Write the image to memory. + + int png_image_write_to_stdio(png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, + png_int_32 row_stride, const void *colormap) + + Write the image to the given (FILE*). + +With all write APIs if image is in one of the linear formats with +(png_uint_16) data then setting convert_to_8_bit will cause the output to be +a (png_byte) PNG gamma encoded according to the sRGB specification, otherwise +a 16-bit linear encoded PNG file is written. + +With all APIs row_stride is handled as in the read APIs - it is the spacing +from one row to the next in component sized units (float) and if negative +indicates a bottom-up row layout in the buffer. If you pass zero, libpng will +calculate the row_stride for you from the width and number of channels. + +Note that the write API does not support interlacing, sub-8-bit pixels, +indexed (paletted) images, or most ancillary chunks. + +.SH VI. Modifying/Customizing libpng + +There are two issues here. The first is changing how libpng does +standard things like memory allocation, input/output, and error handling. +The second deals with more complicated things like adding new chunks, +adding new transformations, and generally changing how libpng works. +Both of those are compile-time issues; that is, they are generally +determined at the time the code is written, and there is rarely a need +to provide the user with a means of changing them. + +Memory allocation, input/output, and error handling + +All of the memory allocation, input/output, and error handling in libpng +goes through callbacks that are user-settable. The default routines are +in pngmem.c, pngrio.c, pngwio.c, and pngerror.c, respectively. To change +these functions, call the appropriate png_set_*_fn() function. + +Memory allocation is done through the functions png_malloc(), png_calloc(), +and png_free(). The png_malloc() and png_free() functions currently just +call the standard C functions and png_calloc() calls png_malloc() and then +clears the newly allocated memory to zero; note that png_calloc(png_ptr, size) +is not the same as the calloc(number, size) function provided by stdlib.h. +There is limited support for certain systems with segmented memory +architectures and the types of pointers declared by png.h match this; you +will have to use appropriate pointers in your application. If you prefer +to use a different method of allocating and freeing data, you can use +png_create_read_struct_2() or png_create_write_struct_2() to register your +own functions as described above. These functions also provide a void +pointer that can be retrieved via + + mem_ptr=png_get_mem_ptr(png_ptr); + +Your replacement memory functions must have prototypes as follows: + + png_voidp malloc_fn(png_structp png_ptr, + png_alloc_size_t size); + + void free_fn(png_structp png_ptr, png_voidp ptr); + +Your malloc_fn() must return NULL in case of failure. The png_malloc() +function will normally call png_error() if it receives a NULL from the +system memory allocator or from your replacement malloc_fn(). + +Your free_fn() will never be called with a NULL ptr, since libpng's +png_free() checks for NULL before calling free_fn(). + +Input/Output in libpng is done through png_read() and png_write(), +which currently just call fread() and fwrite(). The FILE * is stored in +png_struct and is initialized via png_init_io(). If you wish to change +the method of I/O, the library supplies callbacks that you can set +through the function png_set_read_fn() and png_set_write_fn() at run +time, instead of calling the png_init_io() function. These functions +also provide a void pointer that can be retrieved via the function +png_get_io_ptr(). For example: + + png_set_read_fn(png_structp read_ptr, + voidp read_io_ptr, png_rw_ptr read_data_fn) + + png_set_write_fn(png_structp write_ptr, + voidp write_io_ptr, png_rw_ptr write_data_fn, + png_flush_ptr output_flush_fn); + + voidp read_io_ptr = png_get_io_ptr(read_ptr); + voidp write_io_ptr = png_get_io_ptr(write_ptr); + +The replacement I/O functions must have prototypes as follows: + + void user_read_data(png_structp png_ptr, + png_bytep data, size_t length); + + void user_write_data(png_structp png_ptr, + png_bytep data, size_t length); + + void user_flush_data(png_structp png_ptr); + +The user_read_data() function is responsible for detecting and +handling end-of-data errors. + +Supplying NULL for the read, write, or flush functions sets them back +to using the default C stream functions, which expect the io_ptr to +point to a standard *FILE structure. It is probably a mistake +to use NULL for one of write_data_fn and output_flush_fn but not both +of them, unless you have built libpng with PNG_NO_WRITE_FLUSH defined. +It is an error to read from a write stream, and vice versa. + +Error handling in libpng is done through png_error() and png_warning(). +Errors handled through png_error() are fatal, meaning that png_error() +should never return to its caller. Currently, this is handled via +setjmp() and longjmp() (unless you have compiled libpng with +PNG_NO_SETJMP, in which case it is handled via PNG_ABORT()), +but you could change this to do things like exit() if you should wish, +as long as your function does not return. + +On non-fatal errors, png_warning() is called +to print a warning message, and then control returns to the calling code. +By default png_error() and png_warning() print a message on stderr via +fprintf() unless the library is compiled with PNG_NO_CONSOLE_IO defined +(because you don't want the messages) or PNG_NO_STDIO defined (because +fprintf() isn't available). If you wish to change the behavior of the error +functions, you will need to set up your own message callbacks. These +functions are normally supplied at the time that the png_struct is created. +It is also possible to redirect errors and warnings to your own replacement +functions after png_create_*_struct() has been called by calling: + + png_set_error_fn(png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warning_fn); + +If NULL is supplied for either error_fn or warning_fn, then the libpng +default function will be used, calling fprintf() and/or longjmp() if a +problem is encountered. The replacement error functions should have +parameters as follows: + + void user_error_fn(png_structp png_ptr, + png_const_charp error_msg); + + void user_warning_fn(png_structp png_ptr, + png_const_charp warning_msg); + +Then, within your user_error_fn or user_warning_fn, you can retrieve +the error_ptr if you need it, by calling + + png_voidp error_ptr = png_get_error_ptr(png_ptr); + +The motivation behind using setjmp() and longjmp() is the C++ throw and +catch exception handling methods. This makes the code much easier to write, +as there is no need to check every return code of every function call. +However, there are some uncertainties about the status of local variables +after a longjmp, so the user may want to be careful about doing anything +after setjmp returns non-zero besides returning itself. Consult your +compiler documentation for more details. For an alternative approach, you +may wish to use the "cexcept" facility (see https://cexcept.sourceforge.io/), +which is illustrated in pngvalid.c and in contrib/visupng. + +Beginning in libpng-1.4.0, the png_set_benign_errors() API became available. +You can use this to handle certain errors (normally handled as errors) +as warnings. + + png_set_benign_errors (png_ptr, int allowed); + + allowed: 0: treat png_benign_error() as an error. + 1: treat png_benign_error() as a warning. + +As of libpng-1.6.0, the default condition is to treat benign errors as +warnings while reading and as errors while writing. + +.SS Custom chunks + +If you need to read or write custom chunks, you may need to get deeper +into the libpng code. The library now has mechanisms for storing +and writing chunks of unknown type; you can even declare callbacks +for custom chunks. However, this may not be good enough if the +library code itself needs to know about interactions between your +chunk and existing `intrinsic' chunks. + +If you need to write a new intrinsic chunk, first read the PNG +specification. Acquire a first level of understanding of how it works. +Pay particular attention to the sections that describe chunk names, +and look at how other chunks were designed, so you can do things +similarly. Second, check out the sections of libpng that read and +write chunks. Try to find a chunk that is similar to yours and use +it as a template. More details can be found in the comments inside +the code. It is best to handle private or unknown chunks in a generic method, +via callback functions, instead of by modifying libpng functions. This +is illustrated in pngtest.c, which uses a callback function to handle a +private "vpAg" chunk and the new "sTER" chunk, which are both unknown to +libpng. + +If you wish to write your own transformation for the data, look through +the part of the code that does the transformations, and check out some of +the simpler ones to get an idea of how they work. Try to find a similar +transformation to the one you want to add and copy off of it. More details +can be found in the comments inside the code itself. + +.SS Configuring for gui/windowing platforms: + +You will need to write new error and warning functions that use the GUI +interface, as described previously, and set them to be the error and +warning functions at the time that png_create_*_struct() is called, +in order to have them available during the structure initialization. +They can be changed later via png_set_error_fn(). On some compilers, +you may also have to change the memory allocators (png_malloc, etc.). + +.SS Configuring zlib: + +There are special functions to configure the compression. Perhaps the +most useful one changes the compression level, which currently uses +input compression values in the range 0 - 9. The library normally +uses the default compression level (Z_DEFAULT_COMPRESSION = 6). Tests +have shown that for a large majority of images, compression values in +the range 3-6 compress nearly as well as higher levels, and do so much +faster. For online applications it may be desirable to have maximum speed +(Z_BEST_SPEED = 1). With versions of zlib after v0.99, you can also +specify no compression (Z_NO_COMPRESSION = 0), but this would create +files larger than just storing the raw bitmap. You can specify the +compression level by calling: + + #include zlib.h + png_set_compression_level(png_ptr, level); + +Another useful one is to reduce the memory level used by the library. +The memory level defaults to 8, but it can be lowered if you are +short on memory (running DOS, for example, where you only have 640K). +Note that the memory level does have an effect on compression; among +other things, lower levels will result in sections of incompressible +data being emitted in smaller stored blocks, with a correspondingly +larger relative overhead of up to 15% in the worst case. + + #include zlib.h + png_set_compression_mem_level(png_ptr, level); + +The other functions are for configuring zlib. They are not recommended +for normal use and may result in writing an invalid PNG file. See +zlib.h for more information on what these mean. + + #include zlib.h + png_set_compression_strategy(png_ptr, + strategy); + + png_set_compression_window_bits(png_ptr, + window_bits); + + png_set_compression_method(png_ptr, method); + +This controls the size of the IDAT chunks (default 8192): + + png_set_compression_buffer_size(png_ptr, size); + +As of libpng version 1.5.4, additional APIs became +available to set these separately for non-IDAT +compressed chunks such as zTXt, iTXt, and iCCP: + + #include zlib.h + #if PNG_LIBPNG_VER >= 10504 + png_set_text_compression_level(png_ptr, level); + + png_set_text_compression_mem_level(png_ptr, level); + + png_set_text_compression_strategy(png_ptr, + strategy); + + png_set_text_compression_window_bits(png_ptr, + window_bits); + + png_set_text_compression_method(png_ptr, method); + #endif + +.SS Controlling row filtering + +If you want to control whether libpng uses filtering or not, which +filters are used, and how it goes about picking row filters, you +can call one of these functions. The selection and configuration +of row filters can have a significant impact on the size and +encoding speed and a somewhat lesser impact on the decoding speed +of an image. Filtering is enabled by default for RGB and grayscale +images (with and without alpha), but not for paletted images nor +for any images with bit depths less than 8 bits/pixel. + +The 'method' parameter sets the main filtering method, which is +currently only '0' in the PNG 1.2 specification. The 'filters' +parameter sets which filter(s), if any, should be used for each +scanline. Possible values are PNG_ALL_FILTERS, PNG_NO_FILTERS, +or PNG_FAST_FILTERS to turn filtering on and off, or to turn on +just the fast-decoding subset of filters, respectively. + +Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB, +PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise +ORed together with '|' to specify one or more filters to use. +These filters are described in more detail in the PNG specification. +If you intend to change the filter type during the course of writing +the image, you should start with flags set for all of the filters +you intend to use so that libpng can initialize its internal +structures appropriately for all of the filter types. (Note that this +means the first row must always be adaptively filtered, because libpng +currently does not allocate the filter buffers until png_write_row() +is called for the first time.) + + filters = PNG_NO_FILTERS; + filters = PNG_ALL_FILTERS; + filters = PNG_FAST_FILTERS; + + or + + filters = PNG_FILTER_NONE | PNG_FILTER_SUB | + PNG_FILTER_UP | PNG_FILTER_AVG | + PNG_FILTER_PAETH; + + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, + filters); + + The second parameter can also be + PNG_INTRAPIXEL_DIFFERENCING if you are + writing a PNG to be embedded in a MNG + datastream. This parameter must be the + same as the value of filter_method used + in png_set_IHDR(). + +.SS Requesting debug printout + +The macro definition PNG_DEBUG can be used to request debugging +printout. Set it to an integer value in the range 0 to 3. Higher +numbers result in increasing amounts of debugging information. The +information is printed to the "stderr" file, unless another file +name is specified in the PNG_DEBUG_FILE macro definition. + +When PNG_DEBUG > 0, the following functions (macros) become available: + + png_debug(level, message) + png_debug1(level, message, p1) + png_debug2(level, message, p1, p2) + +in which "level" is compared to PNG_DEBUG to decide whether to print +the message, "message" is the formatted string to be printed, +and p1 and p2 are parameters that are to be embedded in the string +according to printf-style formatting directives. For example, + + png_debug1(2, "foo=%d", foo); + +is expanded to + + if (PNG_DEBUG > 2) + fprintf(PNG_DEBUG_FILE, "foo=%d\en", foo); + +When PNG_DEBUG is defined but is zero, the macros aren't defined, but you +can still use PNG_DEBUG to control your own debugging: + + #ifdef PNG_DEBUG + fprintf(stderr, ... + #endif + +When PNG_DEBUG = 1, the macros are defined, but only png_debug statements +having level = 0 will be printed. There aren't any such statements in +this version of libpng, but if you insert some they will be printed. + +.SH VII. MNG support + +The MNG specification (available at http://www.libpng.org/pub/mng) allows +certain extensions to PNG for PNG images that are embedded in MNG datastreams. +Libpng can support some of these extensions. To enable them, use the +png_permit_mng_features() function: + + feature_set = png_permit_mng_features(png_ptr, mask) + + mask is a png_uint_32 containing the bitwise OR of the + features you want to enable. These include + PNG_FLAG_MNG_EMPTY_PLTE + PNG_FLAG_MNG_FILTER_64 + PNG_ALL_MNG_FEATURES + + feature_set is a png_uint_32 that is the bitwise AND of + your mask with the set of MNG features that is + supported by the version of libpng that you are using. + +It is an error to use this function when reading or writing a standalone +PNG file with the PNG 8-byte signature. The PNG datastream must be wrapped +in a MNG datastream. As a minimum, it must have the MNG 8-byte signature +and the MHDR and MEND chunks. Libpng does not provide support for these +or any other MNG chunks; your application must provide its own support for +them. You may wish to consider using libmng (available at +https://www.libmng.com/) instead. + +.SH VIII. Changes to Libpng from version 0.88 + +It should be noted that versions of libpng later than 0.96 are not +distributed by the original libpng author, Guy Schalnat, nor by +Andreas Dilger, who had taken over from Guy during 1996 and 1997, and +distributed versions 0.89 through 0.96, but rather by another member +of the original PNG Group, Glenn Randers-Pehrson. Guy and Andreas are +still alive and well, but they have moved on to other things. + +The old libpng functions png_read_init(), png_write_init(), +png_info_init(), png_read_destroy(), and png_write_destroy() have been +moved to PNG_INTERNAL in version 0.95 to discourage their use. These +functions will be removed from libpng version 1.4.0. + +The preferred method of creating and initializing the libpng structures is +via the png_create_read_struct(), png_create_write_struct(), and +png_create_info_struct() because they isolate the size of the structures +from the application, allow version error checking, and also allow the +use of custom error handling routines during the initialization, which +the old functions do not. The functions png_read_destroy() and +png_write_destroy() do not actually free the memory that libpng +allocated for these structs, but just reset the data structures, so they +can be used instead of png_destroy_read_struct() and +png_destroy_write_struct() if you feel there is too much system overhead +allocating and freeing the png_struct for each image read. + +Setting the error callbacks via png_set_message_fn() before +png_read_init() as was suggested in libpng-0.88 is no longer supported +because this caused applications that do not use custom error functions +to fail if the png_ptr was not initialized to zero. It is still possible +to set the error callbacks AFTER png_read_init(), or to change them with +png_set_error_fn(), which is essentially the same function, but with a new +name to force compilation errors with applications that try to use the old +method. + +Support for the sCAL, iCCP, iTXt, and sPLT chunks was added at libpng-1.0.6; +however, iTXt support was not enabled by default. + +Starting with version 1.0.7, you can find out which version of the library +you are using at run-time: + + png_uint_32 libpng_vn = png_access_version_number(); + +The number libpng_vn is constructed from the major version, minor +version with leading zero, and release number with leading zero, +(e.g., libpng_vn for version 1.0.7 is 10007). + +Note that this function does not take a png_ptr, so you can call it +before you've created one. + +You can also check which version of png.h you used when compiling your +application: + + png_uint_32 application_vn = PNG_LIBPNG_VER; + +.SH IX. Changes to Libpng from version 1.0.x to 1.2.x + +Support for user memory management was enabled by default. To +accomplish this, the functions png_create_read_struct_2(), +png_create_write_struct_2(), png_set_mem_fn(), png_get_mem_ptr(), +png_malloc_default(), and png_free_default() were added. + +Support for the iTXt chunk has been enabled by default as of +version 1.2.41. + +Support for certain MNG features was enabled. + +Support for numbered error messages was added. However, we never got +around to actually numbering the error messages. The function +png_set_strip_error_numbers() was added (Note: the prototype for this +function was inadvertently removed from png.h in PNG_NO_ASSEMBLER_CODE +builds of libpng-1.2.15. It was restored in libpng-1.2.36). + +The png_malloc_warn() function was added at libpng-1.2.3. This issues +a png_warning and returns NULL instead of aborting when it fails to +acquire the requested memory allocation. + +Support for setting user limits on image width and height was enabled +by default. The functions png_set_user_limits(), png_get_user_width_max(), +and png_get_user_height_max() were added at libpng-1.2.6. + +The png_set_add_alpha() function was added at libpng-1.2.7. + +The function png_set_expand_gray_1_2_4_to_8() was added at libpng-1.2.9. +Unlike png_set_gray_1_2_4_to_8(), the new function does not expand the +tRNS chunk to alpha. The png_set_gray_1_2_4_to_8() function is +deprecated. + +A number of macro definitions in support of runtime selection of +assembler code features (especially Intel MMX code support) were +added at libpng-1.2.0: + + PNG_ASM_FLAG_MMX_SUPPORT_COMPILED + PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU + PNG_ASM_FLAG_MMX_READ_COMBINE_ROW + PNG_ASM_FLAG_MMX_READ_INTERLACE + PNG_ASM_FLAG_MMX_READ_FILTER_SUB + PNG_ASM_FLAG_MMX_READ_FILTER_UP + PNG_ASM_FLAG_MMX_READ_FILTER_AVG + PNG_ASM_FLAG_MMX_READ_FILTER_PAETH + PNG_ASM_FLAGS_INITIALIZED + PNG_MMX_READ_FLAGS + PNG_MMX_FLAGS + PNG_MMX_WRITE_FLAGS + PNG_MMX_FLAGS + +We added the following functions in support of runtime +selection of assembler code features: + + png_get_mmx_flagmask() + png_set_mmx_thresholds() + png_get_asm_flags() + png_get_mmx_bitdepth_threshold() + png_get_mmx_rowbytes_threshold() + png_set_asm_flags() + +We replaced all of these functions with simple stubs in libpng-1.2.20, +when the Intel assembler code was removed due to a licensing issue. + +These macros are deprecated: + + PNG_READ_TRANSFORMS_NOT_SUPPORTED + PNG_PROGRESSIVE_READ_NOT_SUPPORTED + PNG_NO_SEQUENTIAL_READ_SUPPORTED + PNG_WRITE_TRANSFORMS_NOT_SUPPORTED + PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED + PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED + +They have been replaced, respectively, by: + + PNG_NO_READ_TRANSFORMS + PNG_NO_PROGRESSIVE_READ + PNG_NO_SEQUENTIAL_READ + PNG_NO_WRITE_TRANSFORMS + PNG_NO_READ_ANCILLARY_CHUNKS + PNG_NO_WRITE_ANCILLARY_CHUNKS + +PNG_MAX_UINT was replaced with PNG_UINT_31_MAX. It has been +deprecated since libpng-1.0.16 and libpng-1.2.6. + +The function + png_check_sig(sig, num) +was replaced with + !png_sig_cmp(sig, 0, num) +It has been deprecated since libpng-0.90. + +The function + png_set_gray_1_2_4_to_8() +which also expands tRNS to alpha was replaced with + png_set_expand_gray_1_2_4_to_8() +which does not. It has been deprecated since libpng-1.0.18 and 1.2.9. + +.SH X. Changes to Libpng from version 1.0.x/1.2.x to 1.4.x + +Private libpng prototypes and macro definitions were moved from +png.h and pngconf.h into a new pngpriv.h header file. + +Functions png_set_benign_errors(), png_benign_error(), and +png_chunk_benign_error() were added. + +Support for setting the maximum amount of memory that the application +will allocate for reading chunks was added, as a security measure. +The functions png_set_chunk_cache_max() and png_get_chunk_cache_max() +were added to the library. + +We implemented support for I/O states by adding png_ptr member io_state +and functions png_get_io_chunk_name() and png_get_io_state() in pngget.c + +We added PNG_TRANSFORM_GRAY_TO_RGB to the available high-level +input transforms. + +Checking for and reporting of errors in the IHDR chunk is more thorough. + +Support for global arrays was removed, to improve thread safety. + +Some obsolete/deprecated macros and functions have been removed. + +Typecasted NULL definitions such as + #define png_voidp_NULL (png_voidp)NULL +were eliminated. If you used these in your application, just use +NULL instead. + +The png_struct and info_struct members "trans" and "trans_values" were +changed to "trans_alpha" and "trans_color", respectively. + +The obsolete, unused pnggccrd.c and pngvcrd.c files and related makefiles +were removed. + +The PNG_1_0_X and PNG_1_2_X macros were eliminated. + +The PNG_LEGACY_SUPPORTED macro was eliminated. + +Many WIN32_WCE #ifdefs were removed. + +The functions png_read_init(info_ptr), png_write_init(info_ptr), +png_info_init(info_ptr), png_read_destroy(), and png_write_destroy() +have been removed. They have been deprecated since libpng-0.95. + +The png_permit_empty_plte() was removed. It has been deprecated +since libpng-1.0.9. Use png_permit_mng_features() instead. + +We removed the obsolete stub functions png_get_mmx_flagmask(), +png_set_mmx_thresholds(), png_get_asm_flags(), +png_get_mmx_bitdepth_threshold(), png_get_mmx_rowbytes_threshold(), +png_set_asm_flags(), and png_mmx_supported() + +We removed the obsolete png_check_sig(), png_memcpy_check(), and +png_memset_check() functions. Instead use !png_sig_cmp(), memcpy(), +and memset(), respectively. + +The function png_set_gray_1_2_4_to_8() was removed. It has been +deprecated since libpng-1.0.18 and 1.2.9, when it was replaced with +png_set_expand_gray_1_2_4_to_8() because the former function also +expanded any tRNS chunk to an alpha channel. + +Macros for png_get_uint_16, png_get_uint_32, and png_get_int_32 +were added and are used by default instead of the corresponding +functions. Unfortunately, +from libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the +function) incorrectly returned a value of type png_uint_32. + +We changed the prototype for png_malloc() from + png_malloc(png_structp png_ptr, png_uint_32 size) +to + png_malloc(png_structp png_ptr, png_alloc_size_t size) + +This also applies to the prototype for the user replacement malloc_fn(). + +The png_calloc() function was added and is used in place of +of "png_malloc(); memset();" except in the case in png_read_png() +where the array consists of pointers; in this case a "for" loop is used +after the png_malloc() to set the pointers to NULL, to give robust. +behavior in case the application runs out of memory part-way through +the process. + +We changed the prototypes of png_get_compression_buffer_size() and +png_set_compression_buffer_size() to work with size_t instead of +png_uint_32. + +Support for numbered error messages was removed by default, since we +never got around to actually numbering the error messages. The function +png_set_strip_error_numbers() was removed from the library by default. + +The png_zalloc() and png_zfree() functions are no longer exported. +The png_zalloc() function no longer zeroes out the memory that it +allocates. Applications that called png_zalloc(png_ptr, number, size) +can call png_calloc(png_ptr, number*size) instead, and can call +png_free() instead of png_zfree(). + +Support for dithering was disabled by default in libpng-1.4.0, because +it has not been well tested and doesn't actually "dither". +The code was not +removed, however, and could be enabled by building libpng with +PNG_READ_DITHER_SUPPORTED defined. In libpng-1.4.2, this support +was re-enabled, but the function was renamed png_set_quantize() to +reflect more accurately what it actually does. At the same time, +the PNG_DITHER_[RED,GREEN_BLUE]_BITS macros were also renamed to +PNG_QUANTIZE_[RED,GREEN,BLUE]_BITS, and PNG_READ_DITHER_SUPPORTED +was renamed to PNG_READ_QUANTIZE_SUPPORTED. + +We removed the trailing '.' from the warning and error messages. + +.SH XI. Changes to Libpng from version 1.4.x to 1.5.x + +From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the +function) incorrectly returned a value of type png_uint_32. +The incorrect macro was removed from libpng-1.4.5. + +Checking for invalid palette index on write was added at libpng +1.5.10. If a pixel contains an invalid (out-of-range) index libpng issues +a benign error. This is enabled by default because this condition is an +error according to the PNG specification, Clause 11.3.2, but the error can +be ignored in each png_ptr with + + png_set_check_for_invalid_index(png_ptr, allowed); + + allowed - one of + 0: disable benign error (accept the + invalid data without warning). + 1: enable benign error (treat the + invalid data as an error or a + warning). + +If the error is ignored, or if png_benign_error() treats it as a warning, +any invalid pixels are decoded as opaque black by the decoder and written +as-is by the encoder. + +Retrieving the maximum palette index found was added at libpng-1.5.15. +This statement must appear after png_read_png() or png_read_image() while +reading, and after png_write_png() or png_write_image() while writing. + + int max_palette = png_get_palette_max(png_ptr, info_ptr); + +This will return the maximum palette index found in the image, or "\-1" if +the palette was not checked, or "0" if no palette was found. Note that this +does not account for any palette index used by ancillary chunks such as the +bKGD chunk; you must check those separately to determine the maximum +palette index actually used. + +There are no substantial API changes between the non-deprecated parts of +the 1.4.5 API and the 1.5.0 API; however, the ability to directly access +members of the main libpng control structures, png_struct and png_info, +deprecated in earlier versions of libpng, has been completely removed from +libpng 1.5, and new private "pngstruct.h", "pnginfo.h", and "pngdebug.h" +header files were created. + +We no longer include zlib.h in png.h. The include statement has been moved +to pngstruct.h, where it is not accessible by applications. Applications that +need access to information in zlib.h will need to add the '#include "zlib.h"' +directive. It does not matter whether this is placed prior to or after +the '"#include png.h"' directive. + +The png_sprintf(), png_strcpy(), and png_strncpy() macros are no longer used +and were removed. + +We moved the png_strlen(), png_memcpy(), png_memset(), and png_memcmp() +macros into a private header file (pngpriv.h) that is not accessible to +applications. + +In png_get_iCCP, the type of "profile" was changed from png_charpp +to png_bytepp, and in png_set_iCCP, from png_charp to png_const_bytep. + +There are changes of form in png.h, including new and changed macros to +declare parts of the API. Some API functions with arguments that are +pointers to data not modified within the function have been corrected to +declare these arguments with const. + +Much of the internal use of C macros to control the library build has also +changed and some of this is visible in the exported header files, in +particular the use of macros to control data and API elements visible +during application compilation may require significant revision to +application code. (It is extremely rare for an application to do this.) + +Any program that compiled against libpng 1.4 and did not use deprecated +features or access internal library structures should compile and work +against libpng 1.5, except for the change in the prototype for +png_get_iCCP() and png_set_iCCP() API functions mentioned above. + +libpng 1.5.0 adds PNG_ PASS macros to help in the reading and writing of +interlaced images. The macros return the number of rows and columns in +each pass and information that can be used to de-interlace and (if +absolutely necessary) interlace an image. + +libpng 1.5.0 adds an API png_longjmp(png_ptr, value). This API calls +the application-provided png_longjmp_ptr on the internal, but application +initialized, longjmp buffer. It is provided as a convenience to avoid +the need to use the png_jmpbuf macro, which had the unnecessary side +effect of resetting the internal png_longjmp_ptr value. + +libpng 1.5.0 includes a complete fixed point API. By default this is +present along with the corresponding floating point API. In general the +fixed point API is faster and smaller than the floating point one because +the PNG file format used fixed point, not floating point. This applies +even if the library uses floating point in internal calculations. A new +macro, PNG_FLOATING_ARITHMETIC_SUPPORTED, reveals whether the library +uses floating point arithmetic (the default) or fixed point arithmetic +internally for performance critical calculations such as gamma correction. +In some cases, the gamma calculations may produce slightly different +results. This has changed the results in png_rgb_to_gray and in alpha +composition (png_set_background for example). This applies even if the +original image was already linear (gamma == 1.0) and, therefore, it is +not necessary to linearize the image. This is because libpng has *not* +been changed to optimize that case correctly, yet. + +Fixed point support for the sCAL chunk comes with an important caveat; +the sCAL specification uses a decimal encoding of floating point values +and the accuracy of PNG fixed point values is insufficient for +representation of these values. Consequently a "string" API +(png_get_sCAL_s and png_set_sCAL_s) is the only reliable way of reading +arbitrary sCAL chunks in the absence of either the floating point API or +internal floating point calculations. Starting with libpng-1.5.0, both +of these functions are present when PNG_sCAL_SUPPORTED is defined. Prior +to libpng-1.5.0, their presence also depended upon PNG_FIXED_POINT_SUPPORTED +being defined and PNG_FLOATING_POINT_SUPPORTED not being defined. + +Applications no longer need to include the optional distribution header +file pngusr.h or define the corresponding macros during application +build in order to see the correct variant of the libpng API. From 1.5.0 +application code can check for the corresponding _SUPPORTED macro: + +#ifdef PNG_INCH_CONVERSIONS_SUPPORTED + /* code that uses the inch conversion APIs. */ +#endif + +This macro will only be defined if the inch conversion functions have been +compiled into libpng. The full set of macros, and whether or not support +has been compiled in, are available in the header file pnglibconf.h. +This header file is specific to the libpng build. Notice that prior to +1.5.0 the _SUPPORTED macros would always have the default definition unless +reset by pngusr.h or by explicit settings on the compiler command line. +These settings may produce compiler warnings or errors in 1.5.0 because +of macro redefinition. + +Applications can now choose whether to use these macros or to call the +corresponding function by defining PNG_USE_READ_MACROS or +PNG_NO_USE_READ_MACROS before including png.h. Notice that this is +only supported from 1.5.0; defining PNG_NO_USE_READ_MACROS prior to 1.5.0 +will lead to a link failure. + +Prior to libpng-1.5.4, the zlib compressor used the same set of parameters +when compressing the IDAT data and textual data such as zTXt and iCCP. +In libpng-1.5.4 we reinitialized the zlib stream for each type of data. +We added five png_set_text_*() functions for setting the parameters to +use with textual data. + +Prior to libpng-1.5.4, the PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +option was off by default, and slightly inaccurate scaling occurred. +This option can no longer be turned off, and the choice of accurate +or inaccurate 16-to-8 scaling is by using the new png_set_scale_16_to_8() +API for accurate scaling or the old png_set_strip_16_to_8() API for simple +chopping. In libpng-1.5.4, the PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +macro became PNG_READ_SCALE_16_TO_8_SUPPORTED, and the PNG_READ_16_TO_8 +macro became PNG_READ_STRIP_16_TO_8_SUPPORTED, to enable the two +png_set_*_16_to_8() functions separately. + +Prior to libpng-1.5.4, the png_set_user_limits() function could only be +used to reduce the width and height limits from the value of +PNG_USER_WIDTH_MAX and PNG_USER_HEIGHT_MAX, although this document said +that it could be used to override them. Now this function will reduce or +increase the limits. + +Starting in libpng-1.5.22, default user limits were established. These +can be overridden by application calls to png_set_user_limits(), +png_set_user_chunk_cache_max(), and/or png_set_user_malloc_max(). +The limits are now + max possible default + png_user_width_max 0x7fffffff 1,000,000 + png_user_height_max 0x7fffffff 1,000,000 + png_user_chunk_cache_max 0 (unlimited) 1000 + png_user_chunk_malloc_max 0 (unlimited) 8,000,000 + +The png_set_option() function (and the "options" member of the png struct) was +added to libpng-1.5.15, with option PNG_ARM_NEON. + +The library now supports a complete fixed point implementation and can +thus be used on systems that have no floating point support or very +limited or slow support. Previously gamma correction, an essential part +of complete PNG support, required reasonably fast floating point. + +As part of this the choice of internal implementation has been made +independent of the choice of fixed versus floating point APIs and all the +missing fixed point APIs have been implemented. + +The exact mechanism used to control attributes of API functions has +changed, as described in the INSTALL file. + +A new test program, pngvalid, is provided in addition to pngtest. +pngvalid validates the arithmetic accuracy of the gamma correction +calculations and includes a number of validations of the file format. +A subset of the full range of tests is run when "make check" is done +(in the 'configure' build.) pngvalid also allows total allocated memory +usage to be evaluated and performs additional memory overwrite validation. + +Many changes to individual feature macros have been made. The following +are the changes most likely to be noticed by library builders who +configure libpng: + +1) All feature macros now have consistent naming: + +#define PNG_NO_feature turns the feature off +#define PNG_feature_SUPPORTED turns the feature on + +pnglibconf.h contains one line for each feature macro which is either: + +#define PNG_feature_SUPPORTED + +if the feature is supported or: + +/*#undef PNG_feature_SUPPORTED*/ + +if it is not. Library code consistently checks for the 'SUPPORTED' macro. +It does not, and libpng applications should not, check for the 'NO' macro +which will not normally be defined even if the feature is not supported. +The 'NO' macros are only used internally for setting or not setting the +corresponding 'SUPPORTED' macros. + +Compatibility with the old names is provided as follows: + +PNG_INCH_CONVERSIONS turns on PNG_INCH_CONVERSIONS_SUPPORTED + +And the following definitions disable the corresponding feature: + +PNG_SETJMP_NOT_SUPPORTED disables SETJMP +PNG_READ_TRANSFORMS_NOT_SUPPORTED disables READ_TRANSFORMS +PNG_NO_READ_COMPOSITED_NODIV disables READ_COMPOSITE_NODIV +PNG_WRITE_TRANSFORMS_NOT_SUPPORTED disables WRITE_TRANSFORMS +PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED disables READ_ANCILLARY_CHUNKS +PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED disables WRITE_ANCILLARY_CHUNKS + +Library builders should remove use of the above, inconsistent, names. + +2) Warning and error message formatting was previously conditional on +the STDIO feature. The library has been changed to use the +CONSOLE_IO feature instead. This means that if CONSOLE_IO is disabled +the library no longer uses the printf(3) functions, even though the +default read/write implementations use (FILE) style stdio.h functions. + +3) Three feature macros now control the fixed/floating point decisions: + +PNG_FLOATING_POINT_SUPPORTED enables the floating point APIs + +PNG_FIXED_POINT_SUPPORTED enables the fixed point APIs; however, in +practice these are normally required internally anyway (because the PNG +file format is fixed point), therefore in most cases PNG_NO_FIXED_POINT +merely stops the function from being exported. + +PNG_FLOATING_ARITHMETIC_SUPPORTED chooses between the internal floating +point implementation or the fixed point one. Typically the fixed point +implementation is larger and slower than the floating point implementation +on a system that supports floating point; however, it may be faster on a +system which lacks floating point hardware and therefore uses a software +emulation. + +4) Added PNG_{READ,WRITE}_INT_FUNCTIONS_SUPPORTED. This allows the +functions to read and write ints to be disabled independently of +PNG_USE_READ_MACROS, which allows libpng to be built with the functions +even though the default is to use the macros - this allows applications +to choose at app buildtime whether or not to use macros (previously +impossible because the functions weren't in the default build.) + +.SH XII. Changes to Libpng from version 1.5.x to 1.6.x + +A "simplified API" has been added (see documentation in png.h and a simple +example in contrib/examples/pngtopng.c). The new publicly visible API +includes the following: + + macros: + PNG_FORMAT_* + PNG_IMAGE_* + structures: + png_control + png_image + read functions + png_image_begin_read_from_file() + png_image_begin_read_from_stdio() + png_image_begin_read_from_memory() + png_image_finish_read() + png_image_free() + write functions + png_image_write_to_file() + png_image_write_to_memory() + png_image_write_to_stdio() + +Starting with libpng-1.6.0, you can configure libpng to prefix all exported +symbols, using the PNG_PREFIX macro. + +We no longer include string.h in png.h. The include statement has been moved +to pngpriv.h, where it is not accessible by applications. Applications that +need access to information in string.h must add an '#include ' +directive. It does not matter whether this is placed prior to or after +the '#include "png.h"' directive. + +The following API are now DEPRECATED: + png_info_init_3() + png_convert_to_rfc1123() which has been replaced + with png_convert_to_rfc1123_buffer() + png_malloc_default() + png_free_default() + png_reset_zstream() + +The following have been removed: + png_get_io_chunk_name(), which has been replaced + with png_get_io_chunk_type(). The new + function returns a 32-bit integer instead of + a string. + The png_sizeof(), png_strlen(), png_memcpy(), png_memcmp(), and + png_memset() macros are no longer used in the libpng sources and + have been removed. These had already been made invisible to applications + (i.e., defined in the private pngpriv.h header file) since libpng-1.5.0. + +The signatures of many exported functions were changed, such that + png_structp became png_structrp or png_const_structrp + png_infop became png_inforp or png_const_inforp +where "rp" indicates a "restricted pointer". + +Dropped support for 16-bit platforms. The support for FAR/far types has +been eliminated and the definition of png_alloc_size_t is now controlled +by a flag so that 'small size_t' systems can select it if necessary. + +Error detection in some chunks has improved; in particular the iCCP chunk +reader now does pretty complete validation of the basic format. Some bad +profiles that were previously accepted are now accepted with a warning or +rejected, depending upon the png_set_benign_errors() setting, in particular +the very old broken Microsoft/HP 3144-byte sRGB profile. Starting with +libpng-1.6.11, recognizing and checking sRGB profiles can be avoided by +means of + + #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && \ + defined(PNG_SET_OPTION_SUPPORTED) + png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, + PNG_OPTION_ON); + #endif + +It's not a good idea to do this if you are using the "simplified API", +which needs to be able to recognize sRGB profiles conveyed via the iCCP +chunk. + +The PNG spec requirement that only grayscale profiles may appear in images +with color type 0 or 4 and that even if the image only contains gray pixels, +only RGB profiles may appear in images with color type 2, 3, or 6, is now +enforced. The sRGB chunk is allowed to appear in images with any color type +and is interpreted by libpng to convey a one-tracer-curve gray profile or a +three-tracer-curve RGB profile as appropriate. + +Libpng 1.5.x erroneously used /MD for Debug DLL builds; if you used the debug +builds in your app and you changed your app to use /MD you will need to +change it back to /MDd for libpng 1.6.x. + +Prior to libpng-1.6.0 a warning would be issued if the iTXt chunk contained +an empty language field or an empty translated keyword. Both of these +are allowed by the PNG specification, so these warnings are no longer issued. + +The library now issues an error if the application attempts to set a +transform after it calls png_read_update_info() or if it attempts to call +both png_read_update_info() and png_start_read_image() or to call either +of them more than once. + +The default condition for benign_errors is now to treat benign errors as +warnings while reading and as errors while writing. + +The library now issues a warning if both background processing and RGB to +gray are used when gamma correction happens. As with previous versions of +the library the results are numerically very incorrect in this case. + +There are some minor arithmetic changes in some transforms such as +png_set_background(), that might be detected by certain regression tests. + +Unknown chunk handling has been improved internally, without any API change. +This adds more correct option control of the unknown handling, corrects +a pre-existing bug where the per-chunk 'keep' setting is ignored, and makes +it possible to skip IDAT chunks in the sequential reader. + +The machine-generated configure files are no longer included in branches +libpng16 and later of the GIT repository. They continue to be included +in the tarball releases, however. + +Libpng-1.6.0 through 1.6.2 used the CMF bytes at the beginning of the IDAT +stream to set the size of the sliding window for reading instead of using the +default 32-kbyte sliding window size. It was discovered that there are +hundreds of PNG files in the wild that have incorrect CMF bytes that caused +zlib to issue the "invalid distance too far back" error and reject the file. +Libpng-1.6.3 and later calculate their own safe CMF from the image dimensions, +provide a way to revert to the libpng-1.5.x behavior (ignoring the CMF bytes +and using a 32-kbyte sliding window), by using + + png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, + PNG_OPTION_ON); + +and provide a tool (contrib/tools/pngfix) for rewriting a PNG file while +optimizing the CMF bytes in its IDAT chunk correctly. + +Libpng-1.6.0 and libpng-1.6.1 wrote uncompressed iTXt chunks with the wrong +length, which resulted in PNG files that cannot be read beyond the bad iTXt +chunk. This error was fixed in libpng-1.6.3, and a tool (called +contrib/tools/png-fix-itxt) has been added to the libpng distribution. + +Starting with libpng-1.6.17, the PNG_SAFE_LIMITS macro was eliminated +and safe limits are used by default (users who need larger limits +can still override them at compile time or run time, as described above). + +The new limits are + default spec limit + png_user_width_max 1,000,000 2,147,483,647 + png_user_height_max 1,000,000 2,147,483,647 + png_user_chunk_cache_max 128 unlimited + png_user_chunk_malloc_max 8,000,000 unlimited + +Starting with libpng-1.6.18, a PNG_RELEASE_BUILD macro was added, which allows +library builders to control compilation for an installed system (a release build). +It can be set for testing debug or beta builds to ensure that they will compile +when the build type is switched to RC or STABLE. In essence this overrides the +PNG_LIBPNG_BUILD_BASE_TYPE definition which is not directly user controllable. + +Starting with libpng-1.6.19, attempting to set an over-length PLTE chunk +is an error. Previously this requirement of the PNG specification was not +enforced, and the palette was always limited to 256 entries. An over-length +PLTE chunk found in an input PNG is silently truncated. + +Starting with libpng-1.6.31, the eXIf chunk is supported. Libpng does not +attempt to decode the Exif profile; it simply returns a byte array +containing the profile to the calling application which must do its own +decoding. + +.SH XIII. Detecting libpng + +The png_get_io_ptr() function has been present since libpng-0.88, has never +changed, and is unaffected by conditional compilation macros. It is the +best choice for use in configure scripts for detecting the presence of any +libpng version since 0.88. In an autoconf "configure.in" you could use + + AC_CHECK_LIB(png, png_get_io_ptr, ... + +.SH XV. Source code repository + +Since about February 2009, version 1.2.34, libpng has been under "git" source +control. The git repository was built from old libpng-x.y.z.tar.gz files +going back to version 0.70. You can access the git repository (read only) +at + + https://github.com/glennrp/libpng or + https://git.code.sf.net/p/libpng/code.git + +or you can browse it with a web browser at + + https://github.com/glennrp/libpng or + https://sourceforge.net/p/libpng/code/ci/libpng16/tree/ + +Patches can be sent to png-mng-implement at lists.sourceforge.net or +uploaded to the libpng bug tracker at + + https://libpng.sourceforge.io/ + +or as a "pull request" to + + https://github.com/glennrp/libpng/pulls + +We also accept patches built from the tar or zip distributions, and +simple verbal descriptions of bug fixes, reported either to the +SourceForge bug tracker, to the png-mng-implement at lists.sf.net +mailing list, as github issues. + +.SH XV. Coding style + +Our coding style is similar to the "Allman" style +(See https://en.wikipedia.org/wiki/Indent_style#Allman_style), with curly +braces on separate lines: + + if (condition) + { + action; + } + + else if (another condition) + { + another action; + } + +The braces can be omitted from simple one-line actions: + + if (condition) + return 0; + +We use 3-space indentation, except for continued statements which +are usually indented the same as the first line of the statement +plus four more spaces. + +For macro definitions we use 2-space indentation, always leaving the "#" +in the first column. + + #ifndef PNG_NO_FEATURE + # ifndef PNG_FEATURE_SUPPORTED + # define PNG_FEATURE_SUPPORTED + # endif + #endif + +Comments appear with the leading "/*" at the same indentation as +the statement that follows the comment: + + /* Single-line comment */ + statement; + + /* This is a multiple-line + * comment. + */ + statement; + +Very short comments can be placed after the end of the statement +to which they pertain: + + statement; /* comment */ + +We don't use C++ style ("//") comments. We have, however, +used them in the past in some now-abandoned MMX assembler +code. + +Functions and their curly braces are not indented, and +exported functions are marked with PNGAPI: + + /* This is a public function that is visible to + * application programmers. It does thus-and-so. + */ + void PNGAPI + png_exported_function(png_ptr, png_info, foo) + { + body; + } + +The return type and decorations are placed on a separate line +ahead of the function name, as illustrated above. + +The prototypes for all exported functions appear in png.h, +above the comment that says + + /* Maintainer: Put new public prototypes here ... */ + +We mark all non-exported functions with "/* PRIVATE */"": + + void /* PRIVATE */ + png_non_exported_function(png_ptr, png_info, foo) + { + body; + } + +The prototypes for non-exported functions (except for those in +pngtest) appear in pngpriv.h above the comment that says + + /* Maintainer: Put new private prototypes here ^ */ + +To avoid polluting the global namespace, the names of all exported +functions and variables begin with "png_", and all publicly visible C +preprocessor macros begin with "PNG". We request that applications that +use libpng *not* begin any of their own symbols with either of these strings. + +We put a space after the "sizeof" operator and we omit the +optional parentheses around its argument when the argument +is an expression, not a type name, and we always enclose the +sizeof operator, with its argument, in parentheses: + + (sizeof (png_uint_32)) + (sizeof array) + +Prior to libpng-1.6.0 we used a "png_sizeof()" macro, formatted as +though it were a function. + +Control keywords if, for, while, and switch are always followed by a space +to distinguish them from function calls, which have no trailing space. + +We put a space after each comma and after each semicolon +in "for" statements, and we put spaces before and after each +C binary operator and after "for" or "while", and before +"?". We don't put a space between a typecast and the expression +being cast, nor do we put one between a function name and the +left parenthesis that follows it: + + for (i = 2; i > 0; \-\-i) + y[i] = a(x) + (int)b; + +We prefer #ifdef and #ifndef to #if defined() and #if !defined() +when there is only one macro being tested. We always use parentheses +with "defined". + +We express integer constants that are used as bit masks in hex format, +with an even number of lower-case hex digits, and to make them unsigned +(e.g., 0x00U, 0xffU, 0x0100U) and long if they are greater than 0x7fff +(e.g., 0xffffUL). + +We prefer to use underscores rather than camelCase in names, except +for a few type names that we inherit from zlib.h. + +We prefer "if (something != 0)" and "if (something == 0)" over +"if (something)" and if "(!something)", respectively, and for pointers +we prefer "if (some_pointer != NULL)" or "if (some_pointer == NULL)". + +We do not use the TAB character for indentation in the C sources. + +Lines do not exceed 80 characters. + +Other rules can be inferred by inspecting the libpng source. + +.SH NOTE + +Note about libpng version numbers: + +Due to various miscommunications, unforeseen code incompatibilities +and occasional factors outside the authors' control, version numbering +on the library has not always been consistent and straightforward. +The following table summarizes matters since version 0.89c, which was +the first widely used release: + + source png.h png.h shared-lib + version string int version + ------- ------ ----- ---------- + 0.89c "1.0 beta 3" 0.89 89 1.0.89 + 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + 0.97c 0.97 97 2.0.97 + 0.98 0.98 98 2.0.98 + 0.99 0.99 98 2.0.99 + 0.99a-m 0.99 99 2.0.99 + 1.00 1.00 100 2.1.0 [100 should be 10000] + 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + 1.0.1 png.h string is 10001 2.1.0 + 1.0.1a-e identical to the 10002 from here on, the shared library + 1.0.2 source version) 10002 is 2.V where V is the source code + 1.0.2a-b 10003 version, except as noted. + 1.0.3 10003 + 1.0.3a-d 10004 + 1.0.4 10004 + 1.0.4a-f 10005 + 1.0.5 (+ 2 patches) 10005 + 1.0.5a-d 10006 + 1.0.5e-r 10100 (not source compatible) + 1.0.5s-v 10006 (not binary compatible) + 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + 1.0.6d-f 10007 (still binary incompatible) + 1.0.6g 10007 + 1.0.6h 10007 10.6h (testing xy.z so-numbering) + 1.0.6i 10007 10.6i + 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + 1.0.7 1 10007 (still compatible) + ... + 1.0.69 10 10069 10.so.0.69[.0] + ... + 1.2.59 13 10259 12.so.0.59[.0] + ... + 1.4.20 14 10420 14.so.0.20[.0] + ... + 1.5.30 15 10530 15.so.15.30[.0] + ... + 1.6.35 16 10635 16.so.16.35[.0] + +Henceforth the source version will match the shared-library minor and +patch numbers; the shared-library major version number will be used for +changes in backward compatibility, as it is intended. +The PNG_PNGLIB_VER macro, which is not used within libpng but is +available for applications, is an unsigned integer of the form XYYZZ +corresponding to the source version X.Y.Z (leading zeros in Y and Z). +Beta versions were given the previous public release number plus a +letter, until version 1.0.6j; from then on they were given the upcoming +public release number plus "betaNN" or "rcNN". + +.SH "SEE ALSO" +.IR libpngpf(3) ", " png(5) +.LP +.IR libpng : +.IP +https://libpng.sourceforge.io/ (follow the [DOWNLOAD] link) +http://www.libpng.org/pub/png + +.LP +.IR zlib : +.IP +(generally) at the same location as +.I libpng +or at +.br +https://zlib.net/ + +.LP +.IR PNG specification: RFC 2083 +.IP +(generally) at the same location as +.I libpng +or at +.br +https://www.ietf.org/rfc/rfc2083.txt +.br +or (as a W3C Recommendation) at +.br +https://www.w3.org/TR/REC-png.html + +.LP +In the case of any inconsistency between the PNG specification +and this library, the specification takes precedence. + +.SH AUTHORS +This man page: +Initially created by Glenn Randers-Pehrson. +Maintained by Cosmin Truta. + +The contributing authors would like to thank all those who helped +with testing, bug fixes, and patience. This wouldn't have been +possible without all of you. + +Thanks to Frank J. T. Wojcik for helping with the documentation. + +Libpng: +Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc. +Maintained by Cosmin Truta. + +Supported by the PNG development group +.br +png-mng-implement at lists.sourceforge.net (subscription required; visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement +to subscribe). + +.\" end of man page diff --git a/libs/platform/RK1808/libpng/share/man/man3/libpngpf.3 b/libs/platform/RK1808/libpng/share/man/man3/libpngpf.3 new file mode 100644 index 0000000..6909c70 --- /dev/null +++ b/libs/platform/RK1808/libpng/share/man/man3/libpngpf.3 @@ -0,0 +1,24 @@ +.TH LIBPNGPF 3 "April 14, 2019" +.SH NAME +libpng \- Portable Network Graphics (PNG) Reference Library 1.6.37 +(private functions) + +.SH SYNOPSIS +\fB#include \fI"pngpriv.h" + +\fBAs of libpng version \fP\fI1.5.1\fP\fB, this section is no longer +\fP\fImaintained\fP\fB, now that the private function prototypes are hidden in +\fP\fIpngpriv.h\fP\fB and not accessible to applications. Look in +\fP\fIpngpriv.h\fP\fB for the prototypes and a short description of each +function. + +.SH DESCRIPTION +The functions previously listed here are used privately by libpng and are not +available for use by applications. They are not "exported" to applications +using shared libraries. + +.SH "SEE ALSO" +.BR "png"(5), " libpng"(3), " zlib"(3), " deflate"(5), " " and " zlib"(5) + +.SH AUTHORS +Cosmin Truta, Glenn Randers-Pehrson diff --git a/libs/platform/RK1808/libpng/share/man/man5/png.5 b/libs/platform/RK1808/libpng/share/man/man5/png.5 new file mode 100644 index 0000000..c2da95c --- /dev/null +++ b/libs/platform/RK1808/libpng/share/man/man5/png.5 @@ -0,0 +1,84 @@ +.TH PNG 5 "April 14, 2019" +.SH NAME +png \- Portable Network Graphics (PNG) format + +.SH DESCRIPTION +PNG (Portable Network Graphics) is an extensible file format for the +lossless, portable, well-compressed storage of raster images. PNG +provides a patent-free replacement for GIF, and can also replace many +common uses of TIFF. Indexed-color, grayscale, and truecolor images are +supported, plus an optional alpha channel. Sample depths range from +1 to 16 bits. +.br +PNG is designed to work well in online viewing applications, such +as the World Wide Web, so it is fully streamable with a progressive +display option. PNG is robust, providing both full file integrity +checking and fast, simple detection of common transmission errors. +Also, PNG can store gamma and chromaticity data for improved color +matching on heterogeneous platforms. + +.SH "SEE ALSO" +.BR "libpng"(3), " libpngpf"(3), " zlib"(3), " deflate"(5), " " and " zlib"(5) +.LP +PNG Specification (Second Edition), November 2003: +.IP +.br +https://www.w3.org/TR/2003/REC-PNG-20031110/ +.LP +PNG 1.2 Specification, July 1999: +.IP +.br +https://png-mng.sourceforge.io/pub/png/spec/1.2/ +.LP +PNG 1.0 Specification, October 1996: +.IP +.br +RFC 2083 +.br +https://www.ietf.org/rfc/rfc2083.txt +.IP +.br +or W3C Recommendation +.br +https://www.w3.org/TR/REC-png-961001 + +.SH AUTHORS +This man page: Cosmin Truta, Glenn Randers-Pehrson +.LP +Portable Network Graphics (PNG) Specification (Second Edition) +Information technology - Computer graphics and image processing - +Portable Network Graphics (PNG): Functional specification. +ISO/IEC 15948:2003 (E) (November 10, 2003): David Duce and others. +.LP +Portable Network Graphics (PNG) Specification Version 1.2 (July 8, 1999): +Glenn Randers-Pehrson and others (png-list). +.LP +Portable Network Graphics (PNG) Specification Version 1.0 (October 1, 1996): +Thomas Boutell and others (png-list). + +.SH COPYRIGHT +.LP +This man page is +.br +Copyright (c) 2018-2019 Cosmin Truta. +.br +Copyright (c) 1998-2006 Glenn Randers-Pehrson. +.br +See png.h for conditions of use and distribution. +.LP +The PNG Specification (Second Edition) is +.br +Copyright (c) 2003 W3C. (MIT, ERCIM, Keio), All Rights Reserved. +.LP +The PNG-1.2 Specification is +.br +Copyright (c) 1999 Glenn Randers-Pehrson. +.br +See the specification for conditions of use and distribution. +.LP +The PNG-1.0 Specification is +.br +Copyright (c) 1996 Massachusetts Institute of Technology. +.br +See the specification for conditions of use and distribution. +.\" end of man page diff --git a/libs/platform/RK1808/zlib/include/zconf.h b/libs/platform/RK1808/zlib/include/zconf.h new file mode 100644 index 0000000..77398c1 --- /dev/null +++ b/libs/platform/RK1808/zlib/include/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/platform/RK1808/zlib/include/zlib.h b/libs/platform/RK1808/zlib/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/libs/platform/RK1808/zlib/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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 notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#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 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#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) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/platform/RK1808/zlib/lib/Linux/libz.a b/libs/platform/RK1808/zlib/lib/Linux/libz.a new file mode 100644 index 0000000..de8e5f8 Binary files /dev/null and b/libs/platform/RK1808/zlib/lib/Linux/libz.a differ diff --git a/libs/platform/RK1808/zlib/lib/Linux/libz.so b/libs/platform/RK1808/zlib/lib/Linux/libz.so new file mode 100644 index 0000000..286b21a Binary files /dev/null and b/libs/platform/RK1808/zlib/lib/Linux/libz.so differ diff --git a/libs/platform/RK1808/zlib/lib/Linux/libz.so.1 b/libs/platform/RK1808/zlib/lib/Linux/libz.so.1 new file mode 100644 index 0000000..286b21a Binary files /dev/null and b/libs/platform/RK1808/zlib/lib/Linux/libz.so.1 differ diff --git a/libs/platform/RK1808/zlib/lib/Linux/libz.so.1.2.11 b/libs/platform/RK1808/zlib/lib/Linux/libz.so.1.2.11 new file mode 100644 index 0000000..286b21a Binary files /dev/null and b/libs/platform/RK1808/zlib/lib/Linux/libz.so.1.2.11 differ diff --git a/libs/platform/RK1808/zlib/lib/Linux/pkgconfig/zlib.pc b/libs/platform/RK1808/zlib/lib/Linux/pkgconfig/zlib.pc new file mode 100644 index 0000000..5495a28 --- /dev/null +++ b/libs/platform/RK1808/zlib/lib/Linux/pkgconfig/zlib.pc @@ -0,0 +1,13 @@ +prefix=/home/xz/Documents/testing/compile_test/zlib1211/zlib-1.2.11/rk1808 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +sharedlibdir=${libdir} +includedir=${prefix}/include + +Name: zlib +Description: zlib compression library +Version: 1.2.11 + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/libs/platform/RK3399PRO/.gitignore b/libs/platform/RK3399PRO/.gitignore new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/libs/platform/RK3399PRO/.gitignore @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/platform/RK3399PRO/libjpeg/include/jconfig.h b/libs/platform/RK3399PRO/libjpeg/include/jconfig.h new file mode 100644 index 0000000..cb09382 --- /dev/null +++ b/libs/platform/RK3399PRO/libjpeg/include/jconfig.h @@ -0,0 +1,163 @@ +/* + * jconfig.txt + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * Modified 2009-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + * DO NOT EDIT THIS FILE --- IT WON'T ACCOMPLISH ANYTHING. + * EDIT A COPY NAMED JCONFIG.H. + */ + + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.txt) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +/* #undef CHAR_IS_UNSIGNED */ + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +/* #undef NEED_BSD_STRINGS */ + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#define NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not enum, on Windows systems. + */ +#ifdef _WIN32 +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* #undef USE_SETMODE */ + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/libs/platform/RK3399PRO/libjpeg/include/jerror.h b/libs/platform/RK3399PRO/libjpeg/include/jerror.h new file mode 100644 index 0000000..a4b661f --- /dev/null +++ b/libs/platform/RK3399PRO/libjpeg/include/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2012 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_BEFORE, "Invalid JPEG file structure: %s before SOF") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/libs/platform/RK3399PRO/libjpeg/include/jmorecfg.h b/libs/platform/RK3399PRO/libjpeg/include/jmorecfg.h new file mode 100644 index 0000000..679d68b --- /dev/null +++ b/libs/platform/RK3399PRO/libjpeg/include/jmorecfg.h @@ -0,0 +1,446 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 9 for 9-bit sample values + * 10 for 10-bit sample values + * 11 for 11-bit sample values + * 12 for 12-bit sample values + * Only 8, 9, 10, 11, and 12 bits sample data precision are supported for + * full-feature DCT processing. Further depths up to 16-bit may be added + * later for the lossless modes of operation. + * Run-time selection and conversion of data precision will be added later + * and are currently not supported, sorry. + * Exception: The transcoding part (jpegtran) supports all settings in a + * single instance, since it operates on the level of DCT coefficients and + * not sample values. The DCT coefficients are of the same type (16 bits) + * in all cases (see below). + */ + +#define BITS_IN_JSAMPLE 8 /* use 8, 9, 10, 11, or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 9 +/* JSAMPLE should be the smallest type that will hold the values 0..511. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 511 +#define CENTERJSAMPLE 256 + +#endif /* BITS_IN_JSAMPLE == 9 */ + + +#if BITS_IN_JSAMPLE == 10 +/* JSAMPLE should be the smallest type that will hold the values 0..1023. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 1023 +#define CENTERJSAMPLE 512 + +#endif /* BITS_IN_JSAMPLE == 10 */ + + +#if BITS_IN_JSAMPLE == 11 +/* JSAMPLE should be the smallest type that will hold the values 0..2047. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 2047 +#define CENTERJSAMPLE 1024 + +#endif /* BITS_IN_JSAMPLE == 11 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* The noreturn type identifier is used to declare functions + * which cannot return. + * Compilers can thus create more optimized code and perform + * better checks for warnings and errors. + * Static analyzer tools can make improved inferences about + * execution paths and are prevented from giving false alerts. + * + * Unfortunately, the proposed specifications of corresponding + * extensions in the Dec 2011 ISO C standard revision (C11), + * GCC, MSVC, etc. are not viable. + * Thus we introduce a user defined type to declare noreturn + * functions at least for clarity. A proper compiler would + * have a suitable noreturn type to match in place of void. + */ + +#ifndef HAVE_NORETURN_T +typedef void noreturn_t; +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +#if defined FALSE || defined TRUE || defined QGLOBAL_H +/* Qt3 defines FALSE and TRUE as "const" variables in qglobal.h */ +typedef int boolean; +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#else +typedef enum { FALSE = 0, TRUE = 1 } boolean; +#endif +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected more than 8-bit data precision, it is dangerous to + * turn off ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only + * good for 8-bit precision, so arithmetic coding is recommended for higher + * precision. The Huffman encoder normally uses entropy optimization to + * compute usable tables for higher precision. Otherwise, you'll have to + * supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? (Requires DCT_ISLOW)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/libs/platform/RK3399PRO/libjpeg/include/jpeglib.h b/libs/platform/RK3399PRO/libjpeg/include/jpeglib.h new file mode 100644 index 0000000..f4fbf23 --- /dev/null +++ b/libs/platform/RK3399PRO/libjpeg/include/jpeglib.h @@ -0,0 +1,1180 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version IDs for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 90". + */ + +#define JPEG_LIB_VERSION 90 /* Compatibility version 9.0 */ +#define JPEG_LIB_VERSION_MAJOR 9 +#define JPEG_LIB_VERSION_MINOR 1 + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, + * so don't change them if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 coefficients */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = + * ceil(image_width * Hi/Hmax * DCT_h_scaled_size/block_size) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* For decompression, in cases where some of the components will be + * ignored (eg grayscale output from YCbCr image), we can skip most + * computations for the unused components. + * For compression, some of the components will need further quantization + * scale by factor of 2 after DCT (eg BG_YCC output from normal RGB input). + * The field is first set TRUE for decompression, FALSE for compression + * in initial_setup, and then adapted in color conversion setup. + */ + boolean component_needed; + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue, standard RGB (sRGB) */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV), standard YCC */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK, /* Y/Cb/Cr/K */ + JCS_BG_RGB, /* big gamut red/green/blue, bg-sRGB */ + JCS_BG_YCC /* big gamut Y/Cb/Cr, bg-sYCC */ +} J_COLOR_SPACE; + +/* Supported color transforms. */ + +typedef enum { + JCT_NONE = 0, + JCT_SUBTRACT_GREEN = 1 +} J_COLOR_TRANSFORM; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + J_COLOR_TRANSFORM color_transform; + /* Color transform identifier, writes LSE marker if nonzero */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + J_COLOR_TRANSFORM color_transform; + /* Color transform identifier derived from LSE marker, otherwise zero */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(noreturn_t, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Android/libjpeg.a b/libs/platform/RK3399PRO/libjpeg/lib/Android/libjpeg.a new file mode 100644 index 0000000..5394bc1 Binary files /dev/null and b/libs/platform/RK3399PRO/libjpeg/lib/Android/libjpeg.a differ diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Android/libjpeg.so b/libs/platform/RK3399PRO/libjpeg/lib/Android/libjpeg.so new file mode 100644 index 0000000..aeda23e Binary files /dev/null and b/libs/platform/RK3399PRO/libjpeg/lib/Android/libjpeg.so differ diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.a b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.a new file mode 100644 index 0000000..1fbd0a9 Binary files /dev/null and b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.a differ diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.la b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.la new file mode 100644 index 0000000..0b8f91c --- /dev/null +++ b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.la @@ -0,0 +1,41 @@ +# libjpeg.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.2 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libjpeg.so.8' + +# Names of this library. +library_names='libjpeg.so.8.4.0 libjpeg.so.8 libjpeg.so' + +# The name of the static archive. +old_library='libjpeg.a' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rk1808/lib' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libjpeg. +current=12 +age=4 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/home/xz/Documents/testing/compile_test/jpegsrc.v8d1/jpeg-8d1/rk1808/lib' diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so new file mode 100644 index 0000000..f91df71 Binary files /dev/null and b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so differ diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so.8 b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so.8 new file mode 100644 index 0000000..f91df71 Binary files /dev/null and b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so.8 differ diff --git a/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so.8.4.0 b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so.8.4.0 new file mode 100644 index 0000000..f91df71 Binary files /dev/null and b/libs/platform/RK3399PRO/libjpeg/lib/Linux/libjpeg.so.8.4.0 differ diff --git a/libs/platform/RK3399PRO/libpng/include/libpng16/png.h b/libs/platform/RK3399PRO/libpng/include/libpng16/png.h new file mode 100644 index 0000000..5fb494f --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/include/libpng16/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.38 - September 14, 2022 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2022 The PNG Reference Library Authors. + * * Copyright (c) 2018-2022 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.38 16 10638 16.so.16.38[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.38" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 38 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_38; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explanation of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer calculations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK3399PRO/libpng/include/libpng16/pngconf.h b/libs/platform/RK3399PRO/libpng/include/libpng16/pngconf.h new file mode 100644 index 0000000..89d28f8 --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/include/libpng16/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.38 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK3399PRO/libpng/include/libpng16/pnglibconf.h b/libs/platform/RK3399PRO/libpng/include/libpng16/pnglibconf.h new file mode 100644 index 0000000..7a0e1fa --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/include/libpng16/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.38 */ + +/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK3399PRO/libpng/include/png.h b/libs/platform/RK3399PRO/libpng/include/png.h new file mode 100644 index 0000000..5fb494f --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/include/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.38 - September 14, 2022 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2022 The PNG Reference Library Authors. + * * Copyright (c) 2018-2022 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.38 16 10638 16.so.16.38[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.38" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 38 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_38; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explanation of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer calculations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK3399PRO/libpng/include/pngconf.h b/libs/platform/RK3399PRO/libpng/include/pngconf.h new file mode 100644 index 0000000..89d28f8 --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/include/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.38 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK3399PRO/libpng/include/pnglibconf.h b/libs/platform/RK3399PRO/libpng/include/pnglibconf.h new file mode 100644 index 0000000..7a0e1fa --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/include/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.38 */ + +/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK3399PRO/libpng/lib/Android/libpng.a b/libs/platform/RK3399PRO/libpng/lib/Android/libpng.a new file mode 100644 index 0000000..90cb292 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Android/libpng.a differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Android/libpng.so b/libs/platform/RK3399PRO/libpng/lib/Android/libpng.so new file mode 100644 index 0000000..1792a09 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Android/libpng.so differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Android/libpng16.a b/libs/platform/RK3399PRO/libpng/lib/Android/libpng16.a new file mode 100644 index 0000000..90cb292 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Android/libpng16.a differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Android/libpng16.so b/libs/platform/RK3399PRO/libpng/lib/Android/libpng16.so new file mode 100644 index 0000000..1792a09 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Android/libpng16.so differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.a b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.a new file mode 100644 index 0000000..eef42bb Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.a differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.la b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.la new file mode 100644 index 0000000..0156394 --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.la @@ -0,0 +1,41 @@ +# libpng16.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libpng16.so.16' + +# Names of this library. +library_names='libpng16.so.16.37.0 libpng16.so.16 libpng16.so' + +# The name of the static archive. +old_library='libpng16.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rk1808/lib/ -lz -lm' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libpng16. +current=53 +age=37 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='//home/xz/Documents/testing/compile_test/lpng1637/rk1808/lib' diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.so b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.so new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng.so differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.a b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.a new file mode 100644 index 0000000..eef42bb Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.a differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.la b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.la new file mode 100644 index 0000000..0156394 --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.la @@ -0,0 +1,41 @@ +# libpng16.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libpng16.so.16' + +# Names of this library. +library_names='libpng16.so.16.37.0 libpng16.so.16 libpng16.so' + +# The name of the static archive. +old_library='libpng16.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rk1808/lib/ -lz -lm' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libpng16. +current=53 +age=37 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='//home/xz/Documents/testing/compile_test/lpng1637/rk1808/lib' diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16 b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16 new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16 differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16.37.0 b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16.37.0 new file mode 100644 index 0000000..35a39c8 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16.37.0 differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16.38.0 b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16.38.0 new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3399PRO/libpng/lib/Linux/libpng16.so.16.38.0 differ diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/pkgconfig/libpng.pc b/libs/platform/RK3399PRO/libpng/lib/Linux/pkgconfig/libpng.pc new file mode 100644 index 0000000..fa2b2de --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/lib/Linux/pkgconfig/libpng.pc @@ -0,0 +1,12 @@ +prefix=//home/xz/Documents/testing/compile_test/lpng1637/rk1808 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/libpng16 + +Name: libpng +Description: Loads and saves PNG files +Version: 1.6.37 +Requires: zlib +Libs: -L${libdir} -lpng16 +Libs.private: -lm -lz -lm +Cflags: -I${includedir} diff --git a/libs/platform/RK3399PRO/libpng/lib/Linux/pkgconfig/libpng16.pc b/libs/platform/RK3399PRO/libpng/lib/Linux/pkgconfig/libpng16.pc new file mode 100644 index 0000000..fa2b2de --- /dev/null +++ b/libs/platform/RK3399PRO/libpng/lib/Linux/pkgconfig/libpng16.pc @@ -0,0 +1,12 @@ +prefix=//home/xz/Documents/testing/compile_test/lpng1637/rk1808 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/libpng16 + +Name: libpng +Description: Loads and saves PNG files +Version: 1.6.37 +Requires: zlib +Libs: -L${libdir} -lpng16 +Libs.private: -lm -lz -lm +Cflags: -I${includedir} diff --git a/libs/platform/RK3399PRO/zlib/include/zconf.h b/libs/platform/RK3399PRO/zlib/include/zconf.h new file mode 100644 index 0000000..5e1d68a --- /dev/null +++ b/libs/platform/RK3399PRO/zlib/include/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/platform/RK3399PRO/zlib/include/zlib.h b/libs/platform/RK3399PRO/zlib/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/libs/platform/RK3399PRO/zlib/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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 notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#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 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#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) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/platform/RK3399PRO/zlib/lib/Android/libz.a b/libs/platform/RK3399PRO/zlib/lib/Android/libz.a new file mode 100644 index 0000000..81f55a6 Binary files /dev/null and b/libs/platform/RK3399PRO/zlib/lib/Android/libz.a differ diff --git a/libs/platform/RK3399PRO/zlib/lib/Android/libz.so b/libs/platform/RK3399PRO/zlib/lib/Android/libz.so new file mode 100644 index 0000000..577172c Binary files /dev/null and b/libs/platform/RK3399PRO/zlib/lib/Android/libz.so differ diff --git a/libs/platform/RK3399PRO/zlib/lib/Linux/libz.a b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.a new file mode 100644 index 0000000..de8e5f8 Binary files /dev/null and b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.a differ diff --git a/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so new file mode 100644 index 0000000..286b21a Binary files /dev/null and b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so differ diff --git a/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so.1 b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so.1 new file mode 100644 index 0000000..286b21a Binary files /dev/null and b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so.1 differ diff --git a/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so.1.2.11 b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so.1.2.11 new file mode 100644 index 0000000..286b21a Binary files /dev/null and b/libs/platform/RK3399PRO/zlib/lib/Linux/libz.so.1.2.11 differ diff --git a/libs/platform/RK3399PRO/zlib/lib/Linux/pkgconfig/zlib.pc b/libs/platform/RK3399PRO/zlib/lib/Linux/pkgconfig/zlib.pc new file mode 100644 index 0000000..5495a28 --- /dev/null +++ b/libs/platform/RK3399PRO/zlib/lib/Linux/pkgconfig/zlib.pc @@ -0,0 +1,13 @@ +prefix=/home/xz/Documents/testing/compile_test/zlib1211/zlib-1.2.11/rk1808 +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +sharedlibdir=${libdir} +includedir=${prefix}/include + +Name: zlib +Description: zlib compression library +Version: 1.2.11 + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/libs/platform/RK3566_3568/.gitignore b/libs/platform/RK3566_3568/.gitignore new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/libs/platform/RK3566_3568/.gitignore @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/platform/RK3566_3568/libjpeg/include/jconfig.h b/libs/platform/RK3566_3568/libjpeg/include/jconfig.h new file mode 100644 index 0000000..cb09382 --- /dev/null +++ b/libs/platform/RK3566_3568/libjpeg/include/jconfig.h @@ -0,0 +1,163 @@ +/* + * jconfig.txt + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * Modified 2009-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + * DO NOT EDIT THIS FILE --- IT WON'T ACCOMPLISH ANYTHING. + * EDIT A COPY NAMED JCONFIG.H. + */ + + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.txt) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +/* #undef CHAR_IS_UNSIGNED */ + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +/* #undef NEED_BSD_STRINGS */ + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#define NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not enum, on Windows systems. + */ +#ifdef _WIN32 +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* #undef USE_SETMODE */ + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/libs/platform/RK3566_3568/libjpeg/include/jerror.h b/libs/platform/RK3566_3568/libjpeg/include/jerror.h new file mode 100644 index 0000000..a4b661f --- /dev/null +++ b/libs/platform/RK3566_3568/libjpeg/include/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2012 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_BEFORE, "Invalid JPEG file structure: %s before SOF") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/libs/platform/RK3566_3568/libjpeg/include/jmorecfg.h b/libs/platform/RK3566_3568/libjpeg/include/jmorecfg.h new file mode 100644 index 0000000..679d68b --- /dev/null +++ b/libs/platform/RK3566_3568/libjpeg/include/jmorecfg.h @@ -0,0 +1,446 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 9 for 9-bit sample values + * 10 for 10-bit sample values + * 11 for 11-bit sample values + * 12 for 12-bit sample values + * Only 8, 9, 10, 11, and 12 bits sample data precision are supported for + * full-feature DCT processing. Further depths up to 16-bit may be added + * later for the lossless modes of operation. + * Run-time selection and conversion of data precision will be added later + * and are currently not supported, sorry. + * Exception: The transcoding part (jpegtran) supports all settings in a + * single instance, since it operates on the level of DCT coefficients and + * not sample values. The DCT coefficients are of the same type (16 bits) + * in all cases (see below). + */ + +#define BITS_IN_JSAMPLE 8 /* use 8, 9, 10, 11, or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 9 +/* JSAMPLE should be the smallest type that will hold the values 0..511. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 511 +#define CENTERJSAMPLE 256 + +#endif /* BITS_IN_JSAMPLE == 9 */ + + +#if BITS_IN_JSAMPLE == 10 +/* JSAMPLE should be the smallest type that will hold the values 0..1023. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 1023 +#define CENTERJSAMPLE 512 + +#endif /* BITS_IN_JSAMPLE == 10 */ + + +#if BITS_IN_JSAMPLE == 11 +/* JSAMPLE should be the smallest type that will hold the values 0..2047. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 2047 +#define CENTERJSAMPLE 1024 + +#endif /* BITS_IN_JSAMPLE == 11 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* The noreturn type identifier is used to declare functions + * which cannot return. + * Compilers can thus create more optimized code and perform + * better checks for warnings and errors. + * Static analyzer tools can make improved inferences about + * execution paths and are prevented from giving false alerts. + * + * Unfortunately, the proposed specifications of corresponding + * extensions in the Dec 2011 ISO C standard revision (C11), + * GCC, MSVC, etc. are not viable. + * Thus we introduce a user defined type to declare noreturn + * functions at least for clarity. A proper compiler would + * have a suitable noreturn type to match in place of void. + */ + +#ifndef HAVE_NORETURN_T +typedef void noreturn_t; +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +#if defined FALSE || defined TRUE || defined QGLOBAL_H +/* Qt3 defines FALSE and TRUE as "const" variables in qglobal.h */ +typedef int boolean; +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#else +typedef enum { FALSE = 0, TRUE = 1 } boolean; +#endif +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected more than 8-bit data precision, it is dangerous to + * turn off ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only + * good for 8-bit precision, so arithmetic coding is recommended for higher + * precision. The Huffman encoder normally uses entropy optimization to + * compute usable tables for higher precision. Otherwise, you'll have to + * supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? (Requires DCT_ISLOW)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/libs/platform/RK3566_3568/libjpeg/include/jpeglib.h b/libs/platform/RK3566_3568/libjpeg/include/jpeglib.h new file mode 100644 index 0000000..f4fbf23 --- /dev/null +++ b/libs/platform/RK3566_3568/libjpeg/include/jpeglib.h @@ -0,0 +1,1180 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version IDs for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 90". + */ + +#define JPEG_LIB_VERSION 90 /* Compatibility version 9.0 */ +#define JPEG_LIB_VERSION_MAJOR 9 +#define JPEG_LIB_VERSION_MINOR 1 + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, + * so don't change them if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 coefficients */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = + * ceil(image_width * Hi/Hmax * DCT_h_scaled_size/block_size) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* For decompression, in cases where some of the components will be + * ignored (eg grayscale output from YCbCr image), we can skip most + * computations for the unused components. + * For compression, some of the components will need further quantization + * scale by factor of 2 after DCT (eg BG_YCC output from normal RGB input). + * The field is first set TRUE for decompression, FALSE for compression + * in initial_setup, and then adapted in color conversion setup. + */ + boolean component_needed; + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue, standard RGB (sRGB) */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV), standard YCC */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK, /* Y/Cb/Cr/K */ + JCS_BG_RGB, /* big gamut red/green/blue, bg-sRGB */ + JCS_BG_YCC /* big gamut Y/Cb/Cr, bg-sYCC */ +} J_COLOR_SPACE; + +/* Supported color transforms. */ + +typedef enum { + JCT_NONE = 0, + JCT_SUBTRACT_GREEN = 1 +} J_COLOR_TRANSFORM; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + J_COLOR_TRANSFORM color_transform; + /* Color transform identifier, writes LSE marker if nonzero */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + J_COLOR_TRANSFORM color_transform; + /* Color transform identifier derived from LSE marker, otherwise zero */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(noreturn_t, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/libs/platform/RK3566_3568/libjpeg/lib/Android/libjpeg.a b/libs/platform/RK3566_3568/libjpeg/lib/Android/libjpeg.a new file mode 100644 index 0000000..5394bc1 Binary files /dev/null and b/libs/platform/RK3566_3568/libjpeg/lib/Android/libjpeg.a differ diff --git a/libs/platform/RK3566_3568/libjpeg/lib/Android/libjpeg.so b/libs/platform/RK3566_3568/libjpeg/lib/Android/libjpeg.so new file mode 100644 index 0000000..aeda23e Binary files /dev/null and b/libs/platform/RK3566_3568/libjpeg/lib/Android/libjpeg.so differ diff --git a/libs/platform/RK3566_3568/libjpeg/lib/Linux/libjpeg.a b/libs/platform/RK3566_3568/libjpeg/lib/Linux/libjpeg.a new file mode 100644 index 0000000..03d6c00 Binary files /dev/null and b/libs/platform/RK3566_3568/libjpeg/lib/Linux/libjpeg.a differ diff --git a/libs/platform/RK3566_3568/libjpeg/lib/Linux/libjpeg.so b/libs/platform/RK3566_3568/libjpeg/lib/Linux/libjpeg.so new file mode 100644 index 0000000..ffc5282 Binary files /dev/null and b/libs/platform/RK3566_3568/libjpeg/lib/Linux/libjpeg.so differ diff --git a/libs/platform/RK3566_3568/libpng/include/libpng16/png.h b/libs/platform/RK3566_3568/libpng/include/libpng16/png.h new file mode 100644 index 0000000..5fb494f --- /dev/null +++ b/libs/platform/RK3566_3568/libpng/include/libpng16/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.38 - September 14, 2022 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2022 The PNG Reference Library Authors. + * * Copyright (c) 2018-2022 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.38 16 10638 16.so.16.38[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.38" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 38 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_38; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explanation of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer calculations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK3566_3568/libpng/include/libpng16/pngconf.h b/libs/platform/RK3566_3568/libpng/include/libpng16/pngconf.h new file mode 100644 index 0000000..89d28f8 --- /dev/null +++ b/libs/platform/RK3566_3568/libpng/include/libpng16/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.38 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK3566_3568/libpng/include/libpng16/pnglibconf.h b/libs/platform/RK3566_3568/libpng/include/libpng16/pnglibconf.h new file mode 100644 index 0000000..7a0e1fa --- /dev/null +++ b/libs/platform/RK3566_3568/libpng/include/libpng16/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.38 */ + +/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK3566_3568/libpng/include/png.h b/libs/platform/RK3566_3568/libpng/include/png.h new file mode 100644 index 0000000..5fb494f --- /dev/null +++ b/libs/platform/RK3566_3568/libpng/include/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.38 - September 14, 2022 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2022 The PNG Reference Library Authors. + * * Copyright (c) 2018-2022 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.38 16 10638 16.so.16.38[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.38" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 38 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_38; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explanation of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer calculations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK3566_3568/libpng/include/pngconf.h b/libs/platform/RK3566_3568/libpng/include/pngconf.h new file mode 100644 index 0000000..89d28f8 --- /dev/null +++ b/libs/platform/RK3566_3568/libpng/include/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.38 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK3566_3568/libpng/include/pnglibconf.h b/libs/platform/RK3566_3568/libpng/include/pnglibconf.h new file mode 100644 index 0000000..7a0e1fa --- /dev/null +++ b/libs/platform/RK3566_3568/libpng/include/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.38 */ + +/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK3566_3568/libpng/lib/Android/libpng16.a b/libs/platform/RK3566_3568/libpng/lib/Android/libpng16.a new file mode 100644 index 0000000..90cb292 Binary files /dev/null and b/libs/platform/RK3566_3568/libpng/lib/Android/libpng16.a differ diff --git a/libs/platform/RK3566_3568/libpng/lib/Android/libpng16.so b/libs/platform/RK3566_3568/libpng/lib/Android/libpng16.so new file mode 100644 index 0000000..1792a09 Binary files /dev/null and b/libs/platform/RK3566_3568/libpng/lib/Android/libpng16.so differ diff --git a/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.a b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.a new file mode 100644 index 0000000..61b56ba Binary files /dev/null and b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.a differ diff --git a/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so differ diff --git a/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so.16 b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so.16 new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so.16 differ diff --git a/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so.16.38.0 b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so.16.38.0 new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3566_3568/libpng/lib/Linux/libpng16.so.16.38.0 differ diff --git a/libs/platform/RK3566_3568/zlib/include/zconf.h b/libs/platform/RK3566_3568/zlib/include/zconf.h new file mode 100644 index 0000000..5e1d68a --- /dev/null +++ b/libs/platform/RK3566_3568/zlib/include/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/platform/RK3566_3568/zlib/include/zlib.h b/libs/platform/RK3566_3568/zlib/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/libs/platform/RK3566_3568/zlib/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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 notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#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 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#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) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/platform/RK3566_3568/zlib/lib/Android/libz.a b/libs/platform/RK3566_3568/zlib/lib/Android/libz.a new file mode 100644 index 0000000..81f55a6 Binary files /dev/null and b/libs/platform/RK3566_3568/zlib/lib/Android/libz.a differ diff --git a/libs/platform/RK3566_3568/zlib/lib/Android/libz.so b/libs/platform/RK3566_3568/zlib/lib/Android/libz.so new file mode 100644 index 0000000..577172c Binary files /dev/null and b/libs/platform/RK3566_3568/zlib/lib/Android/libz.so differ diff --git a/libs/platform/RK3566_3568/zlib/lib/Linux/libz.a b/libs/platform/RK3566_3568/zlib/lib/Linux/libz.a new file mode 100644 index 0000000..9a085c5 Binary files /dev/null and b/libs/platform/RK3566_3568/zlib/lib/Linux/libz.a differ diff --git a/libs/platform/RK3566_3568/zlib/lib/Linux/libz.so b/libs/platform/RK3566_3568/zlib/lib/Linux/libz.so new file mode 100644 index 0000000..ef1d87c Binary files /dev/null and b/libs/platform/RK3566_3568/zlib/lib/Linux/libz.so differ diff --git a/libs/platform/RK3588/.gitignore b/libs/platform/RK3588/.gitignore new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/libs/platform/RK3588/.gitignore @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/platform/RK3588/libjpeg/include/jconfig.h b/libs/platform/RK3588/libjpeg/include/jconfig.h new file mode 100644 index 0000000..cb09382 --- /dev/null +++ b/libs/platform/RK3588/libjpeg/include/jconfig.h @@ -0,0 +1,163 @@ +/* + * jconfig.txt + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * Modified 2009-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + * DO NOT EDIT THIS FILE --- IT WON'T ACCOMPLISH ANYTHING. + * EDIT A COPY NAMED JCONFIG.H. + */ + + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.txt) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +/* #undef CHAR_IS_UNSIGNED */ + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +/* #undef NEED_BSD_STRINGS */ + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#define NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not enum, on Windows systems. + */ +#ifdef _WIN32 +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* #undef USE_SETMODE */ + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/libs/platform/RK3588/libjpeg/include/jerror.h b/libs/platform/RK3588/libjpeg/include/jerror.h new file mode 100644 index 0000000..a4b661f --- /dev/null +++ b/libs/platform/RK3588/libjpeg/include/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2012 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_BEFORE, "Invalid JPEG file structure: %s before SOF") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/libs/platform/RK3588/libjpeg/include/jmorecfg.h b/libs/platform/RK3588/libjpeg/include/jmorecfg.h new file mode 100644 index 0000000..679d68b --- /dev/null +++ b/libs/platform/RK3588/libjpeg/include/jmorecfg.h @@ -0,0 +1,446 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 9 for 9-bit sample values + * 10 for 10-bit sample values + * 11 for 11-bit sample values + * 12 for 12-bit sample values + * Only 8, 9, 10, 11, and 12 bits sample data precision are supported for + * full-feature DCT processing. Further depths up to 16-bit may be added + * later for the lossless modes of operation. + * Run-time selection and conversion of data precision will be added later + * and are currently not supported, sorry. + * Exception: The transcoding part (jpegtran) supports all settings in a + * single instance, since it operates on the level of DCT coefficients and + * not sample values. The DCT coefficients are of the same type (16 bits) + * in all cases (see below). + */ + +#define BITS_IN_JSAMPLE 8 /* use 8, 9, 10, 11, or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 9 +/* JSAMPLE should be the smallest type that will hold the values 0..511. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 511 +#define CENTERJSAMPLE 256 + +#endif /* BITS_IN_JSAMPLE == 9 */ + + +#if BITS_IN_JSAMPLE == 10 +/* JSAMPLE should be the smallest type that will hold the values 0..1023. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 1023 +#define CENTERJSAMPLE 512 + +#endif /* BITS_IN_JSAMPLE == 10 */ + + +#if BITS_IN_JSAMPLE == 11 +/* JSAMPLE should be the smallest type that will hold the values 0..2047. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 2047 +#define CENTERJSAMPLE 1024 + +#endif /* BITS_IN_JSAMPLE == 11 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* The noreturn type identifier is used to declare functions + * which cannot return. + * Compilers can thus create more optimized code and perform + * better checks for warnings and errors. + * Static analyzer tools can make improved inferences about + * execution paths and are prevented from giving false alerts. + * + * Unfortunately, the proposed specifications of corresponding + * extensions in the Dec 2011 ISO C standard revision (C11), + * GCC, MSVC, etc. are not viable. + * Thus we introduce a user defined type to declare noreturn + * functions at least for clarity. A proper compiler would + * have a suitable noreturn type to match in place of void. + */ + +#ifndef HAVE_NORETURN_T +typedef void noreturn_t; +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +#if defined FALSE || defined TRUE || defined QGLOBAL_H +/* Qt3 defines FALSE and TRUE as "const" variables in qglobal.h */ +typedef int boolean; +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif +#else +typedef enum { FALSE = 0, TRUE = 1 } boolean; +#endif +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected more than 8-bit data precision, it is dangerous to + * turn off ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only + * good for 8-bit precision, so arithmetic coding is recommended for higher + * precision. The Huffman encoder normally uses entropy optimization to + * compute usable tables for higher precision. Otherwise, you'll have to + * supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? (Requires DCT_ISLOW)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/libs/platform/RK3588/libjpeg/include/jpeglib.h b/libs/platform/RK3588/libjpeg/include/jpeglib.h new file mode 100644 index 0000000..f4fbf23 --- /dev/null +++ b/libs/platform/RK3588/libjpeg/include/jpeglib.h @@ -0,0 +1,1180 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2013 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version IDs for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 90". + */ + +#define JPEG_LIB_VERSION 90 /* Compatibility version 9.0 */ +#define JPEG_LIB_VERSION_MAJOR 9 +#define JPEG_LIB_VERSION_MINOR 1 + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, + * so don't change them if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 coefficients */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = + * ceil(image_width * Hi/Hmax * DCT_h_scaled_size/block_size) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* For decompression, in cases where some of the components will be + * ignored (eg grayscale output from YCbCr image), we can skip most + * computations for the unused components. + * For compression, some of the components will need further quantization + * scale by factor of 2 after DCT (eg BG_YCC output from normal RGB input). + * The field is first set TRUE for decompression, FALSE for compression + * in initial_setup, and then adapted in color conversion setup. + */ + boolean component_needed; + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue, standard RGB (sRGB) */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV), standard YCC */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK, /* Y/Cb/Cr/K */ + JCS_BG_RGB, /* big gamut red/green/blue, bg-sRGB */ + JCS_BG_YCC /* big gamut Y/Cb/Cr, bg-sYCC */ +} J_COLOR_SPACE; + +/* Supported color transforms. */ + +typedef enum { + JCT_NONE = 0, + JCT_SUBTRACT_GREEN = 1 +} J_COLOR_TRANSFORM; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + J_COLOR_TRANSFORM color_transform; + /* Color transform identifier, writes LSE marker if nonzero */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + J_COLOR_TRANSFORM color_transform; + /* Color transform identifier derived from LSE marker, otherwise zero */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(noreturn_t, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/libs/platform/RK3588/libjpeg/lib/Android/libjpeg.a b/libs/platform/RK3588/libjpeg/lib/Android/libjpeg.a new file mode 100644 index 0000000..5394bc1 Binary files /dev/null and b/libs/platform/RK3588/libjpeg/lib/Android/libjpeg.a differ diff --git a/libs/platform/RK3588/libjpeg/lib/Android/libjpeg.so b/libs/platform/RK3588/libjpeg/lib/Android/libjpeg.so new file mode 100644 index 0000000..aeda23e Binary files /dev/null and b/libs/platform/RK3588/libjpeg/lib/Android/libjpeg.so differ diff --git a/libs/platform/RK3588/libjpeg/lib/Linux/libjpeg.a b/libs/platform/RK3588/libjpeg/lib/Linux/libjpeg.a new file mode 100644 index 0000000..03d6c00 Binary files /dev/null and b/libs/platform/RK3588/libjpeg/lib/Linux/libjpeg.a differ diff --git a/libs/platform/RK3588/libjpeg/lib/Linux/libjpeg.so b/libs/platform/RK3588/libjpeg/lib/Linux/libjpeg.so new file mode 100644 index 0000000..ffc5282 Binary files /dev/null and b/libs/platform/RK3588/libjpeg/lib/Linux/libjpeg.so differ diff --git a/libs/platform/RK3588/libpng/include/libpng16/png.h b/libs/platform/RK3588/libpng/include/libpng16/png.h new file mode 100644 index 0000000..5fb494f --- /dev/null +++ b/libs/platform/RK3588/libpng/include/libpng16/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.38 - September 14, 2022 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2022 The PNG Reference Library Authors. + * * Copyright (c) 2018-2022 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.38 16 10638 16.so.16.38[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.38" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 38 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_38; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explanation of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer calculations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK3588/libpng/include/libpng16/pngconf.h b/libs/platform/RK3588/libpng/include/libpng16/pngconf.h new file mode 100644 index 0000000..89d28f8 --- /dev/null +++ b/libs/platform/RK3588/libpng/include/libpng16/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.38 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK3588/libpng/include/libpng16/pnglibconf.h b/libs/platform/RK3588/libpng/include/libpng16/pnglibconf.h new file mode 100644 index 0000000..7a0e1fa --- /dev/null +++ b/libs/platform/RK3588/libpng/include/libpng16/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.38 */ + +/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK3588/libpng/include/png.h b/libs/platform/RK3588/libpng/include/png.h new file mode 100644 index 0000000..5fb494f --- /dev/null +++ b/libs/platform/RK3588/libpng/include/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.38 - September 14, 2022 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.38, September 2022: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * PNG Reference Library License version 2 + * --------------------------------------- + * + * * Copyright (c) 1995-2022 The PNG Reference Library Authors. + * * Copyright (c) 2018-2022 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.38 16 10638 16.so.16.38[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.38" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.38 - September 14, 2022\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 38 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10638 /* 1.6.38 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_38; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explanation of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer calculations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RK3588/libpng/include/pngconf.h b/libs/platform/RK3588/libpng/include/pngconf.h new file mode 100644 index 0000000..89d28f8 --- /dev/null +++ b/libs/platform/RK3588/libpng/include/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.38 + * + * Copyright (c) 2018-2022 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || \ + defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RK3588/libpng/include/pnglibconf.h b/libs/platform/RK3588/libpng/include/pnglibconf.h new file mode 100644 index 0000000..7a0e1fa --- /dev/null +++ b/libs/platform/RK3588/libpng/include/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.38 */ + +/* Copyright (c) 2018-2022 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RK3588/libpng/lib/Android/libpng16.a b/libs/platform/RK3588/libpng/lib/Android/libpng16.a new file mode 100644 index 0000000..90cb292 Binary files /dev/null and b/libs/platform/RK3588/libpng/lib/Android/libpng16.a differ diff --git a/libs/platform/RK3588/libpng/lib/Android/libpng16.so b/libs/platform/RK3588/libpng/lib/Android/libpng16.so new file mode 100644 index 0000000..1792a09 Binary files /dev/null and b/libs/platform/RK3588/libpng/lib/Android/libpng16.so differ diff --git a/libs/platform/RK3588/libpng/lib/Linux/libpng16.a b/libs/platform/RK3588/libpng/lib/Linux/libpng16.a new file mode 100644 index 0000000..61b56ba Binary files /dev/null and b/libs/platform/RK3588/libpng/lib/Linux/libpng16.a differ diff --git a/libs/platform/RK3588/libpng/lib/Linux/libpng16.so b/libs/platform/RK3588/libpng/lib/Linux/libpng16.so new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3588/libpng/lib/Linux/libpng16.so differ diff --git a/libs/platform/RK3588/libpng/lib/Linux/libpng16.so.16 b/libs/platform/RK3588/libpng/lib/Linux/libpng16.so.16 new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3588/libpng/lib/Linux/libpng16.so.16 differ diff --git a/libs/platform/RK3588/libpng/lib/Linux/libpng16.so.16.38.0 b/libs/platform/RK3588/libpng/lib/Linux/libpng16.so.16.38.0 new file mode 100644 index 0000000..4ad0176 Binary files /dev/null and b/libs/platform/RK3588/libpng/lib/Linux/libpng16.so.16.38.0 differ diff --git a/libs/platform/RK3588/zlib/include/zconf.h b/libs/platform/RK3588/zlib/include/zconf.h new file mode 100644 index 0000000..5e1d68a --- /dev/null +++ b/libs/platform/RK3588/zlib/include/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/platform/RK3588/zlib/include/zlib.h b/libs/platform/RK3588/zlib/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/libs/platform/RK3588/zlib/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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 notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#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 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#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) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/platform/RK3588/zlib/lib/Android/libz.a b/libs/platform/RK3588/zlib/lib/Android/libz.a new file mode 100644 index 0000000..81f55a6 Binary files /dev/null and b/libs/platform/RK3588/zlib/lib/Android/libz.a differ diff --git a/libs/platform/RK3588/zlib/lib/Android/libz.so b/libs/platform/RK3588/zlib/lib/Android/libz.so new file mode 100644 index 0000000..577172c Binary files /dev/null and b/libs/platform/RK3588/zlib/lib/Android/libz.so differ diff --git a/libs/platform/RK3588/zlib/lib/Linux/libz.a b/libs/platform/RK3588/zlib/lib/Linux/libz.a new file mode 100644 index 0000000..9a085c5 Binary files /dev/null and b/libs/platform/RK3588/zlib/lib/Linux/libz.a differ diff --git a/libs/platform/RK3588/zlib/lib/Linux/libz.so b/libs/platform/RK3588/zlib/lib/Linux/libz.so new file mode 100644 index 0000000..ef1d87c Binary files /dev/null and b/libs/platform/RK3588/zlib/lib/Linux/libz.so differ diff --git a/libs/platform/RV1106_1103/zlib/include/zconf.h b/libs/platform/RV1106_1103/zlib/include/zconf.h new file mode 100644 index 0000000..995ad4f --- /dev/null +++ b/libs/platform/RV1106_1103/zlib/include/zconf.h @@ -0,0 +1,536 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H +/* #undef Z_PREFIX */ +#define Z_HAVE_UNISTD_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/platform/RV1106_1103/zlib/include/zlib.h b/libs/platform/RV1106_1103/zlib/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/libs/platform/RV1106_1103/zlib/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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 notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#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 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#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) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/platform/RV1106_1103/zlib/lib/libz.a b/libs/platform/RV1106_1103/zlib/lib/libz.a new file mode 100644 index 0000000..d8bd0a5 Binary files /dev/null and b/libs/platform/RV1106_1103/zlib/lib/libz.a differ diff --git a/libs/platform/RV1106_1103/zlib/lib/libz.so b/libs/platform/RV1106_1103/zlib/lib/libz.so new file mode 100644 index 0000000..621149b Binary files /dev/null and b/libs/platform/RV1106_1103/zlib/lib/libz.so differ diff --git a/libs/platform/RV1106_1103/zlib/lib/libz.so.1 b/libs/platform/RV1106_1103/zlib/lib/libz.so.1 new file mode 100644 index 0000000..621149b Binary files /dev/null and b/libs/platform/RV1106_1103/zlib/lib/libz.so.1 differ diff --git a/libs/platform/RV1106_1103/zlib/lib/libz.so.1.2.11 b/libs/platform/RV1106_1103/zlib/lib/libz.so.1.2.11 new file mode 100644 index 0000000..621149b Binary files /dev/null and b/libs/platform/RV1106_1103/zlib/lib/libz.so.1.2.11 differ diff --git a/libs/platform/RV1109_1126/libjpeg/include/jconfig.h b/libs/platform/RV1109_1126/libjpeg/include/jconfig.h new file mode 100644 index 0000000..966b1d5 --- /dev/null +++ b/libs/platform/RV1109_1126/libjpeg/include/jconfig.h @@ -0,0 +1,54 @@ +/* jconfig.h. Generated from jconfig.cfg by configure. */ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.txt for explanations */ + +#define HAVE_PROTOTYPES 1 +#define HAVE_UNSIGNED_CHAR 1 +#define HAVE_UNSIGNED_SHORT 1 +/* #undef void */ +/* #undef const */ +/* #undef CHAR_IS_UNSIGNED */ +#define HAVE_STDDEF_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_LOCALE_H 1 +/* #undef NEED_BSD_STRINGS */ +/* #undef NEED_SYS_TYPES_H */ +/* #undef NEED_FAR_POINTERS */ +/* #undef NEED_SHORT_EXTERNAL_NAMES */ +/* Define this if you get warnings about undefined structures. */ +/* #undef INCOMPLETE_TYPES_BROKEN */ + +/* Define "boolean" as unsigned char, not int, on Windows systems. */ +#ifdef _WIN32 +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +#endif + +#ifdef JPEG_INTERNALS + +/* #undef RIGHT_SHIFT_IS_UNSIGNED */ +#define INLINE __inline__ +/* These are for configuring the JPEG memory manager. */ +/* #undef DEFAULT_MAX_MEM */ +/* #undef NO_MKTEMP */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +/* #undef RLE_SUPPORTED */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* #undef TWO_FILE_COMMANDLINE */ +/* #undef NEED_SIGNAL_CATCHER */ +/* #undef DONT_USE_B_MODE */ + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +/* #undef PROGRESS_REPORT */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/libs/platform/RV1109_1126/libjpeg/include/jerror.h b/libs/platform/RV1109_1126/libjpeg/include/jerror.h new file mode 100644 index 0000000..1cfb2b1 --- /dev/null +++ b/libs/platform/RV1109_1126/libjpeg/include/jerror.h @@ -0,0 +1,304 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_CROP_SPEC, "Invalid crop request") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "DCT scaled block size %dx%d not supported") +JMESSAGE(JERR_BAD_DROP_SAMPLING, + "Component index %d: mismatching sampling ratio %d:%d, %d:%d, %c") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_ARITH_TABLE, "Arithmetic table 0x%02x was not defined") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_ARITH_BAD_CODE, "Corrupt JPEG data: bad arithmetic code") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT6(cinfo,code,p1,p2,p3,p4,p5,p6) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (cinfo)->err->msg_parm.i[4] = (p5), \ + (cinfo)->err->msg_parm.i[5] = (p6), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/libs/platform/RV1109_1126/libjpeg/include/jmorecfg.h b/libs/platform/RV1109_1126/libjpeg/include/jmorecfg.h new file mode 100644 index 0000000..6c085c3 --- /dev/null +++ b/libs/platform/RV1109_1126/libjpeg/include/jmorecfg.h @@ -0,0 +1,369 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2011 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ +typedef long INT32; +#endif +#endif +#endif +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifndef FAR +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#define C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define DCT_SCALING_SUPPORTED /* Input rescaling via DCT? (Requires DCT_ISLOW)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#define D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/libs/platform/RV1109_1126/libjpeg/include/jpeglib.h b/libs/platform/RV1109_1126/libjpeg/include/jpeglib.h new file mode 100644 index 0000000..1327cff --- /dev/null +++ b/libs/platform/RV1109_1126/libjpeg/include/jpeglib.h @@ -0,0 +1,1160 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * Modified 2002-2011 by Guido Vollbeding. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +extern "C" { +#endif +#endif + +/* Version IDs for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 80". + */ + +#define JPEG_LIB_VERSION 80 /* Compatibility version 8.0 */ +#define JPEG_LIB_VERSION_MAJOR 8 +#define JPEG_LIB_VERSION_MINOR 4 + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 coefficients */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples, + * reflecting any scaling we choose to apply during the DCT step. + * Values from 1 to 16 are supported. + * Note that different components may receive different DCT scalings. + */ + int DCT_h_scaled_size; + int DCT_v_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface); + * DCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_h_scaled_size/DCTSIZE) + * and similarly for height. + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples: MCU_width * DCT_h_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + JDIMENSION jpeg_width; /* scaled JPEG image width */ + JDIMENSION jpeg_height; /* scaled JPEG image height */ + /* Dimensions of actual JPEG image that will be written to file, + * derived from input dimensions by scaling factors above. + * These fields are computed by jpeg_start_compress(). + * You can also use jpeg_calc_jpeg_dimensions() to determine these values + * in advance of calling jpeg_start_compress(). + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + int q_scale_factor[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined, + * and corresponding scale factors (percentage, initialized 100). + */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean is_baseline; /* TRUE if Baseline SOF0 encountered */ + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */ + int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_v_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* These fields are derived from Se of first SOS marker. + */ + int block_size; /* the basic DCT block size: 1..16 */ + const int * natural_order; /* natural-order position array for entropy decode */ + int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_mem_dest jMemDest +#define jpeg_mem_src jMemSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_default_qtables jDefQTables +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_calc_jpeg_dimensions jCjpegDimensions +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_core_output_dimensions jCoreDimensions +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* Data source and destination managers: memory buffers. */ +EXTERN(void) jpeg_mem_dest JPP((j_compress_ptr cinfo, + unsigned char ** outbuffer, + unsigned long * outsize)); +EXTERN(void) jpeg_mem_src JPP((j_decompress_ptr cinfo, + unsigned char * inbuffer, + unsigned long insize)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_default_qtables JPP((j_compress_ptr cinfo, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Precalculate JPEG dimensions for current compression parameters. */ +EXTERN(void) jpeg_calc_jpeg_dimensions JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.txt concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_core_output_dimensions JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +#ifndef DONT_USE_EXTERN_C +} +#endif +#endif + +#endif /* JPEGLIB_H */ diff --git a/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.a b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.a new file mode 100644 index 0000000..b49d26b Binary files /dev/null and b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.a differ diff --git a/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.la b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.la new file mode 100644 index 0000000..27b9cc9 --- /dev/null +++ b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.la @@ -0,0 +1,41 @@ +# libjpeg.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.2 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libjpeg.so.8' + +# Names of this library. +library_names='libjpeg.so.8.4.0 libjpeg.so.8 libjpeg.so' + +# The name of the static archive. +old_library='libjpeg.a' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs='' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libjpeg. +current=12 +age=4 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/home/xz/Documents/testing/compile_test/jpegsrc.v8d1/jpeg-8d1/_install/lib' diff --git a/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so new file mode 100644 index 0000000..fe8d5c8 Binary files /dev/null and b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so differ diff --git a/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so.8 b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so.8 new file mode 100644 index 0000000..fe8d5c8 Binary files /dev/null and b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so.8 differ diff --git a/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so.8.4.0 b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so.8.4.0 new file mode 100644 index 0000000..fe8d5c8 Binary files /dev/null and b/libs/platform/RV1109_1126/libjpeg/lib/Linux/libjpeg.so.8.4.0 differ diff --git a/libs/platform/RV1109_1126/libpng/include/libpng16/png.h b/libs/platform/RV1109_1126/libpng/include/libpng16/png.h new file mode 100644 index 0000000..139eb0d --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/include/libpng16/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.37 - April 14, 2019 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.37, April 2019: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.37 16 10637 16.so.16.37[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.37" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 37 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10637 /* 1.6.37 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_37; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RV1109_1126/libpng/include/libpng16/pngconf.h b/libs/platform/RV1109_1126/libpng/include/libpng16/pngconf.h new file mode 100644 index 0000000..927a769 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/include/libpng16/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.37 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RV1109_1126/libpng/include/libpng16/pnglibconf.h b/libs/platform/RV1109_1126/libpng/include/libpng16/pnglibconf.h new file mode 100644 index 0000000..7f6a817 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/include/libpng16/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.37 */ + +/* Copyright (c) 2018-2019 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RV1109_1126/libpng/include/png.h b/libs/platform/RV1109_1126/libpng/include/png.h new file mode 100644 index 0000000..139eb0d --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/include/png.h @@ -0,0 +1,3247 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.6.37 - April 14, 2019 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. (See LICENSE, below.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.6.35, July 2018: + * Glenn Randers-Pehrson + * libpng versions 1.6.36, December 2018, through 1.6.37, April 2019: + * Cosmin Truta + * See also "Contributing Authors", below. + */ + +/* + * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE + * ========================================= + * + * 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. + * + * + * PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) + * ----------------------------------------------------------------------- + * + * libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are + * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are + * derived from libpng-1.0.6, and are distributed according to the same + * disclaimer and license as libpng-1.0.6 with the following individuals + * added to the list of Contributing Authors: + * + * Simon-Pierre Cadieux + * Eric S. Raymond + * Mans Rullgard + * Cosmin Truta + * Gilles Vollant + * James Yu + * Mandar Sahastrabuddhe + * Google Inc. + * Vadim Barkov + * + * and with the following additions to the disclaimer: + * + * There is no warranty against interference with your enjoyment of + * the library or against infringement. There is no warranty that our + * efforts or the library will fulfill any of your particular purposes + * or needs. This library is provided with all faults, and the entire + * risk of satisfactory quality, performance, accuracy, and effort is + * with the user. + * + * Some files in the "contrib" directory and some configure-generated + * files that are distributed with libpng have other copyright owners, and + * are released under other open source licenses. + * + * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are + * Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from + * libpng-0.96, and are distributed according to the same disclaimer and + * license as libpng-0.96, with the following individuals added to the + * list of Contributing Authors: + * + * Tom Lane + * Glenn Randers-Pehrson + * Willem van Schaik + * + * libpng versions 0.89, June 1996, through 0.96, May 1997, are + * Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, + * and are distributed according to the same disclaimer and license as + * libpng-0.88, with the following individuals added to the list of + * Contributing Authors: + * + * John Bowler + * Kevin Bracey + * Sam Bushell + * Magnus Holmgren + * Greg Roelofs + * Tom Tanner + * + * Some files in the "scripts" directory have other copyright owners, + * but are released under this license. + * + * libpng versions 0.5, May 1995, through 0.88, January 1996, are + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * For the purposes of this copyright and license, "Contributing Authors" + * is defined as the following set of individuals: + * + * Andreas Dilger + * Dave Martindale + * Guy Eric Schalnat + * Paul Schmidt + * Tim Wegner + * + * The PNG Reference Library is supplied "AS IS". The Contributing + * Authors and Group 42, Inc. disclaim all warranties, expressed or + * implied, including, without limitation, the warranties of + * merchantability and of fitness for any purpose. The Contributing + * Authors and Group 42, Inc. assume no liability for direct, indirect, + * incidental, special, exemplary, or consequential damages, which may + * result from the use of the PNG Reference Library, even if advised of + * the possibility of such damage. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * source code, or portions hereof, for any purpose, without fee, subject + * to the following restrictions: + * + * 1. The origin of this source code must not be misrepresented. + * + * 2. Altered versions must be plainly marked as such and must not + * be misrepresented as being the original source. + * + * 3. This Copyright notice may not be removed or altered from any + * source or altered source distribution. + * + * The Contributing Authors and Group 42, Inc. specifically permit, + * without fee, and encourage the use of this source code as a component + * to supporting the PNG file format in commercial products. If you use + * this source code in a product, acknowledgment is not required but would + * be appreciated. + * + * END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE. + * + * TRADEMARK + * ========= + * + * The name "libpng" has not been registered by the Copyright owners + * as a trademark in any jurisdiction. However, because libpng has + * been distributed and maintained world-wide, continually since 1995, + * the Copyright owners claim "common-law trademark protection" in any + * jurisdiction where common-law trademark is recognized. + */ + +/* + * A "png_get_copyright" function is available, for convenient use in "about" + * boxes and the like: + * + * printf("%s", png_get_copyright(NULL)); + * + * Also, the PNG logo (in PNG format, of course) is supplied in the + * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + */ + +/* + * The contributing authors would like to thank all those who helped + * with testing, bug fixes, and patience. This wouldn't have been + * possible without all of you. + * + * Thanks to Frank J. T. Wojcik for helping with the documentation. + */ + +/* Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * ... + * 1.0.69 10 10069 10.so.0.69[.0] + * ... + * 1.2.59 13 10259 12.so.0.59[.0] + * ... + * 1.4.20 14 10420 14.so.0.20[.0] + * ... + * 1.5.30 15 10530 15.so.15.30[.0] + * ... + * 1.6.37 16 10637 16.so.16.37[.0] + * + * Henceforth the source version will match the shared-library major and + * minor numbers; the shared-library major version number will be used for + * changes in backward compatibility, as it is intended. + * The PNG_LIBPNG_VER macro, which is not used within libpng but is + * available for applications, is an unsigned integer of the form XYYZZ + * corresponding to the source version X.Y.Z (leading zeros in Y and Z). + * Beta versions were given the previous public release number plus a + * letter, until version 1.0.6j; from then on they were given the upcoming + * public release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO/IEC Standard; see + * + */ + +#ifndef PNG_H +#define PNG_H + +/* This is not the place to learn how to use libpng. The file libpng-manual.txt + * describes how to use libpng, and the file example.c summarizes it + * with some code on which to build. This file is useful for looking + * at the actual function definitions and structure components. If that + * file has been stripped from your copy of libpng, you can find it at + * + * + * If you just need to read a PNG file and don't want to read the documentation + * skip to the end of this file and read the section entitled 'simplified API'. + */ + +/* Version information for png.h - this should match the version in png.c */ +#define PNG_LIBPNG_VER_STRING "1.6.37" +#define PNG_HEADER_VERSION_STRING " libpng version 1.6.37 - April 14, 2019\n" + +#define PNG_LIBPNG_VER_SONUM 16 +#define PNG_LIBPNG_VER_DLLNUM 16 + +/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ +#define PNG_LIBPNG_VER_MAJOR 1 +#define PNG_LIBPNG_VER_MINOR 6 +#define PNG_LIBPNG_VER_RELEASE 37 + +/* This should be zero for a public release, or non-zero for a + * development version. [Deprecated] + */ +#define PNG_LIBPNG_VER_BUILD 0 + +/* Release Status */ +#define PNG_LIBPNG_BUILD_ALPHA 1 +#define PNG_LIBPNG_BUILD_BETA 2 +#define PNG_LIBPNG_BUILD_RC 3 +#define PNG_LIBPNG_BUILD_STABLE 4 +#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7 + +/* Release-Specific Flags */ +#define PNG_LIBPNG_BUILD_PATCH 8 /* Can be OR'ed with + PNG_LIBPNG_BUILD_STABLE only */ +#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_SPECIAL */ +#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with + PNG_LIBPNG_BUILD_PRIVATE */ + +#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE + +/* Careful here. At one time, Guy wanted to use 082, but that + * would be octal. We must not include leading zeros. + * Versions 0.7 through 1.0.0 were in the range 0 to 100 here + * (only version 1.0.0 was mis-numbered 100 instead of 10000). + * From version 1.0.1 it is: + * XXYYZZ, where XX=major, YY=minor, ZZ=release + */ +#define PNG_LIBPNG_VER 10637 /* 1.6.37 */ + +/* Library configuration: these options cannot be changed after + * the library has been built. + */ +#ifndef PNGLCONF_H +/* If pnglibconf.h is missing, you can + * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h + */ +# include "pnglibconf.h" +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* Machine specific configuration. */ +# include "pngconf.h" +#endif + +/* + * Added at libpng-1.2.8 + * + * Ref MSDN: Private as priority over Special + * VS_FF_PRIVATEBUILD File *was not* built using standard release + * procedures. If this value is given, the StringFileInfo block must + * contain a PrivateBuild string. + * + * VS_FF_SPECIALBUILD File *was* built by the original company using + * standard release procedures but is a variation of the standard + * file of the same version number. If this value is given, the + * StringFileInfo block must contain a SpecialBuild string. + */ + +#ifdef PNG_USER_PRIVATEBUILD /* From pnglibconf.h */ +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE) +#else +# ifdef PNG_LIBPNG_SPECIALBUILD +# define PNG_LIBPNG_BUILD_TYPE \ + (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL) +# else +# define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE) +# endif +#endif + +#ifndef PNG_VERSION_INFO_ONLY + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Version information for C files, stored in png.c. This had better match + * the version above. + */ +#define png_libpng_ver png_get_header_ver(NULL) + +/* This file is arranged in several sections: + * + * 1. [omitted] + * 2. Any configuration options that can be specified by for the application + * code when it is built. (Build time configuration is in pnglibconf.h) + * 3. Type definitions (base types are defined in pngconf.h), structure + * definitions. + * 4. Exported library functions. + * 5. Simplified API. + * 6. Implementation options. + * + * The library source code has additional files (principally pngpriv.h) that + * allow configuration of the library. + */ + +/* Section 1: [omitted] */ + +/* Section 2: run time configuration + * See pnglibconf.h for build time configuration + * + * Run time configuration allows the application to choose between + * implementations of certain arithmetic APIs. The default is set + * at build time and recorded in pnglibconf.h, but it is safe to + * override these (and only these) settings. Note that this won't + * change what the library does, only application code, and the + * settings can (and probably should) be made on a per-file basis + * by setting the #defines before including png.h + * + * Use macros to read integers from PNG data or use the exported + * functions? + * PNG_USE_READ_MACROS: use the macros (see below) Note that + * the macros evaluate their argument multiple times. + * PNG_NO_USE_READ_MACROS: call the relevant library function. + * + * Use the alternative algorithm for compositing alpha samples that + * does not use division? + * PNG_READ_COMPOSITE_NODIV_SUPPORTED: use the 'no division' + * algorithm. + * PNG_NO_READ_COMPOSITE_NODIV: use the 'division' algorithm. + * + * How to handle benign errors if PNG_ALLOW_BENIGN_ERRORS is + * false? + * PNG_ALLOW_BENIGN_ERRORS: map calls to the benign error + * APIs to png_warning. + * Otherwise the calls are mapped to png_error. + */ + +/* Section 3: type definitions, including structures and compile time + * constants. + * See pngconf.h for base types that vary by machine/system + */ + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef char* png_libpng_version_1_6_37; + +/* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. + * + * png_struct is the cache of information used while reading or writing a single + * PNG file. One of these is always required, although the simplified API + * (below) hides the creation and destruction of it. + */ +typedef struct png_struct_def png_struct; +typedef const png_struct * png_const_structp; +typedef png_struct * png_structp; +typedef png_struct * * png_structpp; + +/* png_info contains information read from or to be written to a PNG file. One + * or more of these must exist while reading or creating a PNG file. The + * information is not used by libpng during read but is used to control what + * gets written when a PNG file is created. "png_get_" function calls read + * information during read and "png_set_" functions calls write information + * when creating a PNG. + * been moved into a separate header file that is not accessible to + * applications. Read libpng-manual.txt or libpng.3 for more info. + */ +typedef struct png_info_def png_info; +typedef png_info * png_infop; +typedef const png_info * png_const_infop; +typedef png_info * * png_infopp; + +/* Types with names ending 'p' are pointer types. The corresponding types with + * names ending 'rp' are identical pointer types except that the pointer is + * marked 'restrict', which means that it is the only pointer to the object + * passed to the function. Applications should not use the 'restrict' types; + * it is always valid to pass 'p' to a pointer with a function argument of the + * corresponding 'rp' type. Different compilers have different rules with + * regard to type matching in the presence of 'restrict'. For backward + * compatibility libpng callbacks never have 'restrict' in their parameters and, + * consequentially, writing portable application code is extremely difficult if + * an attempt is made to use 'restrict'. + */ +typedef png_struct * PNG_RESTRICT png_structrp; +typedef const png_struct * PNG_RESTRICT png_const_structrp; +typedef png_info * PNG_RESTRICT png_inforp; +typedef const png_info * PNG_RESTRICT png_const_inforp; + +/* Three color definitions. The order of the red, green, and blue, (and the + * exact size) is not important, although the size of the fields need to + * be png_byte or png_uint_16 (as defined below). + */ +typedef struct png_color_struct +{ + png_byte red; + png_byte green; + png_byte blue; +} png_color; +typedef png_color * png_colorp; +typedef const png_color * png_const_colorp; +typedef png_color * * png_colorpp; + +typedef struct png_color_16_struct +{ + png_byte index; /* used for palette files */ + png_uint_16 red; /* for use in red green blue files */ + png_uint_16 green; + png_uint_16 blue; + png_uint_16 gray; /* for use in grayscale files */ +} png_color_16; +typedef png_color_16 * png_color_16p; +typedef const png_color_16 * png_const_color_16p; +typedef png_color_16 * * png_color_16pp; + +typedef struct png_color_8_struct +{ + png_byte red; /* for use in red green blue files */ + png_byte green; + png_byte blue; + png_byte gray; /* for use in grayscale files */ + png_byte alpha; /* for alpha channel files */ +} png_color_8; +typedef png_color_8 * png_color_8p; +typedef const png_color_8 * png_const_color_8p; +typedef png_color_8 * * png_color_8pp; + +/* + * The following two structures are used for the in-core representation + * of sPLT chunks. + */ +typedef struct png_sPLT_entry_struct +{ + png_uint_16 red; + png_uint_16 green; + png_uint_16 blue; + png_uint_16 alpha; + png_uint_16 frequency; +} png_sPLT_entry; +typedef png_sPLT_entry * png_sPLT_entryp; +typedef const png_sPLT_entry * png_const_sPLT_entryp; +typedef png_sPLT_entry * * png_sPLT_entrypp; + +/* When the depth of the sPLT palette is 8 bits, the color and alpha samples + * occupy the LSB of their respective members, and the MSB of each member + * is zero-filled. The frequency member always occupies the full 16 bits. + */ + +typedef struct png_sPLT_struct +{ + png_charp name; /* palette name */ + png_byte depth; /* depth of palette samples */ + png_sPLT_entryp entries; /* palette entries */ + png_int_32 nentries; /* number of palette entries */ +} png_sPLT_t; +typedef png_sPLT_t * png_sPLT_tp; +typedef const png_sPLT_t * png_const_sPLT_tp; +typedef png_sPLT_t * * png_sPLT_tpp; + +#ifdef PNG_TEXT_SUPPORTED +/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file, + * and whether that contents is compressed or not. The "key" field + * points to a regular zero-terminated C string. The "text" fields can be a + * regular C string, an empty string, or a NULL pointer. + * However, the structure returned by png_get_text() will always contain + * the "text" field as a regular zero-terminated C string (possibly + * empty), never a NULL pointer, so it can be safely used in printf() and + * other string-handling functions. Note that the "itxt_length", "lang", and + * "lang_key" members of the structure only exist when the library is built + * with iTXt chunk support. Prior to libpng-1.4.0 the library was built by + * default without iTXt support. Also note that when iTXt *is* supported, + * the "lang" and "lang_key" fields contain NULL pointers when the + * "compression" field contains * PNG_TEXT_COMPRESSION_NONE or + * PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the + * same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag" + * which is always 0 or 1, or its "compression method" which is always 0. + */ +typedef struct png_text_struct +{ + int compression; /* compression value: + -1: tEXt, none + 0: zTXt, deflate + 1: iTXt, none + 2: iTXt, deflate */ + png_charp key; /* keyword, 1-79 character description of "text" */ + png_charp text; /* comment, may be an empty string (ie "") + or a NULL pointer */ + size_t text_length; /* length of the text string */ + size_t itxt_length; /* length of the itxt string */ + png_charp lang; /* language code, 0-79 characters + or a NULL pointer */ + png_charp lang_key; /* keyword translated UTF-8 string, 0 or more + chars or a NULL pointer */ +} png_text; +typedef png_text * png_textp; +typedef const png_text * png_const_textp; +typedef png_text * * png_textpp; +#endif + +/* Supported compression types for text in PNG files (tEXt, and zTXt). + * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */ +#define PNG_TEXT_COMPRESSION_NONE_WR -3 +#define PNG_TEXT_COMPRESSION_zTXt_WR -2 +#define PNG_TEXT_COMPRESSION_NONE -1 +#define PNG_TEXT_COMPRESSION_zTXt 0 +#define PNG_ITXT_COMPRESSION_NONE 1 +#define PNG_ITXT_COMPRESSION_zTXt 2 +#define PNG_TEXT_COMPRESSION_LAST 3 /* Not a valid value */ + +/* png_time is a way to hold the time in an machine independent way. + * Two conversions are provided, both from time_t and struct tm. There + * is no portable way to convert to either of these structures, as far + * as I know. If you know of a portable way, send it to me. As a side + * note - PNG has always been Year 2000 compliant! + */ +typedef struct png_time_struct +{ + png_uint_16 year; /* full year, as in, 1995 */ + png_byte month; /* month of year, 1 - 12 */ + png_byte day; /* day of month, 1 - 31 */ + png_byte hour; /* hour of day, 0 - 23 */ + png_byte minute; /* minute of hour, 0 - 59 */ + png_byte second; /* second of minute, 0 - 60 (for leap seconds) */ +} png_time; +typedef png_time * png_timep; +typedef const png_time * png_const_timep; +typedef png_time * * png_timepp; + +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) ||\ + defined(PNG_USER_CHUNKS_SUPPORTED) +/* png_unknown_chunk is a structure to hold queued chunks for which there is + * no specific support. The idea is that we can use this to queue + * up private chunks for output even though the library doesn't actually + * know about their semantics. + * + * The data in the structure is set by libpng on read and used on write. + */ +typedef struct png_unknown_chunk_t +{ + png_byte name[5]; /* Textual chunk name with '\0' terminator */ + png_byte *data; /* Data, should not be modified on read! */ + size_t size; + + /* On write 'location' must be set using the flag values listed below. + * Notice that on read it is set by libpng however the values stored have + * more bits set than are listed below. Always treat the value as a + * bitmask. On write set only one bit - setting multiple bits may cause the + * chunk to be written in multiple places. + */ + png_byte location; /* mode of operation at read time */ +} +png_unknown_chunk; + +typedef png_unknown_chunk * png_unknown_chunkp; +typedef const png_unknown_chunk * png_const_unknown_chunkp; +typedef png_unknown_chunk * * png_unknown_chunkpp; +#endif + +/* Flag values for the unknown chunk location byte. */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_AFTER_IDAT 0x08 + +/* Maximum positive integer used in PNG is (2^31)-1 */ +#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL) +#define PNG_UINT_32_MAX ((png_uint_32)(-1)) +#define PNG_SIZE_MAX ((size_t)(-1)) + +/* These are constants for fixed point values encoded in the + * PNG specification manner (x100000) + */ +#define PNG_FP_1 100000 +#define PNG_FP_HALF 50000 +#define PNG_FP_MAX ((png_fixed_point)0x7fffffffL) +#define PNG_FP_MIN (-PNG_FP_MAX) + +/* These describe the color_type field in png_info. */ +/* color type masks */ +#define PNG_COLOR_MASK_PALETTE 1 +#define PNG_COLOR_MASK_COLOR 2 +#define PNG_COLOR_MASK_ALPHA 4 + +/* color types. Note that not all combinations are legal */ +#define PNG_COLOR_TYPE_GRAY 0 +#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) +#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) +#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) +#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) +/* aliases */ +#define PNG_COLOR_TYPE_RGBA PNG_COLOR_TYPE_RGB_ALPHA +#define PNG_COLOR_TYPE_GA PNG_COLOR_TYPE_GRAY_ALPHA + +/* This is for compression type. PNG 1.0-1.2 only define the single type. */ +#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */ +#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE + +/* This is for filter type. PNG 1.0-1.2 only define the single type. */ +#define PNG_FILTER_TYPE_BASE 0 /* Single row per-byte filtering */ +#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */ +#define PNG_FILTER_TYPE_DEFAULT PNG_FILTER_TYPE_BASE + +/* These are for the interlacing type. These values should NOT be changed. */ +#define PNG_INTERLACE_NONE 0 /* Non-interlaced image */ +#define PNG_INTERLACE_ADAM7 1 /* Adam7 interlacing */ +#define PNG_INTERLACE_LAST 2 /* Not a valid value */ + +/* These are for the oFFs chunk. These values should NOT be changed. */ +#define PNG_OFFSET_PIXEL 0 /* Offset in pixels */ +#define PNG_OFFSET_MICROMETER 1 /* Offset in micrometers (1/10^6 meter) */ +#define PNG_OFFSET_LAST 2 /* Not a valid value */ + +/* These are for the pCAL chunk. These values should NOT be changed. */ +#define PNG_EQUATION_LINEAR 0 /* Linear transformation */ +#define PNG_EQUATION_BASE_E 1 /* Exponential base e transform */ +#define PNG_EQUATION_ARBITRARY 2 /* Arbitrary base exponential transform */ +#define PNG_EQUATION_HYPERBOLIC 3 /* Hyperbolic sine transformation */ +#define PNG_EQUATION_LAST 4 /* Not a valid value */ + +/* These are for the sCAL chunk. These values should NOT be changed. */ +#define PNG_SCALE_UNKNOWN 0 /* unknown unit (image scale) */ +#define PNG_SCALE_METER 1 /* meters per pixel */ +#define PNG_SCALE_RADIAN 2 /* radians per pixel */ +#define PNG_SCALE_LAST 3 /* Not a valid value */ + +/* These are for the pHYs chunk. These values should NOT be changed. */ +#define PNG_RESOLUTION_UNKNOWN 0 /* pixels/unknown unit (aspect ratio) */ +#define PNG_RESOLUTION_METER 1 /* pixels/meter */ +#define PNG_RESOLUTION_LAST 2 /* Not a valid value */ + +/* These are for the sRGB chunk. These values should NOT be changed. */ +#define PNG_sRGB_INTENT_PERCEPTUAL 0 +#define PNG_sRGB_INTENT_RELATIVE 1 +#define PNG_sRGB_INTENT_SATURATION 2 +#define PNG_sRGB_INTENT_ABSOLUTE 3 +#define PNG_sRGB_INTENT_LAST 4 /* Not a valid value */ + +/* This is for text chunks */ +#define PNG_KEYWORD_MAX_LENGTH 79 + +/* Maximum number of entries in PLTE/sPLT/tRNS arrays */ +#define PNG_MAX_PALETTE_LENGTH 256 + +/* These determine if an ancillary chunk's data has been successfully read + * from the PNG header, or if the application has filled in the corresponding + * data in the info_struct to be written into the output file. The values + * of the PNG_INFO_ defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001U +#define PNG_INFO_sBIT 0x0002U +#define PNG_INFO_cHRM 0x0004U +#define PNG_INFO_PLTE 0x0008U +#define PNG_INFO_tRNS 0x0010U +#define PNG_INFO_bKGD 0x0020U +#define PNG_INFO_hIST 0x0040U +#define PNG_INFO_pHYs 0x0080U +#define PNG_INFO_oFFs 0x0100U +#define PNG_INFO_tIME 0x0200U +#define PNG_INFO_pCAL 0x0400U +#define PNG_INFO_sRGB 0x0800U /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000U /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + size_t rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info * png_row_infop; +typedef png_row_info * * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. Note that the 'write' function must not + * modify the buffer it is passed. The 'read' function, on the other hand, is + * expected to return the read data in the buffer. + */ +typedef PNG_CALLBACK(void, *png_error_ptr, (png_structp, png_const_charp)); +typedef PNG_CALLBACK(void, *png_rw_ptr, (png_structp, png_bytep, size_t)); +typedef PNG_CALLBACK(void, *png_flush_ptr, (png_structp)); +typedef PNG_CALLBACK(void, *png_read_status_ptr, (png_structp, png_uint_32, + int)); +typedef PNG_CALLBACK(void, *png_write_status_ptr, (png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef PNG_CALLBACK(void, *png_progressive_info_ptr, (png_structp, png_infop)); +typedef PNG_CALLBACK(void, *png_progressive_end_ptr, (png_structp, png_infop)); + +/* The following callback receives png_uint_32 row_number, int pass for the + * png_bytep data of the row. When transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +typedef PNG_CALLBACK(void, *png_progressive_row_ptr, (png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +typedef PNG_CALLBACK(void, *png_user_transform_ptr, (png_structp, png_row_infop, + png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef PNG_CALLBACK(int, *png_user_chunk_ptr, (png_structp, + png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +/* not used anywhere */ +/* typedef PNG_CALLBACK(void, *png_unknown_chunk_ptr, (png_structp)); */ +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This must match the function definition in , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#if INT_MAX >= 0x8000 /* else this might break */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ +#endif + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. + */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); + +/* Section 4: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes + */ + +/* Returns the version number of the library */ +PNG_EXPORT(1, png_uint_32, png_access_version_number, (void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp png_ptr, int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +PNG_EXPORT(3, int, png_sig_cmp, (png_const_bytep sig, size_t start, + size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); + +PNG_EXPORT(6, size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) +#endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); + +#ifdef PNG_READ_SUPPORTED +/* Reset the compression stream */ +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +#endif + +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_bytep data, size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_bytep data, size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); + +/* Allocate and initialize the info structure */ +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); + +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + size_t png_info_struct_size), PNG_DEPRECATED); + +/* Writes all the PNG information before the image. */ +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* CONVERT_tIME */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); +#endif + +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels + * of a PNG file are returned to the calling application when an alpha channel, + * or a tRNS chunk in a palette file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel, and the color channels are unassociated + * (not premultiplied). The gamma encoded color channels must be scaled + * according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and re-encode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. + * image. These are the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' modes + * (the latter being the two common names for associated alpha color channels). + * + * For the 'OPTIMIZED' mode, a pixel is treated as opaque only if the alpha + * value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the output gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(39, void, png_set_filler, (png_structrp png_ptr, png_uint_32 filler, + int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit or 16-bit Gray or 24-bit or 48-bit RGB images. */ +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); +#endif /* READ_FILLER || WRITE_FILLER */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +PNG_EXPORT(41, void, png_set_swap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +PNG_EXPORT(43, void, png_set_packswap, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_color_8p + true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) +#endif +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8_SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp png_ptr)); + +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp png_ptr, png_bytep row, + png_bytep display_row)); +#endif + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp png_ptr, png_bytepp image)); +#endif + +/* Write a row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)); + +/* Write a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows)); + +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp png_ptr, png_bytepp image)); + +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr)); + +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); + +/* Values for png_set_crc_action() say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +#ifdef PNG_WRITE_SUPPORTED +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +PNG_EXPORT(67, void, png_set_filter, (png_structrp png_ptr, int method, + int filters)); +#endif /* WRITE */ + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_FAST_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP) +#define PNG_ALL_FILTERS (PNG_FAST_FILTERS | PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* DEPRECATED */ +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) +#endif /* WRITE_WEIGHTED_FILTER */ + +/* The following are no longer used and will be removed from libpng-1.7: */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +#ifdef PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); + +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_COMPRESSION */ + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* WRITE_CUSTOMIZE_ZTXT_COMPRESSION */ +#endif /* WRITE */ + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng-manual.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); + +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); + +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp png_ptr, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); +#endif + +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp png_ptr, + png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occurred; png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WITH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp png_ptr, + png_voidp progressive_ptr, png_progressive_info_ptr info_fn, + png_progressive_row_ptr row_fn, png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp png_ptr)); + +/* Function to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp info_ptr, png_bytep buffer, size_t buffer_size)); + +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. + */ +PNG_EXPORT(219, size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PROGRESSIVE_READ */ + +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Added at libpng version 1.2.4 */ +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); + +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); + +/* Free data that was allocated internally */ +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask)); + +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008U +#define PNG_FREE_ICCP 0x0010U +#define PNG_FREE_SPLT 0x0020U +#define PNG_FREE_ROWS 0x0040U +#define PNG_FREE_PCAL 0x0080U +#define PNG_FREE_SCAL 0x0100U +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200U +#endif +/* PNG_FREE_LIST 0x0400U removed in 1.6.0 because it is ignored */ +#define PNG_FREE_PLTE 0x1000U +#define PNG_FREE_TRNS 0x2000U +#define PNG_FREE_TEXT 0x4000U +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU +#define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); +#endif + +#ifdef PNG_ERROR_TEXT_SUPPORTED +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +/* The same, but the chunk name is prepended to the error string. */ +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + +#else +/* Fatal error in PNG image of libpng - can't continue */ +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +PNG_EXPORT(111, size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image height in pixels. */ +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image bit_depth. */ +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image color_type. */ +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image filter_type. */ +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image interlace_type. */ +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image compression_type. */ +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +#endif /* EASY_ACCESS */ + +#ifdef PNG_READ_SUPPORTED +/* Returns pointer to signature string read from PNG header */ +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) +#endif + +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) +#endif + +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep exif)); + +PNG_EXPORT(248, png_uint_32, png_get_eXIf_1, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *num_exif, png_bytep *exif)); +PNG_EXPORT(249, void, png_set_eXIf_1, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 num_exif, png_bytep exif)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) +#endif + +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) +#endif + +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif + +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); + +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); +#endif + +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) +#endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); + +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); +#endif /* sCAL */ + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WITH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); +#endif /* HANDLE_AS_UNKNOWN */ + +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#ifdef PNG_WRITE_SUPPORTED +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +#endif +#endif + +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 +#define PNG_HANDLE_CHUNK_LAST 4 + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); +#endif + +/* Added in libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); +#endif + +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* pHYs */ +#endif /* INCH_CONVERSIONS */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* IO_STATE */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { \ + png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ + (composite) = (png_byte)(((temp + (temp >> 8)) >> 8) & 0xff); \ + } + +# define png_composite_16(composite, fg, alpha, bg) \ + { \ + png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ + (composite) = (png_uint_16)(0xffff & ((temp + (temp >> 16)) >> 16)); \ + } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = \ + (png_byte)(0xff & (((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255)) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = \ + (png_uint_16)(0xffff & (((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535)) +#endif /* READ_COMPOSITE_NODIV */ + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif + +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. + */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) + +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)(((png_get_uint_32(buf)^0xffffffffU)+1U)&0x7fffffffU)) \ + : (png_int_32)png_get_uint_32(buf))) + +/* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif +#else +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) +# endif +#endif + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * Section 5: SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accommodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack, set the + * version field to PNG_IMAGE_VERSION and the 'opaque' pointer to NULL + * (this is REQUIRED, your program may crash if you don't do it.) + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) || \ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + +#define PNG_IMAGE_VERSION 1 + +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ + + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two-bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) + + png_uint_32 warning_or_error; + + char message[64]; +} png_image, *png_imagep; + +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at ) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. + */ + +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled. If you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2-byte channels else 1-byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ + +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ +#endif + +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ +#endif + +#define PNG_FORMAT_FLAG_ASSOCIATED_ALPHA 0x40U /* alpha channel is associated */ + +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + * + * WARNING: this macro overflows for some images with more than one component + * and very large image widths. libpng will refuse to process an image where + * this macro would overflow. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + * + * WARNING: this macro overflows a 32-bit integer for some large PNG images, + * libpng will refuse to process an image where such an overflow would occur. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates that the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* STDIO */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* SIMPLIFIED_READ */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +#ifdef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ +#endif /* SIMPLIFIED_WRITE_STDIO */ + +/* With all write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. If row_stride is + * zero, libpng will calculate it for you from the image width and number of + * channels. + * + * Note that the write API does not support interlacing, sub-8-bit pixels or + * most ancillary chunks. If you need to write text chunks (e.g. for copyright + * notices) you need to use one of the other APIs. + */ + +PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, + png_alloc_size_t * PNG_RESTRICT memory_bytes, int convert_to_8_bit, + const void *buffer, png_int_32 row_stride, const void *colormap)); + /* Write the image to the given memory buffer. The function both writes the + * whole PNG data stream to *memory and updates *memory_bytes with the count + * of bytes written. + * + * 'memory' may be NULL. In this case *memory_bytes is not read however on + * success the number of bytes which would have been written will still be + * stored in *memory_bytes. On failure *memory_bytes will contain 0. + * + * If 'memory' is not NULL it must point to memory[*memory_bytes] of + * writeable memory. + * + * If the function returns success memory[*memory_bytes] (if 'memory' is not + * NULL) contains the written PNG data. *memory_bytes will always be less + * than or equal to the original value. + * + * If the function returns false and *memory_bytes was not changed an error + * occurred during write. If *memory_bytes was changed, or is not 0 if + * 'memory' was NULL, the write would have succeeded but for the memory + * buffer being too small. *memory_bytes contains the required number of + * bytes and will be bigger that the original value. + */ + +#define png_image_write_get_memory_size(image, size, convert_to_8_bit, buffer,\ + row_stride, colormap)\ + png_image_write_to_memory(&(image), 0, &(size), convert_to_8_bit, buffer,\ + row_stride, colormap) + /* Return the amount of memory in 'size' required to compress this image. + * The png_image structure 'image' must be filled in as in the above + * function and must not be changed before the actual write call, the buffer + * and all other parameters must also be identical to that in the final + * write call. The 'size' variable need not be initialized. + * + * NOTE: the macro returns true/false, if false is returned 'size' will be + * set to zero and the write failed and probably will fail if tried again. + */ + +/* You can pre-allocate the buffer by making sure it is of sufficient size + * regardless of the amount of compression achieved. The buffer size will + * always be bigger than the original image and it will never be filled. The + * following macros are provided to assist in allocating the buffer. + */ +#define PNG_IMAGE_DATA_SIZE(image) (PNG_IMAGE_SIZE(image)+(image).height) + /* The number of uncompressed bytes in the PNG byte encoding of the image; + * uncompressing the PNG IDAT data will give this number of bytes. + * + * NOTE: while PNG_IMAGE_SIZE cannot overflow for an image in memory this + * macro can because of the extra bytes used in the PNG byte encoding. You + * need to avoid this macro if your image size approaches 2^30 in width or + * height. The same goes for the remainder of these macros; they all produce + * bigger numbers than the actual in-memory image size. + */ +#ifndef PNG_ZLIB_MAX_SIZE +# define PNG_ZLIB_MAX_SIZE(b) ((b)+(((b)+7U)>>3)+(((b)+63U)>>6)+11U) + /* An upper bound on the number of compressed bytes given 'b' uncompressed + * bytes. This is based on deflateBounds() in zlib; different + * implementations of zlib compression may conceivably produce more data so + * if your zlib implementation is not zlib itself redefine this macro + * appropriately. + */ +#endif + +#define PNG_IMAGE_COMPRESSED_SIZE_MAX(image)\ + PNG_ZLIB_MAX_SIZE((png_alloc_size_t)PNG_IMAGE_DATA_SIZE(image)) + /* An upper bound on the size of the data in the PNG IDAT chunks. */ + +#define PNG_IMAGE_PNG_SIZE_MAX_(image, image_size)\ + ((8U/*sig*/+25U/*IHDR*/+16U/*gAMA*/+44U/*cHRM*/+12U/*IEND*/+\ + (((image).format&PNG_FORMAT_FLAG_COLORMAP)?/*colormap: PLTE, tRNS*/\ + 12U+3U*(image).colormap_entries/*PLTE data*/+\ + (((image).format&PNG_FORMAT_FLAG_ALPHA)?\ + 12U/*tRNS*/+(image).colormap_entries:0U):0U)+\ + 12U)+(12U*((image_size)/PNG_ZBUF_SIZE))/*IDAT*/+(image_size)) + /* A helper for the following macro; if your compiler cannot handle the + * following macro use this one with the result of + * PNG_IMAGE_COMPRESSED_SIZE_MAX(image) as the second argument (most + * compilers should handle this just fine.) + */ + +#define PNG_IMAGE_PNG_SIZE_MAX(image)\ + PNG_IMAGE_PNG_SIZE_MAX_(image, PNG_IMAGE_COMPRESSED_SIZE_MAX(image)) + /* An upper bound on the total length of the PNG data stream for 'image'. + * The result is of type png_alloc_size_t, on 32-bit systems this may + * overflow even though PNG_IMAGE_DATA_SIZE does not overflow; the write will + * run out of buffer space but return a corrected size which should work. + */ +#endif /* SIMPLIFIED_WRITE */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ +#endif /* SIMPLIFIED_{READ|WRITE} */ + +/******************************************************************************* + * Section 6: IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilities, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */ +#define PNG_SKIP_sRGB_CHECK_PROFILE 4 /* SOFTWARE: Check ICC profile for sRGB */ +#ifdef PNG_MIPS_MSA_API_SUPPORTED +# define PNG_MIPS_MSA 6 /* HARDWARE: MIPS Msa SIMD instructions supported */ +#endif +#define PNG_IGNORE_ADLER32 8 +#ifdef PNG_POWERPC_VSX_API_SUPPORTED +# define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); +#endif /* SET_OPTION */ + +/******************************************************************************* + * END OF HARDWARE AND SOFTWARE OPTIONS + ******************************************************************************/ + +/* Maintainer: Put new public prototypes here ^, in libpng.3, in project + * defs, and in scripts/symbols.def. + */ + +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(249); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/libs/platform/RV1109_1126/libpng/include/pngconf.h b/libs/platform/RV1109_1126/libpng/include/pngconf.h new file mode 100644 index 0000000..927a769 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/include/pngconf.h @@ -0,0 +1,623 @@ + +/* pngconf.h - machine-configurable file for libpng + * + * libpng version 1.6.37 + * + * Copyright (c) 2018-2019 Cosmin Truta + * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson + * Copyright (c) 1996-1997 Andreas Dilger + * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ + +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. + * + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. + */ + +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Required for the definition of jmp_buf and the declaration of longjmp: */ +# include +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +/* Prior to 1.6.0, it was possible to turn off 'const' in declarations, + * using PNG_NO_CONST. This is no longer supported. + */ +#define PNG_CONST const /* backward compatibility only */ + +/* This controls optimization of the reading of 16-bit and 32-bit + * values from PNG files. It can be set on a per-app-file basis: it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. + */ + +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows systems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall +# endif +# else + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl +# endif +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall +# endif +# endif /* compiler/api */ + + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" +# endif + +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP +# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ + +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ + +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI +#endif +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI +#endif +#ifndef PNGAPI +# define PNGAPI PNGCAPI +#endif + +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. + */ +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# endif + +# ifndef PNG_IMPEXP +# define PNG_IMPEXP +# endif +#endif + +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args +#endif + +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif + + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ + +#ifndef PNG_EXPORTA +# define PNG_EXPORTA(ordinal, type, name, args, attributes) \ + PNG_FUNCTION(PNG_EXPORT_TYPE(type), (PNGAPI name), PNGARG(args), \ + PNG_LINKAGE_API attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args) \ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. + */ + +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__clang__) && defined(__has_attribute) + /* Clang defines both __clang__ and __GNUC__. Check __clang__ first. */ +# if !defined(PNG_USE_RESULT) && __has_attribute(__warn_unused_result__) +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# if !defined(PNG_NORETURN) && __has_attribute(__noreturn__) +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if !defined(PNG_ALLOCATED) && __has_attribute(__malloc__) +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# if !defined(PNG_DEPRECATED) && __has_attribute(__deprecated__) +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# if !defined(PNG_PRIVATE) +# ifdef __has_extension +# if __has_extension(attribute_unavailable_with_message) +# define PNG_PRIVATE __attribute__((__unavailable__(\ + "This function is not exported by libpng."))) +# endif +# endif +# endif +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif + +# elif defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ > 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__.__GNUC_MINOR__ > 3.0 */ +# endif /* __GNUC__ >= 3 */ + +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif + +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif + +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) +# endif +#endif + +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8-bit bytes" +#endif + +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16-bit type" +#endif + +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16-bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32-bit (or more) type" +#endif + +#if UINT_MAX > 4294967294U + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294U + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32-bit (or more) type" +#endif + +/* Prior to 1.6.0, it was possible to disable the use of size_t and ptrdiff_t. + * From 1.6.0 onwards, an ISO C90 compiler, as well as a standard-compliant + * behavior of sizeof and ptrdiff_t are required. + * The legacy typedefs are provided here for backwards compatibility. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than size_t, and no smaller + * than png_uint_32. Casts from size_t or png_uint_32 to png_alloc_size_t are + * not necessary; in fact, it is recommended not to use them at all, so that + * the compiler can complain when something turns out to be problematic. + * + * Casts in the other direction (from png_alloc_size_t to size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef size_t * png_size_tp; +typedef const size_t * png_const_size_tp; + +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * png_doublep; +typedef const double * png_const_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double * * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char * * * png_charppp; + +#endif /* PNG_BUILDING_SYMBOL_TABLE */ + +#endif /* PNGCONF_H */ diff --git a/libs/platform/RV1109_1126/libpng/include/pnglibconf.h b/libs/platform/RV1109_1126/libpng/include/pnglibconf.h new file mode 100644 index 0000000..7f6a817 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/include/pnglibconf.h @@ -0,0 +1,219 @@ +/* pnglibconf.h - library build configuration */ + +/* libpng version 1.6.37 */ + +/* Copyright (c) 2018-2019 Cosmin Truta */ +/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ + +/* This code is released under the libpng license. */ +/* For conditions of distribution and use, see the disclaimer */ +/* and license in png.h */ + +/* pnglibconf.h */ +/* Machine generated file: DO NOT EDIT */ +/* Derived from: scripts/pnglibconf.dfa */ +#ifndef PNGLCONF_H +#define PNGLCONF_H +/* options */ +#define PNG_16BIT_SUPPORTED +#define PNG_ALIGNED_MEMORY_SUPPORTED +/*#undef PNG_ARM_NEON_API_SUPPORTED*/ +/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/ +#define PNG_BENIGN_ERRORS_SUPPORTED +#define PNG_BENIGN_READ_ERRORS_SUPPORTED +/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/ +#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_COLORSPACE_SUPPORTED +#define PNG_CONSOLE_IO_SUPPORTED +#define PNG_CONVERT_tIME_SUPPORTED +#define PNG_EASY_ACCESS_SUPPORTED +/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/ +#define PNG_ERROR_TEXT_SUPPORTED +#define PNG_FIXED_POINT_SUPPORTED +#define PNG_FLOATING_ARITHMETIC_SUPPORTED +#define PNG_FLOATING_POINT_SUPPORTED +#define PNG_FORMAT_AFIRST_SUPPORTED +#define PNG_FORMAT_BGR_SUPPORTED +#define PNG_GAMMA_SUPPORTED +#define PNG_GET_PALETTE_MAX_SUPPORTED +#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#define PNG_INCH_CONVERSIONS_SUPPORTED +#define PNG_INFO_IMAGE_SUPPORTED +#define PNG_IO_STATE_SUPPORTED +#define PNG_MNG_FEATURES_SUPPORTED +#define PNG_POINTER_INDEXING_SUPPORTED +/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/ +/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/ +#define PNG_PROGRESSIVE_READ_SUPPORTED +#define PNG_READ_16BIT_SUPPORTED +#define PNG_READ_ALPHA_MODE_SUPPORTED +#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_READ_BACKGROUND_SUPPORTED +#define PNG_READ_BGR_SUPPORTED +#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_READ_COMPOSITE_NODIV_SUPPORTED +#define PNG_READ_COMPRESSED_TEXT_SUPPORTED +#define PNG_READ_EXPAND_16_SUPPORTED +#define PNG_READ_EXPAND_SUPPORTED +#define PNG_READ_FILLER_SUPPORTED +#define PNG_READ_GAMMA_SUPPORTED +#define PNG_READ_GET_PALETTE_MAX_SUPPORTED +#define PNG_READ_GRAY_TO_RGB_SUPPORTED +#define PNG_READ_INTERLACING_SUPPORTED +#define PNG_READ_INT_FUNCTIONS_SUPPORTED +#define PNG_READ_INVERT_ALPHA_SUPPORTED +#define PNG_READ_INVERT_SUPPORTED +#define PNG_READ_OPT_PLTE_SUPPORTED +#define PNG_READ_PACKSWAP_SUPPORTED +#define PNG_READ_PACK_SUPPORTED +#define PNG_READ_QUANTIZE_SUPPORTED +#define PNG_READ_RGB_TO_GRAY_SUPPORTED +#define PNG_READ_SCALE_16_TO_8_SUPPORTED +#define PNG_READ_SHIFT_SUPPORTED +#define PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_STRIP_ALPHA_SUPPORTED +#define PNG_READ_SUPPORTED +#define PNG_READ_SWAP_ALPHA_SUPPORTED +#define PNG_READ_SWAP_SUPPORTED +#define PNG_READ_TEXT_SUPPORTED +#define PNG_READ_TRANSFORMS_SUPPORTED +#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_READ_USER_CHUNKS_SUPPORTED +#define PNG_READ_USER_TRANSFORM_SUPPORTED +#define PNG_READ_bKGD_SUPPORTED +#define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED +#define PNG_READ_gAMA_SUPPORTED +#define PNG_READ_hIST_SUPPORTED +#define PNG_READ_iCCP_SUPPORTED +#define PNG_READ_iTXt_SUPPORTED +#define PNG_READ_oFFs_SUPPORTED +#define PNG_READ_pCAL_SUPPORTED +#define PNG_READ_pHYs_SUPPORTED +#define PNG_READ_sBIT_SUPPORTED +#define PNG_READ_sCAL_SUPPORTED +#define PNG_READ_sPLT_SUPPORTED +#define PNG_READ_sRGB_SUPPORTED +#define PNG_READ_tEXt_SUPPORTED +#define PNG_READ_tIME_SUPPORTED +#define PNG_READ_tRNS_SUPPORTED +#define PNG_READ_zTXt_SUPPORTED +#define PNG_SAVE_INT_32_SUPPORTED +#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SEQUENTIAL_READ_SUPPORTED +#define PNG_SETJMP_SUPPORTED +#define PNG_SET_OPTION_SUPPORTED +#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_SET_USER_LIMITS_SUPPORTED +#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED +#define PNG_SIMPLIFIED_READ_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED +#define PNG_SIMPLIFIED_WRITE_SUPPORTED +#define PNG_STDIO_SUPPORTED +#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_TEXT_SUPPORTED +#define PNG_TIME_RFC1123_SUPPORTED +#define PNG_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_USER_CHUNKS_SUPPORTED +#define PNG_USER_LIMITS_SUPPORTED +#define PNG_USER_MEM_SUPPORTED +#define PNG_USER_TRANSFORM_INFO_SUPPORTED +#define PNG_USER_TRANSFORM_PTR_SUPPORTED +#define PNG_WARNINGS_SUPPORTED +#define PNG_WRITE_16BIT_SUPPORTED +#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#define PNG_WRITE_BGR_SUPPORTED +#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED +#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED +#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +#define PNG_WRITE_FILLER_SUPPORTED +#define PNG_WRITE_FILTER_SUPPORTED +#define PNG_WRITE_FLUSH_SUPPORTED +#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED +#define PNG_WRITE_INTERLACING_SUPPORTED +#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED +#define PNG_WRITE_INVERT_ALPHA_SUPPORTED +#define PNG_WRITE_INVERT_SUPPORTED +#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED +#define PNG_WRITE_PACKSWAP_SUPPORTED +#define PNG_WRITE_PACK_SUPPORTED +#define PNG_WRITE_SHIFT_SUPPORTED +#define PNG_WRITE_SUPPORTED +#define PNG_WRITE_SWAP_ALPHA_SUPPORTED +#define PNG_WRITE_SWAP_SUPPORTED +#define PNG_WRITE_TEXT_SUPPORTED +#define PNG_WRITE_TRANSFORMS_SUPPORTED +#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +#define PNG_WRITE_USER_TRANSFORM_SUPPORTED +#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#define PNG_WRITE_bKGD_SUPPORTED +#define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED +#define PNG_WRITE_gAMA_SUPPORTED +#define PNG_WRITE_hIST_SUPPORTED +#define PNG_WRITE_iCCP_SUPPORTED +#define PNG_WRITE_iTXt_SUPPORTED +#define PNG_WRITE_oFFs_SUPPORTED +#define PNG_WRITE_pCAL_SUPPORTED +#define PNG_WRITE_pHYs_SUPPORTED +#define PNG_WRITE_sBIT_SUPPORTED +#define PNG_WRITE_sCAL_SUPPORTED +#define PNG_WRITE_sPLT_SUPPORTED +#define PNG_WRITE_sRGB_SUPPORTED +#define PNG_WRITE_tEXt_SUPPORTED +#define PNG_WRITE_tIME_SUPPORTED +#define PNG_WRITE_tRNS_SUPPORTED +#define PNG_WRITE_zTXt_SUPPORTED +#define PNG_bKGD_SUPPORTED +#define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED +#define PNG_gAMA_SUPPORTED +#define PNG_hIST_SUPPORTED +#define PNG_iCCP_SUPPORTED +#define PNG_iTXt_SUPPORTED +#define PNG_oFFs_SUPPORTED +#define PNG_pCAL_SUPPORTED +#define PNG_pHYs_SUPPORTED +#define PNG_sBIT_SUPPORTED +#define PNG_sCAL_SUPPORTED +#define PNG_sPLT_SUPPORTED +#define PNG_sRGB_SUPPORTED +#define PNG_tEXt_SUPPORTED +#define PNG_tIME_SUPPORTED +#define PNG_tRNS_SUPPORTED +#define PNG_zTXt_SUPPORTED +/* end of options */ +/* settings */ +#define PNG_API_RULE 0 +#define PNG_DEFAULT_READ_MACROS 1 +#define PNG_GAMMA_THRESHOLD_FIXED 5000 +#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE +#define PNG_INFLATE_BUF_SIZE 1024 +#define PNG_LINKAGE_API extern +#define PNG_LINKAGE_CALLBACK extern +#define PNG_LINKAGE_DATA extern +#define PNG_LINKAGE_FUNCTION extern +#define PNG_MAX_GAMMA_8 11 +#define PNG_QUANTIZE_BLUE_BITS 5 +#define PNG_QUANTIZE_GREEN_BITS 5 +#define PNG_QUANTIZE_RED_BITS 5 +#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) +#define PNG_TEXT_Z_DEFAULT_STRATEGY 0 +#define PNG_USER_CHUNK_CACHE_MAX 1000 +#define PNG_USER_CHUNK_MALLOC_MAX 8000000 +#define PNG_USER_HEIGHT_MAX 1000000 +#define PNG_USER_WIDTH_MAX 1000000 +#define PNG_ZBUF_SIZE 8192 +#define PNG_ZLIB_VERNUM 0x12b0 +#define PNG_Z_DEFAULT_COMPRESSION (-1) +#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 +#define PNG_Z_DEFAULT_STRATEGY 1 +#define PNG_sCAL_PRECISION 5 +#define PNG_sRGB_PROFILE_CHECKS 2 +/* end of settings */ +#endif /* PNGLCONF_H */ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.a b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.a new file mode 100644 index 0000000..eb43fc0 Binary files /dev/null and b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.a differ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.la b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.la new file mode 100644 index 0000000..26050e0 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.la @@ -0,0 +1,41 @@ +# libpng16.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libpng16.so.16' + +# Names of this library. +library_names='libpng16.so.16.37.0 libpng16.so.16 libpng16.so' + +# The name of the static archive. +old_library='libpng16.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rv1109/lib/ -lz -lm' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libpng16. +current=53 +age=37 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='///home/xz/Documents/testing/compile_test/lpng1637/build/lib' diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.so b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.so new file mode 100644 index 0000000..56fbd19 Binary files /dev/null and b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng.so differ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.a b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.a new file mode 100644 index 0000000..eb43fc0 Binary files /dev/null and b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.a differ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.la b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.la new file mode 100644 index 0000000..26050e0 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.la @@ -0,0 +1,41 @@ +# libpng16.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libpng16.so.16' + +# Names of this library. +library_names='libpng16.so.16.37.0 libpng16.so.16 libpng16.so' + +# The name of the static archive. +old_library='libpng16.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -L/home/xz/Documents/testing/compile_test/lpng1637/zlib/rv1109/lib/ -lz -lm' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libpng16. +current=53 +age=37 +revision=0 + +# Is this an already installed library? +installed=yes + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='///home/xz/Documents/testing/compile_test/lpng1637/build/lib' diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so new file mode 100644 index 0000000..56fbd19 Binary files /dev/null and b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so differ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so.16 b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so.16 new file mode 100644 index 0000000..56fbd19 Binary files /dev/null and b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so.16 differ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so.16.37.0 b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so.16.37.0 new file mode 100644 index 0000000..56fbd19 Binary files /dev/null and b/libs/platform/RV1109_1126/libpng/lib/Linux/libpng16.so.16.37.0 differ diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/pkgconfig/libpng.pc b/libs/platform/RV1109_1126/libpng/lib/Linux/pkgconfig/libpng.pc new file mode 100644 index 0000000..5a33e37 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/lib/Linux/pkgconfig/libpng.pc @@ -0,0 +1,12 @@ +prefix=///home/xz/Documents/testing/compile_test/lpng1637/build +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/libpng16 + +Name: libpng +Description: Loads and saves PNG files +Version: 1.6.37 +Requires: zlib +Libs: -L${libdir} -lpng16 +Libs.private: -lm -lz -lm +Cflags: -I${includedir} diff --git a/libs/platform/RV1109_1126/libpng/lib/Linux/pkgconfig/libpng16.pc b/libs/platform/RV1109_1126/libpng/lib/Linux/pkgconfig/libpng16.pc new file mode 100644 index 0000000..5a33e37 --- /dev/null +++ b/libs/platform/RV1109_1126/libpng/lib/Linux/pkgconfig/libpng16.pc @@ -0,0 +1,12 @@ +prefix=///home/xz/Documents/testing/compile_test/lpng1637/build +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include/libpng16 + +Name: libpng +Description: Loads and saves PNG files +Version: 1.6.37 +Requires: zlib +Libs: -L${libdir} -lpng16 +Libs.private: -lm -lz -lm +Cflags: -I${includedir} diff --git a/libs/platform/RV1109_1126/zlib/include/zconf.h b/libs/platform/RV1109_1126/zlib/include/zconf.h new file mode 100644 index 0000000..77398c1 --- /dev/null +++ b/libs/platform/RV1109_1126/zlib/include/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#if 1 /* was set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/libs/platform/RV1109_1126/zlib/include/zlib.h b/libs/platform/RV1109_1126/zlib/include/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/libs/platform/RV1109_1126/zlib/include/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, 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 notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#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 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#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) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/libs/platform/RV1109_1126/zlib/lib/Linux/libz.a b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.a new file mode 100644 index 0000000..b0497bd Binary files /dev/null and b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.a differ diff --git a/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so new file mode 100644 index 0000000..a789b70 Binary files /dev/null and b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so differ diff --git a/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so.1 b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so.1 new file mode 100644 index 0000000..a789b70 Binary files /dev/null and b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so.1 differ diff --git a/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so.1.2.11 b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so.1.2.11 new file mode 100644 index 0000000..a789b70 Binary files /dev/null and b/libs/platform/RV1109_1126/zlib/lib/Linux/libz.so.1.2.11 differ diff --git a/libs/platform/RV1109_1126/zlib/lib/Linux/pkgconfig/zlib.pc b/libs/platform/RV1109_1126/zlib/lib/Linux/pkgconfig/zlib.pc new file mode 100644 index 0000000..d378ea2 --- /dev/null +++ b/libs/platform/RV1109_1126/zlib/lib/Linux/pkgconfig/zlib.pc @@ -0,0 +1,13 @@ +prefix=/home/xz/Documents/testing/compile_test/zlib1211/zlib-1.2.11/build +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +sharedlibdir=${libdir} +includedir=${prefix}/include + +Name: zlib +Description: zlib compression library +Version: 1.2.11 + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/libs/rklibs/.gitignore b/libs/rklibs/.gitignore new file mode 100644 index 0000000..70b84df --- /dev/null +++ b/libs/rklibs/.gitignore @@ -0,0 +1 @@ +.gitreview diff --git a/libs/rklibs/LICENSE b/libs/rklibs/LICENSE new file mode 100644 index 0000000..0fbe7ec --- /dev/null +++ b/libs/rklibs/LICENSE @@ -0,0 +1,28 @@ +// Copyright 2020 Rockchip Electronics Co.,Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/rklibs/README.md b/libs/rklibs/README.md new file mode 100644 index 0000000..416a03c --- /dev/null +++ b/libs/rklibs/README.md @@ -0,0 +1,18 @@ +## rklibs + +在 rknn_model_zoo/libs/rklibs 目录下,根据实际需要,下载以下依赖库 + +``` +# RK1808/RV1126/RV1109 NPU 依赖库 +git clone https://github.com/rockchip-linux/rknpu + +# RK3399pro NPU 依赖库 +git clone https://github.com/airockchip/RK3399Pro_npu + +# RK3566/RK3568/RK3588/RV1106/RV1103 NPU 依赖库 +git clone https://github.com/rockchip-linux/rknpu2 + +# RGA调用依赖库,不区分硬件平台 +git clone https://github.com/airockchip/librga +``` + diff --git a/libs/rklibs/drivers/README b/libs/rklibs/drivers/README new file mode 100644 index 0000000..221dc00 --- /dev/null +++ b/libs/rklibs/drivers/README @@ -0,0 +1,5 @@ +Mini driver (folder name contains mini) only support rknn models with precompiled mode. + +linux-aarch64: RK1808 +linux-arm: RK1806 +linux-armhf-puma: RV1109/RV1126 diff --git a/libs/rklibs/drivers/common/etc/init.d/S05NPU_init b/libs/rklibs/drivers/common/etc/init.d/S05NPU_init new file mode 100644 index 0000000..b9e97ab --- /dev/null +++ b/libs/rklibs/drivers/common/etc/init.d/S05NPU_init @@ -0,0 +1,43 @@ +#!/bin/sh + +GetDDRBitWidth() +{ + bit=1 + a=1 + while [ 1 ];do + a=$((1<<$bit)) + if [ $a -ge $1 ];then + break + fi + let bit++ + done + return $(($bit<16?16:$bit)) +} + + + +case "$1" in + start) + printf "insmod NPU modules: " + cp /usr/lib/cl_*.h /tmp/ + DDR_SIZE=`cat /proc/meminfo|grep MemTotal|awk '{print $2}'` + GetDDRBitWidth $DDR_SIZE + bitWidth=$(($?+10)) + insmod /lib/modules/galcore.ko contiguousSize=0x400000 DDRBitWidth=$bitWidth + unset MAX_FREQ + read MAX_FREQ < /sys/class/devfreq/ffbc0000.npu/max_freq + echo $MAX_FREQ > /sys/class/devfreq/ffbc0000.npu/userspace/set_freq + [ $? = 0 ] && echo "OK" || echo "FAIL" +#start_rknn.sh & + ;; + stop) + ;; + restart|reload) + $0 stop + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac +exit 0 diff --git a/libs/rklibs/drivers/common/etc/init.d/S60NPU_init b/libs/rklibs/drivers/common/etc/init.d/S60NPU_init new file mode 100644 index 0000000..2b3e454 --- /dev/null +++ b/libs/rklibs/drivers/common/etc/init.d/S60NPU_init @@ -0,0 +1,43 @@ +#!/bin/sh -x + +GetDDRBitWidth() +{ + bit=1 + a=1 + while [ 1 ];do + a=$((1<<$bit)) + if [ $a -ge $1 ];then + break + fi + let bit++ + done + return $(($bit<16?16:$bit)) +} + + + +case "$1" in + start) + printf "insmod NPU modules: " + cp /usr/lib/cl_*.h /tmp/ + DDR_SIZE=`cat /proc/meminfo|grep MemTotal|awk '{print $2}'` + GetDDRBitWidth $DDR_SIZE + bitWidth=$(($?+10)) + insmod /lib/modules/galcore.ko contiguousSize=0x400000 DDRBitWidth=$bitWidth + unset MAX_FREQ + read MAX_FREQ < /sys/class/devfreq/ffbc0000.npu/max_freq + echo $MAX_FREQ > /sys/class/devfreq/ffbc0000.npu/userspace/set_freq + [ $? = 0 ] && echo "OK" || echo "FAIL" + start_rknn.sh & + ;; + stop) + ;; + restart|reload) + $0 stop + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac +exit 0 diff --git a/libs/rklibs/drivers/common/usr/bin/restart_rknn.sh b/libs/rklibs/drivers/common/usr/bin/restart_rknn.sh new file mode 100644 index 0000000..a38bee3 --- /dev/null +++ b/libs/rklibs/drivers/common/usr/bin/restart_rknn.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +killall start_rknn.sh > /dev/null 2>&1 +killall rknn_server > /dev/null 2>&1 +start_rknn.sh & diff --git a/libs/rklibs/drivers/common/usr/bin/start_rknn.sh b/libs/rklibs/drivers/common/usr/bin/start_rknn.sh new file mode 100644 index 0000000..4de64ac --- /dev/null +++ b/libs/rklibs/drivers/common/usr/bin/start_rknn.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +export RKNN_SERVER_PLUGINS='/usr/lib/npu/rknn/plugins/' + +while true +do + sleep 1 + rknn_server #>/dev/null 2>&1 +done diff --git a/libs/rklibs/drivers/common/usr/bin/start_usb.sh b/libs/rklibs/drivers/common/usr/bin/start_usb.sh new file mode 100644 index 0000000..0dc8ab4 --- /dev/null +++ b/libs/rklibs/drivers/common/usr/bin/start_usb.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +configfs_init() +{ + PID=$1 + CONFIG_STRING=$2 + mkdir -p /dev/usb-ffs -m 0770 + mkdir -p /dev/usb-ffs/$CONFIG_STRING -m 0770 + mount -t configfs none /sys/kernel/config + mkdir -p /sys/kernel/config/usb_gadget/rockchip -m 0770 + echo 0x2207 > /sys/kernel/config/usb_gadget/rockchip/idVendor + echo $PID > /sys/kernel/config/usb_gadget/rockchip/idProduct + mkdir -p /sys/kernel/config/usb_gadget/rockchip/strings/0x409 -m 0770 + echo "0123456789ABCDEF" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/serialnumber + echo "rockchip" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/manufacturer + echo "rk3xxx" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/product + mkdir -p /sys/kernel/config/usb_gadget/rockchip/configs/b.1 -m 0770 + mkdir -p /sys/kernel/config/usb_gadget/rockchip/configs/b.1/strings/0x409 -m 0770 + echo 500 > /sys/kernel/config/usb_gadget/rockchip/configs/b.1/MaxPower + echo \"$CONFIG_STRING\" > /sys/kernel/config/usb_gadget/rockchip/configs/b.1/strings/0x409/configuration +} + +function_init() +{ + CONFIG_STRING=$1 + mkdir -p /sys/kernel/config/usb_gadget/rockchip/functions/ffs.$CONFIG_STRING + rm -f /sys/kernel/config/usb_gadget/rockchip/configs/b.1/ffs.* + ln -s /sys/kernel/config/usb_gadget/rockchip/functions/ffs.$CONFIG_STRING /sys/kernel/config/usb_gadget/rockchip/configs/b.1/ffs.$CONFIG_STRING +} + +case "$1" in +adb) + killall adbd start_rknn.sh rknn_server > /dev/null 2>&1 + + echo "none" > /sys/kernel/config/usb_gadget/rockchip/UDC + + umount /sys/kernel/config + umount /dev/usb-ffs/ntb > /dev/null 2>&1 + rm -rf /dev/usb-ffs/ntb + + configfs_init 0x0006 adb + function_init adb + + # START_APP_BEFORE_UDC + mkdir -p /dev/usb-ffs/adb + mount -o uid=2000,gid=2000 -t functionfs adb /dev/usb-ffs/adb + export service_adb_tcp_port=5555 + adbd& + sleep 1 + + UDC=`ls /sys/class/udc/| awk '{print $1}'` + echo $UDC > /sys/kernel/config/usb_gadget/rockchip/UDC + # START_APP_AFTER_UDC + + start_rknn.sh & + + ;; +ntb) + killall adbd start_rknn.sh rknn_server > /dev/null 2>&1 + + echo "none" > /sys/kernel/config/usb_gadget/rockchip/UDC + + umount /sys/kernel/config + umount /dev/usb-ffs/adb > /dev/null 2>&1 + rm -rf /dev/usb-ffs/adb + + configfs_init 0x1808 ntb + function_init ntb + + # START_APP_BEFORE_UDC + mkdir -p /dev/usb-ffs/ntb + mount -o uid=2000,gid=2000 -t functionfs ntb /dev/usb-ffs/ntb + + start_rknn.sh & + + ;; +*) + echo "Usage: $0 {adb|ntb}" + exit 1 +esac + +exit 0 diff --git a/libs/rklibs/drivers/common/usr/lib/cl_viv_vx_ext.h b/libs/rklibs/drivers/common/usr/lib/cl_viv_vx_ext.h new file mode 100644 index 0000000..a6ecd50 --- /dev/null +++ b/libs/rklibs/drivers/common/usr/lib/cl_viv_vx_ext.h @@ -0,0 +1,1456 @@ + + + +#ifndef _GC_VX_H +#define _GC_VX_H 1 + +#ifdef _VIV_VX_EXTENSION + +#pragma OPENCL EXTENSION CL_VIV_asm : enable + +#ifndef VX_VERSION +#define VX_VERSION 1 +#endif + +typedef enum _VXC_FilterMode +{ + VXC_FM_BOX = 0, + VXC_FM_Guassian = 1, + VXC_FM_SobelX = 2, + VXC_FM_SobelY = 3, + VXC_FM_ScharrX = 4, + VXC_FM_ScharrY = 5, + VXC_FM_Max = 8, + VXC_FM_Min = 9, + VXC_FM_Median = 10 +} vxc_filter_mode; + +typedef enum _VXC_RoundMode +{ + VXC_RM_Truncate = 0, + VXC_RM_TowardZero = 0, + VXC_RM_TowardInf = 1, + VXC_RM_ToNearestEven = 2 +} vxc_round_mode; + +typedef enum _VXC_ScatteredOffsetType +{ + VXC_OFFSET_UNSIGNED32 = 0, + VXC_OFFSET_SIGNED32 = 1, + VXC_OFFSET_UNSIGNED16 = 2, + VXC_OFFSET_SIGNED16 = 3, + VXC_OFFSET_UNSIGNED8 = 4, + VXC_OFFSET_SIGNED8 = 5, +} VXC_ScatteredOffsetType; + +typedef enum _VXC_AtomicOp +{ + VXC_ATOMIC_OP_ADD = 0, + VXC_ATOMIC_OP_MIN = 1, + VXC_ATOMIC_OP_MAX = 2, + VXC_ATOMIC_OP_OR = 3, + VXC_ATOMIC_OP_AND = 4, + VXC_ATOMIC_OP_XOR = 5, + VXC_ATOMIC_OP_XCHG = 6, +}VXC_AtomicOpType; + +#define VXC_CLAMP_BITMASK 0x00400000 +#define VXC_PREADJ_BITMASK 0x00200000 +#define VXC_RANGEPI_BITMASK 0x00100000 +#define VXC_FILTER_BITMASK 0x000F0000 +#define VXC_START_BIN_BITMASK 0x0000F000 +#define VXC_END_BIN_BITMASK 0x00000F00 +#define VXC_SOURCE_BIN_BITMASK 0x000000F0 +#define VXC_ROUNDING_MODE_BITMASK 0x0000000C +#define VXC_ENABLEBOOL_BITMASK 0x00000002 +#define VXC_SIGNEXT_BITMASK 0x00000001 + + +#define VXC_OFFSET_TYPE_BITMASK 0x00070000 +#define VXC_OFFSET_TYPE_SHIFT 16 + + +#define VXC_ATOM_OP_BITMASK 0x00380000 +#define VXC_ATOM_OP_SHIFT 19 + +#define VXC_MODIFIER(StartBin, EndBin, SourceBin, RoundingMode, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) | \ + ((RoundingMode << 2)&VXC_ROUNDING_MODE_BITMASK) \ + ) + +#define VXC_MODIFIER_SIGNEXT(StartBin, EndBin, SourceBin, SignExt, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + ((SignExt)&VXC_SIGNEXT_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_MAGPHASE(StartBin, EndBin, SourceBin, NoPreAdjust, RangePi) \ + ( \ + (VXC_CLAMP_BITMASK) | \ + (((RangePi) << 20)&VXC_RANGEPI_BITMASK) | \ + (((NoPreAdjust) << 21)&VXC_PREADJ_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_CLAMP(StartBin, EndBin, SourceBin, EnableBool) \ + ( \ + (((EnableBool) << 1)&VXC_ENABLEBOOL_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_FILTER(StartBin, EndBin, SourceBin, Filter, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + (((Filter) << 16)&VXC_FILTER_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_BIN(StartBin, EndBin, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_GATHER(StartBin, EndBin, SourceBin, OffsetType) \ + ( \ + (((OffsetType) << VXC_OFFSET_TYPE_SHIFT)&VXC_OFFSET_TYPE_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_SCATTER(StartBin, EndBin, SourceBin, OffsetType) \ + ( \ + (((OffsetType) << VXC_OFFSET_TYPE_SHIFT)&VXC_OFFSET_TYPE_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_ATOMIC_S(StartBin, EndBin, SourceBin, OffsetType, AtomOp) \ + ( \ + (((OffsetType) << VXC_OFFSET_TYPE_SHIFT)&VXC_OFFSET_TYPE_BITMASK) | \ + (((AtomOp) << VXC_ATOM_OP_SHIFT)&VXC_ATOM_OP_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + + +#define VXC_MODIFIER_SetDestClamp(VxModifier, Clamp) ((VxModifier) | (((Clamp) << 22)&VXC_CLAMP_BITMASK)) + +#define VXC_DEFAULT_MODIFIER (-1) + +typedef unsigned int vxc_modifier; + + +typedef _viv_char2_packed vxc_char2; +typedef _viv_char4_packed vxc_char4; +typedef _viv_char8_packed vxc_char8; +typedef _viv_char16_packed vxc_char16; +typedef struct _vxc_char32 +{ + vxc_char16 hi; + vxc_char16 lo; +} vxc_char32; + + +typedef _viv_uchar2_packed vxc_uchar2; +typedef _viv_uchar4_packed vxc_uchar4; +typedef _viv_uchar8_packed vxc_uchar8; +typedef _viv_uchar16_packed vxc_uchar16; +typedef struct _vxc_uchar32 +{ + vxc_uchar16 hi; + vxc_uchar16 lo; +} vxc_uchar32; + + +typedef _viv_short2_packed vxc_short2; +typedef _viv_short4_packed vxc_short4; +typedef _viv_short8_packed vxc_short8; +typedef struct _vxc_short16 +{ + vxc_short8 hi; + vxc_short8 lo; +} vxc_short16; + + +typedef _viv_ushort2_packed vxc_ushort2; +typedef _viv_ushort4_packed vxc_ushort4; +typedef _viv_ushort8_packed vxc_ushort8; +typedef struct _vxc_ushort16 +{ + vxc_ushort8 hi; + vxc_ushort8 lo; +} vxc_ushort16; + + +typedef int vxc_int; +typedef int2 vxc_int2; +typedef int4 vxc_int4; +typedef int8 vxc_int8; +typedef int16 vxc_int16; + + +typedef uint vxc_uint; +typedef uint2 vxc_uint2; +typedef uint4 vxc_uint4; +typedef uint8 vxc_uint8; +typedef uint16 vxc_uint16; + + +typedef float vxc_float; +typedef float2 vxc_float2; +typedef float4 vxc_float4; +typedef float8 vxc_float8; +typedef float16 vxc_float16; + + +typedef half vxc_half; +typedef _viv_half2_packed vxc_half2; +typedef _viv_half4_packed vxc_half4; +typedef _viv_half8_packed vxc_half8; +typedef struct _vxc_half16 +{ + vxc_half8 hi; + vxc_half8 lo; +} vxc_half16; + +typedef uint16 vxc_512bits; +typedef uint4 vxc_128bits; + +typedef vxc_512bits VXC_512Bits; +typedef vxc_128bits VXC_128Bits; +typedef vxc_modifier VXC_Modifier_t ; +typedef vxc_round_mode VXC_RoundMode; +typedef vxc_filter_mode VXC_FilterMode; + +#ifndef VX_USE_INTRINSIC +#define VX_USE_INTRINSIC 0 +#endif + +enum VXC_OP { + VXC_OP_abs_diff = 3, + VXC_OP_iadd, + VXC_OP_iacc_sq, + VXC_OP_lerp, + VXC_OP_filter, + VXC_OP_mag_phase, + VXC_OP_mul_shift, + VXC_OP_dp16x1, + VXC_OP_dp8x2, + VXC_OP_dp4x4, + VXC_OP_dp2x8, + VXC_OP_clamp, + VXC_OP_bi_linear, + VXC_OP_select_add, + VXC_OP_atomic_add, + VXC_OP_bit_extract, + VXC_OP_bit_replace, + VXC_OP_dp32x1, + VXC_OP_dp16x2, + VXC_OP_dp8x4, + VXC_OP_dp4x8, + VXC_OP_dp2x16, + VXC_OP_dp32x1_b, + VXC_OP_dp16x2_b, + VXC_OP_dp8x4_b, + VXC_OP_dp4x8_b, + VXC_OP_dp2x16_b, + VXC_OP_img_load, + VXC_OP_img_load_3d, + VXC_OP_img_store, + VXC_OP_img_store_3d, + VXC_OP_vload2, + VXC_OP_vload3, + VXC_OP_vload4, + VXC_OP_vload8, + VXC_OP_vload16, + VXC_OP_vstore2, + VXC_OP_vstore3, + VXC_OP_vstore4, + VXC_OP_vstore8, + VXC_OP_vstore16, + VXC_OP_index_add, + VXC_OP_vert_min3, + VXC_OP_vert_max3, + VXC_OP_vert_med3, + VXC_OP_horz_min3, + VXC_OP_horz_max3, + VXC_OP_horz_med3, + VXC_OP_error, + OP_bit_extract, + VXC_OP_dp16x1_b, + VXC_OP_dp8x2_b, + VXC_OP_dp4x4_b, + VXC_OP_dp2x8_b, + VXC_OP_gather, + VXC_OP_gather_b, + VXC_OP_scatter, + VXC_OP_scatter_b, + VXC_OP_atomic_s, + VXC_OP_atomic_s_b, +}; + +enum eVXC_ERROR +{ + ERROR_DP2x16_NOT_SUPPORTED, + ERROR_IADD_NOT_SUPPORTED, + ERROR_SELECTADD_NOT_SUPPORTED, + ERROR_BITREPLACE_NOT_SUPPORTED +}; + +#define VXC_OP1(Op, Dest, Src0) _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, Src0) + +#define VXC_OP2(Op, Dest, Src0, Src1) \ + do { \ + int _t1; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t1); \ + } while(0) + +#define VXC_OP3(Op, Dest, Src0, Src1, Src2) \ + do { \ + int _t1, _t2; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t2); \ + } while(0) + +#define VXC_OP3_NoDest(Op, Src0, Src1, Src2) \ + do { \ + int _t1, _t2, _t3; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(INTRINSIC_ST, _t3, VXC_OP_##Op, _t2); \ + } while(0) + + +#define VXC_OP4(Op, Dest, Src0, Src1, Src2, Src3) \ + do { \ + int _t1, _t2, _t3; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t3); \ + } while(0) + +#define VXC_OP4_NoDest(Op, Src0, Src1, Src2, Src3) \ + do { \ + int _t1, _t2, _t3, _t4; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(INTRINSIC_ST, _t4, VXC_OP_##Op, _t3); \ + } while(0) + +#define VXC_OP4_ST(Op, Dest, Src0, Src1, Src2, Src3) \ + do { \ + int _t1, _t2, _t3; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(INTRINSIC_ST, Dest, VXC_OP_##Op, _t3);\ + } while(0) + +#define VXC_OP5(Op, Dest, Src0, Src1, Src2, Src3, Src4) \ + do { \ + int _t1, _t2, _t3, _t4; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(PARAM_CHAIN, _t4, _t3, Src4); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t4); \ + } while(0) + +#define VXC_OP5_NoDest(Op, Src0, Src1, Src2, Src3, Src4) \ + do { \ + int _t1, _t2, _t3, _t4, _t5; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(PARAM_CHAIN, _t4, _t3, Src4); \ + _viv_asm(INTRINSIC_ST, _t5, VXC_OP_##Op, _t4); \ + } while(0) + + +#define VXC_5BITOFFSET_XY(offsetX, offsetY) ((((offsetY) & 0x1F) << 5) | ((offsetX) & 0x1F)) + + + +#if !VX_USE_INTRINSIC +#define VXC_AbsDiff(Dest, Src0, Src1, Info) VXC_OP3(abs_diff, Dest, Src0, Src1, Info) +#define VXC_IAccSq(Dest, Src0, Src1, Imm, Info) VXC_OP4(iacc_sq, Dest, Src0, Src1, Imm, Info) +#define VXC_Lerp(Dest, Src0, Src1, Src2, Info) VXC_OP4(lerp, Dest, Src0, Src1, Src2, Info) + +#define VXC_MulShift(Dest, Src0, Src1, Imm, Info) VXC_OP4(mul_shift, Dest, Src0, Src1, Imm, Info) +#define VXC_Clamp(Dest, Src0, Src1, Src2, Info) VXC_OP4(clamp, Dest, Src0, Src1, Src2, Info) +#define VXC_AtomicAdd(Dest, Base, Offset, Data, Info) VXC_OP4_ST(atomic_add, Dest, Base, Offset, Data, Info) +#define VXC_BitExtract(Dest, Src0, Src1, Src2, Info) VXC_OP4(bit_extract, Dest, Src0, Src1, Src2, Info) + +#define VXC_DP16x1(Dest, Src0, Src1, Info, U512) VXC_OP4(dp16x1, Dest, Src0, Src1, Info, U512) +#define VXC_DP8x2(Dest, Src0, Src1, Info, U512) VXC_OP4(dp8x2, Dest, Src0, Src1, Info, U512) +#define VXC_DP4x4(Dest, Src0, Src1, Info, U512) VXC_OP4(dp4x4, Dest, Src0, Src1, Info, U512) +#define VXC_DP2x8(Dest, Src0, Src1, Info, U512) VXC_OP4(dp2x8, Dest, Src0, Src1, Info, U512) + +#define VXC_DP32x1(Dest, Src0, Src1, Info, U512) VXC_OP4(dp32x1, Dest, Src0, Src1, Info, U512) +#define VXC_DP16x2(Dest, Src0, Src1, Info, U512) VXC_OP4(dp16x2, Dest, Src0, Src1, Info, U512) +#define VXC_DP8x4(Dest, Src0, Src1, Info, U512) VXC_OP4(dp8x4, Dest, Src0, Src1, Info, U512) +#define VXC_DP4x8(Dest, Src0, Src1, Info, U512) VXC_OP4(dp4x8, Dest, Src0, Src1, Info, U512) +#if (VX_VERSION >= 2) +#define VXC_DP2x16(Dest, Src0, Src1, Info, U512) VXC_OP1(error, ERROR_DP2x16_NOT_SUPPORTED) +#else +#define VXC_DP2x16(Dest, Src0, Src1, Info, U512) VXC_OP4(dp2x16, Dest, Src0, Src1, Info, U512) +#endif + +#if (VX_VERSION >= 2) + +#define VXC_DP16x1_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp16x1_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP8x2_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp8x2_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP4x4_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp4x4_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP2x8_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp2x8_b, Dest, Src0, Src1, Src2, Info, U512) +#endif + + +#define VXC_DP32x1_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp32x1_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP16x2_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp16x2_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP8x4_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp8x4_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP4x8_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp4x8_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP2x16_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp2x16_b, Dest, Src0, Src1, Src2, Info, U512) + +#define VXC_Gather(Dest, BaseAddr, Offsets, GatherInfo) VXC_OP3(gather, Dest, BaseAddr, Offsets, GatherInfo) +#define VXC_Gather_b(Dest, BaseAddr, Offsets, Offsets_b, GatherInfo) VXC_OP4(gather_b, Dest, BaseAddr, Offsets, Offsets_b, GatherInfo) + +#define VXC_Scatter(BaseAddr, Offsets, Data, ScatterInfo) VXC_OP4_NoDest(scatter, BaseAddr, Offsets, Data, ScatterInfo) +#define VXC_Scatter_b(BaseAddr, Offsets, Offsets_b, Data, ScatterInfo) VXC_OP5_NoDest(scatter_b, BaseAddr, Offsets, Offsets_b, Data, ScatterInfo) + +#define VXC_AtomicS(Dest, BaseAddr, Offsets, Data, AtomicSInfo) VXC_OP4(atomic_s, Dest, BaseAddr, Offsets, Data, AtomicSInfo) +#define VXC_AtomicS_b(Dest, BaseAddr, Offsets, Offsets_b, Data, AtomicSInfo) VXC_OP5(atomic_s_b, Dest, BaseAddr, Offsets, Offsets_b, Data, AtomicSInfo) + + + +#define VXC_ReadImage(Dest, Image, Coord, Offset, Info) VXC_OP4(img_load, Dest, Image, Coord, Offset, Info) +#define VXC_WriteImage(Image, Coord, Color, Info) VXC_OP4_NoDest(img_store, Image, Coord, Color, Info) + + +#define VXC_ReadImage2DArray(Dest, Image, Coord, Offset, Info) \ + do { \ + int8 desc; \ + _viv_asm(COPY, desc, Image, sizeof(desc)); \ + _viv_asm(CLAMP0MAX, (Coord).w, (Coord).z, desc.s5 - 1); \ + int baseAddr = (int)(Coord).w *desc.s4 + desc.s0; \ + _viv_asm(MOV, (Coord).w, baseAddr); \ + VXC_OP4(img_load_3d, Dest, Image, (Coord).xyww, Offset, Info); \ + } while (0) +#define VXC_WriteImage2DArray(Image, Coord, Color, Info) \ + do { \ + int8 desc; \ + _viv_asm(COPY, desc, Image, sizeof(desc)); \ + _viv_asm(CLAMP0MAX, (Coord).w, (Coord).z, desc.s5 - 1); \ + int baseAddr = (int)(Coord).w *(desc).s4 + desc.s0; \ + _viv_asm(MOV, (Coord).w, baseAddr); \ + VXC_OP4_NoDest(img_store_3d, Image, (Coord).xyww, Color, Info); \ + } while (0) + + +#define VXC_ReadImage3D(Dest, Image, Coord, Offset, Info) VXC_OP4(img_load_3d, Dest, Image, Coord, Offset, Info) +#define VXC_WriteImage3D(Image, Coord, Color, Info) VXC_OP4_NoDest(img_store_3d, Image, Coord, Color, Info) + +#define VXC_Vload2(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload2, Dest, Pointer, byteOffset); } while(0) +#define VXC_Vload4(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload4, Dest, Pointer, byteOffset); } while(0) +#define VXC_Vload8(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload8, Dest, Pointer, byteOffset); } while(0) +#define VXC_Vload16(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload16, Dest, Pointer, byteOffset); } while(0) + +#define VXC_Vstore2(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore2, Pointer, byteOffset, Data); } while(0) +#define VXC_Vstore4(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore4, Pointer, byteOffset, Data); } while(0) +#define VXC_Vstore8(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore8, Pointer, byteOffset, Data); } while(0) +#define VXC_Vstore16(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore16, Pointer, byteOffset, Data); } while(0) + + +#define VXC_IndexAdd(Dest, Src0, Src1, Src2, Info) VXC_OP4(index_add, Dest, Src0, Src1, Src2, Info) +#define VXC_VertMin3(Dest, Src0, Src1, Src2, Info) VXC_OP4(vert_min3, Dest, Src0, Src1, Src2, Info) +#define VXC_VertMax3(Dest, Src0, Src1, Src2, Info) VXC_OP4(vert_max3, Dest, Src0, Src1, Src2, Info) +#define VXC_VertMed3(Dest, Src0, Src1, Src2, Info) VXC_OP4(vert_med3, Dest, Src0, Src1, Src2, Info) +#define VXC_HorzMin3(Dest, Src0, Info) VXC_OP2(horz_min3, Dest, Src0, Info) +#define VXC_HorzMax3(Dest, Src0, Info) VXC_OP2(horz_max3, Dest, Src0, Info) +#define VXC_HorzMed3(Dest, Src0, Info) VXC_OP2(horz_med3, Dest, Src0, Info) + +#if (VX_VERSION == 2) +#define VXC_BiLinear(Dest, Src0, Src1, Src2, Info) \ + do { \ + int endBin = ((Info) & VXC_END_BIN_BITMASK) >> 8; \ + int roundMode = ((Info) & VXC_ROUNDING_MODE_BITMASK) >> 2; \ + int clamp = ((Info) & VXC_CLAMP_BITMASK) >> 22; \ + int mod1 = VXC_MODIFIER(0, endBin + 1, 0, roundMode, clamp); \ + int4 bitMask = { 0x00000000, 0x00000008, 0x00000010, 0x00000018}; \ + typeof (Dest) bi1; \ + uint4 bi2; \ + int bi3, bi4; \ + VXC_Lerp(bi1, Src0, Src1, (Src2).y, mod1); \ + _viv_asm(PARAM_CHAIN, bi3, bi1.x!, bitMask); \ + _viv_asm(PARAM_CHAIN, bi4, bi3, 8); \ + _viv_asm(INTRINSIC, bi2, OP_bit_extract, bi4); \ + VXC_Lerp(Dest, bi2!, bi2.y!, (Src2).x, Info); \ + } while (0) + +#define VXC_BitReplace(Dest, Src0, Src1, Src2, Info) +#define VXC_IAdd(Dest, Src0, Src1, Src2, Info) +#define VXC_MagPhase(Dest, Src0, Src1, Info) +#define VXC_SelectAdd(Dest, Src0, Src1, U512, Info) VXC_OP1(error, ERROR_SELECTADD_NOT_SUPPORTED) + +#define VXC_Filter_Box(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Guassian(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_SobelX(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_SobelY(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_ScharrX(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_ScharrY(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Max(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Min(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Median(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter(Dest, Src0, Src1, Src2, Info) do { \ + int filter = (((Info) >> 16)&0x0F); \ + if (filter == VXC_FM_BOX) { VXC_Filter_Box(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Guassian) { VXC_Filter_Guassian(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_SobelX) { VXC_Filter_SobelX(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_SobelY) { VXC_Filter_SobelY(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_ScharrX) { VXC_Filter_ScharrX(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_ScharrY) { VXC_Filter_ScharrY(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Max) { VXC_Filter_Max(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Min) { VXC_Filter_Min(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Median) { VXC_Filter_Median(Dest, Src0, Src1, Src2, Info); } \ + } while (0) + +#else + +#define VXC_BiLinear(Dest, Src0, Src1, Src2, Info) VXC_OP4(bi_linear, Dest, Src0, Src1, Src2, Info) +#define VXC_BitReplace(Dest, Src0, Src1, Src2, Info) VXC_OP4(bit_replace, Dest, Src0, Src1, Src2, Info) +#define VXC_IAdd(Dest, Src0, Src1, Src2, Info) VXC_OP4(iadd, Dest, Src0, Src1, Src2, Info) +#define VXC_MagPhase(Dest, Src0, Src1, Info) VXC_OP3(mag_phase, Dest, Src0, Src1, Info) +#define VXC_SelectAdd(Dest, Src0, Src1, U512, Info) VXC_OP4(select_add, Dest, Src0, Src1, U512, Info) +#define VXC_Filter(Dest, Src0, Src1, Src2, Info) VXC_OP4(filter, Dest, Src0, Src1, Src2, Info) +#endif + +#else + +#ifdef __cplusplus +extern "c" { +#endif + +#define viv_vx_api_only 0 + +#if viv_vx_api_only +#define _RET0_ ; +#define _RET_ ; +#define _EXT_ extern +#else +#define _RET0_ { return (0); } +#define _RET_ { return ; } +#define _EXT_ +#endif + + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_char16 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_char8 a) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_short8 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_short4 a) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_ushort8 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_half8 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_half4 a) _RET0_ + + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_uchar16 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_short8 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_short4 a) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_ushort8 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_half8 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_half4 a) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_uchar16 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_char16 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_char8 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_short8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_short4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_half8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_half4 a) _RET0_ + + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_uchar16 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_char16 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_char8 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_ushort8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_half8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_half4 a) _RET0_ + + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_uchar16 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_char16 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_char8 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_ushort8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_short8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_short4 a) _RET0_ + + + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastD_uc(vxc_char16 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_char8 a) _RET0_ + +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_short8 a) _RET0_ +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_short4 a) _RET0_ + +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_ushort8 a) _RET0_ +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_half8 a) _RET0_ +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_half4 a) _RET0_ + +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_int4 a) _RET0_ +_EXT_ vxc_uchar2 viv_intrinsic_vx_icastD_uc(vxc_int2 a) _RET0_ + +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_uint4 a) _RET0_ +_EXT_ vxc_uchar2 viv_intrinsic_vx_icastD_uc(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_char16 viv_intrinsic_vx_icastD_c(vxc_uchar16 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_short8 a) _RET0_ +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_short4 a) _RET0_ + +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_ushort8 a) _RET0_ +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_half8 a) _RET0_ +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_half4 a) _RET0_ + +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_int4 a) _RET0_ +_EXT_ vxc_char2 viv_intrinsic_vx_icastD_c(vxc_int2 a) _RET0_ + +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_uint4 a) _RET0_ +_EXT_ vxc_char2 viv_intrinsic_vx_icastD_c(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_uchar8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_uchar4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_char8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_char4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_short8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_short4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_half8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_half4 a) _RET0_ + +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_int4 a) _RET0_ +_EXT_ vxc_ushort2 viv_intrinsic_vx_icastD_us(vxc_int2 a) _RET0_ + +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_uint4 a) _RET0_ +_EXT_ vxc_ushort2 viv_intrinsic_vx_icastD_us(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_uchar8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_uchar4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_char8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_char4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_ushort8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_half8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_half4 a) _RET0_ + +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_int4 a) _RET0_ +_EXT_ vxc_short2 viv_intrinsic_vx_icastD_s(vxc_int2 a) _RET0_ + +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_uint4 a) _RET0_ +_EXT_ vxc_short2 viv_intrinsic_vx_icastD_s(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_uchar8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_uchar4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_char8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_char4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_ushort8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_short8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_short4 a) _RET0_ + +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_int4 a) _RET0_ +_EXT_ vxc_half2 viv_intrinsic_vx_icastD_h(vxc_int2 a) _RET0_ + +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_uint4 a) _RET0_ +_EXT_ vxc_half2 viv_intrinsic_vx_icastD_h(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_char4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_uchar4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_short4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_ushort4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_half4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_uint4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_float4 a) _RET0_ + +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_char2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_uchar2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_short2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_ushort2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_half2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_uint2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_float2 a) _RET0_ + + +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_char4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_uchar4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_short4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_ushort4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_half4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_int4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_float4 a) _RET0_ + +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_char2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_uchar2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_short2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_ushort2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_half2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_int2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_float2 a) _RET0_ + + +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_char4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_uchar4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_short4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_ushort4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_half4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_int4 a) _RET0_ + +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_char2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_uchar2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_short2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_ushort2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_half2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_int2 a) _RET0_ + + + +#define VXC_SWIZZLE_MASK8_ALL() 0xFFFF +#define VXC_SWIZZLE_MASK8(E0, E1, E2, E3, E4, E5, E6, E7) \ + (((E0) * 0x3) | ((E1) * (0x3 << 2)) | ((E2) * (0x3 << 4)) | \ + ((E3) * (0x3 << 6)) | ((E4) * (0x3 << 8)) | ((E5) * (0x3 << 10)) | \ + ((E6) * (0x3 << 12)) | ((E7) * (0x3 << 14)) ) + +#define VXC_SWIZZLE_MASK16_ALL() 0xFFFF +#define VXC_SWIZZLE_MASK16(E0, E1, E2, E3, E4, E5, E6, E7, E9, E10, E11, E12, E13, E14, E15) \ + (((E0) * 0x1) | ((E1) * (0x1 << 1)) | ((E2) * (0x1 << 2)) | \ + ((E3) * (0x1 << 1)) | ((E4) * (0x1 << 4)) | ((E5) * (0x1 << 5)) | \ + ((E6) * (0x1 << 6)) | ((E7) * (0x1 << 7)) | ((E8) * (0x1 << 8)) | \ + ((E9) * (0x1 << 9)) | ((E10) * (0x1 << 10)) | ((E11) * (0x1 << 11)) | \ + ((E12) * (0x1 << 12)) | ((E13) * (0x1 << 13)) | ((E14) * (0x1 << 14)) | \ + ((E15) * (0x1 << 15)) ) + +#define VXC_SWIZZLE8(S0, S1, S2, S3, S4, S5, S6, S7) \ + (uint)((S0) << 0 | (S1) << 4 | (S2) << 8 | (S3) << 12 | \ + (S4) << 16 | (S5) << 20 | (S6) << 24 | (S7) << 28 ) + + +_EXT_ vxc_char16 viv_intrinsic_vx_read_imagec (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_read_imageuc (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_read_images (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_read_imageus (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vx_read_imageh (image2d_t image, int2 coord) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_read_imagec (image1d_t image, int coord) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_read_imageuc (image1d_t image, int coord) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_read_images (image1d_t image, int coord) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_read_imageus (image1d_t image, int coord) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vx_read_imageh (image1d_t image, int coord) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_read_imagec (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_read_imageuc (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_read_images (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_read_imageus (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vx_read_imageh (image1d_array_t image, int2 coord) _RET0_ + + +_EXT_ void viv_intrinsic_vx_write_imagec (image2d_t image, int2 coord, vxc_char16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageuc (image2d_t image, int2 coord, vxc_uchar16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_images (image2d_t image, int2 coord, vxc_short8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageus (image2d_t image, int2 coord, vxc_ushort8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageh (image2d_t image, int2 coord, vxc_half8 color) _RET_ + +_EXT_ void viv_intrinsic_vx_write_imagec (image1d_t image, int coord, vxc_char16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageuc (image1d_t image, int coord, vxc_uchar16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_images (image1d_t image, int coord, vxc_short8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageus (image1d_t image, int coord, vxc_ushort8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageh (image1d_t image, int coord, vxc_half8 color) _RET_ + +_EXT_ void viv_intrinsic_vx_write_imagec (image1d_array_t image, int2 coord, vxc_char16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageuc (image1d_array_t image, int2 coord, vxc_uchar16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_images (image1d_array_t image, int2 coord, vxc_short8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageus (image1d_array_t image, int2 coord, vxc_ushort8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageh (image1d_array_t image, int2 coord, vxc_half8 color) _RET_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_AbsDiff_uc(vxc_uchar16 a, vxc_uchar16 b) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_AbsDiff_c(vxc_char16 a, vxc_char16 b) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_AbsDiff_s(vxc_short8 a, vxc_short8 b) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_AbsDiff_us(vxc_ushort8 a, vxc_ushort8 b) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_IAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_IAdd_c(vxc_char16 a, vxc_char16 b, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_IAdd_s(vxc_short8 a, vxc_short8 b, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_IAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_IAccSq_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_IAccSq_c(vxc_char16 a, vxc_char16 b, uint Imm) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_IAccSq_s(vxc_short8 a, vxc_short8 b, uint Imm) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_IAccSq_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_Lerp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_float c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_Lerp_c(vxc_char16 a, vxc_char16 b, vxc_float c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_Lerp_s(vxc_short8 a, vxc_short8 b, vxc_float c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_Lerp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_float c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_Filter_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_filter_mode f) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_Filter_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_filter_mode f) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_Filter_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_filter_mode f) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_Filter_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_filter_mode f) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_MagPhase_uc(vxc_uchar16 a, vxc_uchar16 b) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_MagPhase_c(vxc_char16 a, vxc_char16 b) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_MagPhase_s(vxc_short8 a, vxc_short8 b) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_MagPhase_us(vxc_ushort8 a, vxc_ushort8 b) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_MulShift_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_MulShift_c(vxc_char16 a, vxc_char16 b, uint Imm) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_MulShift_s(vxc_short8 a, vxc_short8 b, uint Imm) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_MulShift_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm) _RET0_ + +/* Clamp: clamps up to 16 values to a min and.or max value + * + * Syntax: + * r = Clamp(a, b, c) ; + * r = ClampBoolean(a, b, c) ; // boolean mode + * Semantics: + * r[i] = clamp(a[i], b[i], c[i]) ; i E [0, elem(r) ) + * + * In boolean mode it will write a 0 in the result if the value + * is inside the specified min/max range, otherwise all 1s will + * be written to the result. + */ +_EXT_ vxc_uchar16 viv_intrinsic_vx_Clamp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_Clamp_c(vxc_char16 a, vxc_char16 b, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_Clamp_s(vxc_short8 a, vxc_short8 b, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_Clamp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_ClampBoolean_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_ClampBoolean_c(vxc_char16 a, vxc_char16 b, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_ClampBoolean_s(vxc_short8 a, vxc_short8 b, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_ClampBoolean_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_BiLinear_uc(vxc_uchar16 a, vxc_uchar16 b, float2 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_BiLinear_c(vxc_char16 a, vxc_char16 b, float2 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_BiLinear_s(vxc_short8 a, vxc_short8 b, float2 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_BiLinear_us(vxc_ushort8 a, vxc_ushort8 b, float2 c) _RET0_ + + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_SelectAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_512bits c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_SelectAdd_c(vxc_char16 a, vxc_char16 b, vxc_512bits c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_SelectAdd_s(vxc_short8 a, vxc_short8 b, vxc_512bits c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_SelectAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_512bits c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_AtomicAdd_uc(vxc_uchar16 * a, vxc_int offset, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_AtomicAdd_c(vxc_char16 * a, vxc_int offset, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_AtomicAdd_s(vxc_short8 * a, vxc_int offset, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_AtomicAdd_us(vxc_ushort8 * a, vxc_int offset, vxc_ushort8 c) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_BitExtract_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_BitExtract_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_BitReplace_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_BitReplace_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ + + + + + + +_EXT_ vxc_char16 viv_intrinsic_vxmc_read_imagec (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_read_imageuc (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_read_images (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_read_imageus (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_read_imageh (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vxmc_read_imagec (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_read_imageuc (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_read_images (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_read_imageus (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_read_imageh (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vxmc_read_imagec (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_read_imageuc (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_read_images (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_read_imageus (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_read_imageh (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ + + +_EXT_ void viv_intrinsic_vxmc_write_imagec (image2d_t image, int2 coord, vxc_char16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageuc (image2d_t image, int2 coord, vxc_uchar16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_images (image2d_t image, int2 coord, vxc_short8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageus (image2d_t image, int2 coord, vxc_ushort8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageh (image2d_t image, int2 coord, vxc_half8 color, vxc_modifier modifier) _RET_ + +_EXT_ void viv_intrinsic_vxmc_write_imagec (image1d_t image, int coord, vxc_char16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageuc (image1d_t image, int coord, vxc_uchar16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_images (image1d_t image, int coord, vxc_short8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageus (image1d_t image, int coord, vxc_ushort8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageh (image1d_t image, int coord, vxc_half8 color, vxc_modifier modifier) _RET_ + +_EXT_ void viv_intrinsic_vxmc_write_imagec (image1d_array_t image, int2 coord, vxc_char16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageuc (image1d_array_t image, int2 coord, vxc_uchar16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_images (image1d_array_t image, int2 coord, vxc_short8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageus (image1d_array_t image, int2 coord, vxc_ushort8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageh (image1d_array_t image, int2 coord, vxc_half8 color, vxc_modifier modifier) _RET_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_AbsDiff_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_AbsDiff_c(vxc_char16 a, vxc_char16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_AbsDiff_s(vxc_short8 a, vxc_short8 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_AbsDiff_us(vxc_ushort8 a, vxc_ushort8 b, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_IAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_IAdd_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_IAdd_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_IAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_IAccSq_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_IAccSq_c(vxc_char16 a, vxc_char16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_IAccSq_s(vxc_short8 a, vxc_short8 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_IAccSq_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_Lerp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_float c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_Lerp_c(vxc_char16 a, vxc_char16 b, vxc_float c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_Lerp_s(vxc_short8 a, vxc_short8 b, vxc_float c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_Lerp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_float c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_Filter_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_Filter_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_Filter_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_Filter_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_MagPhase_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_MagPhase_c(vxc_char16 a, vxc_char16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_MagPhase_s(vxc_short8 a, vxc_short8 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_MagPhase_us(vxc_ushort8 a, vxc_ushort8 b, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_MulShift_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_MulShift_c(vxc_char16 a, vxc_char16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_MulShift_s(vxc_short8 a, vxc_short8 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_MulShift_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uint viv_intrinsic_vxmc_DP16x1(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int viv_intrinsic_vxmc_DP16x1(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_float viv_intrinsic_vxmc_DP16x1(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint2 viv_intrinsic_vxmc_DP8x2(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vxmc_DP8x2(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vxmc_DP8x2(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint4 viv_intrinsic_vxmc_DP4x4(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vxmc_DP4x4(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vxmc_DP4x4(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uchar8 viv_intrinsic_vxmc_DP2x8(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vxmc_DP2x8(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_DP2x8(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint viv_intrinsic_vxmc_DP32x1(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int viv_intrinsic_vxmc_DP32x1(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint2 viv_intrinsic_vxmc_DP16x2(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vxmc_DP16x2(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint4 viv_intrinsic_vxmc_DP8x4(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vxmc_DP8x4(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_DP4x8(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_DP4x8(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_DP2x16(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_DP2x16(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint viv_intrinsic_vxmc_DP32x1_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int viv_intrinsic_vxmc_DP32x1_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint2 viv_intrinsic_vxmc_DP16x2_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vxmc_DP16x2_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint4 viv_intrinsic_vxmc_DP8x4_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vxmc_DP8x4_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_DP4x8_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_DP4x8_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_DP2x16_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_DP2x16_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_Clamp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_Clamp_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_Clamp_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_Clamp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_BiLinear_uc(vxc_uchar16 a, vxc_uchar16 b, float2 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_BiLinear_c(vxc_char16 a, vxc_char16 b, float2 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_BiLinear_s(vxc_short8 a, vxc_short8 b, float2 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_BiLinear_us(vxc_ushort8 a, vxc_ushort8 b, float2 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_SelectAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_512bits c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_SelectAdd_c(vxc_char16 a, vxc_char16 b, vxc_512bits c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_SelectAdd_s(vxc_short8 a, vxc_short8 b, vxc_512bits c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_SelectAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_512bits c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_AtomicAdd_uc(vxc_uchar16 * a, vxc_int offset, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_AtomicAdd_c(vxc_char16 * a, vxc_int offset, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_AtomicAdd_s(vxc_short8 * a, vxc_int offset, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_AtomicAdd_us(vxc_ushort8 * a, vxc_int offset, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_BitExtract_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vxmc_BitExtract_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_BitReplace_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_BitReplace_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ + + +vxc_char2 viv_intrinsic_vx_vload2(size_t Offset, char *Pointer) { + vxc_char2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_char2)); + return dest; +} + +vxc_char4 viv_intrinsic_vx_vload4(size_t Offset, char *Pointer) { + vxc_char4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_char4)); + return dest; +} + +vxc_char8 viv_intrinsic_vx_vload8(size_t Offset, char *Pointer) { + vxc_char8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_char8)); + return dest; +} + +vxc_char16 viv_intrinsic_vx_vload16(size_t Offset, char *Pointer) { + vxc_char16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_char16)); + return dest; +} + +vxc_uchar2 viv_intrinsic_vx_vload2(size_t Offset, uchar *Pointer) { + vxc_uchar2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_uchar2)); + return dest; +} + +vxc_uchar4 viv_intrinsic_vx_vload4(size_t Offset, uchar *Pointer) { + vxc_uchar4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_uchar4)); + return dest; +} + +vxc_uchar8 viv_intrinsic_vx_vload8(size_t Offset, uchar *Pointer) { + vxc_uchar8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_uchar8)); + return dest; +} + +vxc_uchar16 viv_intrinsic_vx_vload16(size_t Offset, uchar *Pointer) { + vxc_uchar16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_uchar16)); + return dest; +} + +vxc_short2 viv_intrinsic_vx_vload2(size_t Offset, short *Pointer) { + vxc_short2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_short2)); + return dest; +} + +vxc_short4 viv_intrinsic_vx_vload4(size_t Offset, short *Pointer) { + vxc_short4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_short4)); + return dest; +} + +vxc_short8 viv_intrinsic_vx_vload8(size_t Offset, short *Pointer) { + vxc_short8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_short8)); + return dest; +} + +vxc_short16 viv_intrinsic_vx_vload16(size_t Offset, short *Pointer) { + vxc_short16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_short16)); + return dest; +} + +vxc_ushort2 viv_intrinsic_vx_vload2(size_t Offset, ushort *Pointer) { + vxc_ushort2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_ushort2)); + return dest; +} + +vxc_ushort4 viv_intrinsic_vx_vload4(size_t Offset, ushort *Pointer) { + vxc_ushort4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_ushort4)); + return dest; +} + +vxc_ushort8 viv_intrinsic_vx_vload8(size_t Offset, ushort *Pointer) { + vxc_ushort8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_ushort8)); + return dest; +} + +vxc_ushort16 viv_intrinsic_vx_vload16(size_t Offset, ushort *Pointer) { + vxc_ushort16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_ushort16)); + return dest; +} + +vxc_half2 viv_intrinsic_vx_vload2(size_t Offset, half *Pointer) { + vxc_half2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_half2)); + return dest; +} + +vxc_half4 viv_intrinsic_vx_vload4(size_t Offset, half *Pointer) { + vxc_half4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_half4)); + return dest; +} + +vxc_half8 viv_intrinsic_vx_vload8(size_t Offset, half *Pointer) { + vxc_half8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_half8)); + return dest; +} + +vxc_half16 viv_intrinsic_vx_vload16(size_t Offset, half *Pointer) { + vxc_half16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_half16)); + return dest; +} + + +void viv_intrinsic_vx_vstore2(vxc_char2 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_char2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_char4 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_char4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_char8 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_char8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_char16 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_char16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_uchar2 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_uchar2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_uchar4 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_uchar4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_uchar8 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_uchar8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_uchar16 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_uchar16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_short2 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_short2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_short4 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_short4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_short8 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_short8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_short16 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_short16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_ushort2 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_ushort2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_ushort4 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_ushort4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_ushort8 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_ushort8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_ushort16 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_ushort16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_half2 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_half2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_half4 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_half4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_half8 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_half8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_half16 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_half16), Data); +} + +#undef _RET0_ +#undef _RET_ +#undef _EXT_ + +#ifdef __cplusplus +} +#endif + +#endif +typedef struct +{ + size_t size; + global char* item; +} vx_array_char; + +typedef struct +{ + size_t size; + global unsigned char* item; +} vx_array_uchar; + +typedef struct +{ + size_t size; + global short* item; +} vx_array_short; + +typedef struct +{ + size_t size; + global unsigned short* item; +} vx_array_ushort; + +typedef struct +{ + size_t size; + global int* item; +} vx_array_int; + +typedef struct +{ + size_t size; + global unsigned int* item; +} vx_array_uint; + + +typedef struct +{ + size_t size; + global float * item; +} vx_array_float; + +typedef struct +{ + size_t size; + global unsigned char* item; +} vx_lut_uchar; + +typedef struct +{ + size_t size; + global unsigned short* item; +} vx_lut_ushort; + +typedef struct +{ + size_t columns; + size_t rows; + global short* matrix; + uint scale; +} vx_convolution; + +typedef struct +{ + size_t columns; + size_t rows; + global char* matrix; +} vx_matrix_char; + +typedef struct +{ + size_t columns; + size_t rows; + global unsigned char* matrix; +} vx_matrix_uchar; + +typedef struct +{ + size_t columns; + size_t rows; + global short* matrix; +} vx_matrix_short; + +typedef struct +{ + size_t columns; + size_t rows; + global unsigned short* matrix; +} vx_matrix_ushort; + +typedef struct +{ + size_t columns; + size_t rows; + global int* matrix; +} vx_matrix_int; + +typedef struct +{ + size_t columns; + size_t rows; + global unsigned int* matrix; +} vx_matrix_uint; + +typedef struct +{ + size_t columns; + size_t rows; + global float* matrix; +} vx_matrix_float; + +typedef struct +{ + int type; + uint value; + uint lower; + uint upper; + uint trueValue; + uint falseValue; +} vx_threshold; + +typedef struct { + int dst_width; + int dst_height; + global float* ptr; +} vx_remap; + +typedef struct +{ + int bins; + int rang; + int offset; + float window_r; + global int* ptr; +} vx_distribution; + +typedef struct _vxc_pyramid +{ + float scale; + uint width; + uint height; + uint format; + uint levelCount; + _viv_image2d_array_t imageArray; +} vxc_pyramid; + +#endif + +#endif diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libArchModelSw.so b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..c26c2bc Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libGAL.so b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libGAL.so new file mode 100644 index 0000000..4c5fdaa Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libGAL.so differ diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libNNArchPerf.so b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..816a76e Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..687ebe9 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libOpenVXU.so b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..8a1185c Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libVSC_Lite.so b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libVSC_Lite.so new file mode 100644 index 0000000..22d0cc3 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/libVSC_Lite.so differ diff --git a/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/librknn_runtime.so b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..1264ce3 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64-mini/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/bin/rknn_server b/libs/rklibs/drivers/linux-aarch64/usr/bin/rknn_server new file mode 100644 index 0000000..fb5ab85 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/bin/rknn_server differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libArchModelSw.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..c26c2bc Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libCLC.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libCLC.so new file mode 100644 index 0000000..100c901 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libCLC.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libGAL.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libGAL.so new file mode 100644 index 0000000..87da367 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libGAL.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNArchPerf.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..816a76e Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNGPUBinary.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNGPUBinary.so new file mode 100644 index 0000000..8987d4d Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNGPUBinary.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNVXCBinary.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNVXCBinary.so new file mode 100644 index 0000000..0793f10 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libNNVXCBinary.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenCL.so.1.2 b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenCL.so.1.2 new file mode 100644 index 0000000..94ce057 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenCL.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..23f5ae6 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenVXU.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..8a1185c Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libOvx12VXCBinary.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOvx12VXCBinary.so new file mode 100644 index 0000000..fbdc57d Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libOvx12VXCBinary.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libVSC.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libVSC.so new file mode 100644 index 0000000..ed6e52a Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libVSC.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/libneuralnetworks.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/libneuralnetworks.so new file mode 100644 index 0000000..ea9768d Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/libneuralnetworks.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/librknn_runtime.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..a207e3f Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/memory_profile b/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/memory_profile new file mode 100644 index 0000000..c8d125b Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/memory_profile differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/libann_plugin.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/libann_plugin.so new file mode 100644 index 0000000..269d25a Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/libann_plugin.so differ diff --git a/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/librknn_plugin.so b/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/librknn_plugin.so new file mode 100644 index 0000000..27977c6 Binary files /dev/null and b/libs/rklibs/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/librknn_plugin.so differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libArchModelSw.so b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libGAL.so b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libGAL.so new file mode 100644 index 0000000..fe54bb2 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libGAL.so differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libNNArchPerf.so b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..d3e8586 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libOpenVXU.so b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..ccae677 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libVSC_Lite.so b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libVSC_Lite.so new file mode 100644 index 0000000..041deca Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/libVSC_Lite.so differ diff --git a/libs/rklibs/drivers/linux-armhf-mini/usr/lib/librknn_runtime.so b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..b2c409b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-mini/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libArchModelSw.so b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libGAL.so b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libGAL.so new file mode 100644 index 0000000..669fadd Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libGAL.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libNNArchPerf.so b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..44c4399 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libOpenVXU.so b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..40c62f4 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libVSC_Lite.so b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libVSC_Lite.so new file mode 100644 index 0000000..041deca Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/libVSC_Lite.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/librknn_runtime.so b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..f2c5c5f Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma-mini/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/bin/rknn_server b/libs/rklibs/drivers/linux-armhf-puma/usr/bin/rknn_server new file mode 100644 index 0000000..1ff3313 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/bin/rknn_server differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libArchModelSw.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libCLC.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libCLC.so new file mode 100644 index 0000000..642b34b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libCLC.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libGAL.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libGAL.so new file mode 100644 index 0000000..7f5902c Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libGAL.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNArchPerf.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNGPUBinary.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNGPUBinary.so new file mode 100644 index 0000000..c43c374 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNGPUBinary.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNVXCBinary.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNVXCBinary.so new file mode 100644 index 0000000..2e20033 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libNNVXCBinary.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenCL.so.1.2 b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenCL.so.1.2 new file mode 100644 index 0000000..e6c22e1 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenCL.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..cb0ef11 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenVXU.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..40c62f4 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOvx12VXCBinary.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOvx12VXCBinary.so new file mode 100644 index 0000000..ffb6711 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libOvx12VXCBinary.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libVSC.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libVSC.so new file mode 100644 index 0000000..639013d Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libVSC.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libneuralnetworks.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libneuralnetworks.so new file mode 100644 index 0000000..4afe4f3 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/libneuralnetworks.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/librknn_runtime.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..3576099 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/memory_profile b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/memory_profile new file mode 100644 index 0000000..542fae6 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/memory_profile differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/libann_plugin.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/libann_plugin.so new file mode 100644 index 0000000..da693e0 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/libann_plugin.so differ diff --git a/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/librknn_plugin.so b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/librknn_plugin.so new file mode 100644 index 0000000..8d675de Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/librknn_plugin.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/bin/rknn_server b/libs/rklibs/drivers/linux-armhf/usr/bin/rknn_server new file mode 100644 index 0000000..3731a46 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/bin/rknn_server differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libArchModelSw.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libCLC.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libCLC.so new file mode 100644 index 0000000..642b34b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libCLC.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libGAL.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libGAL.so new file mode 100644 index 0000000..109e22a Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libGAL.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libNNArchPerf.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libNNGPUBinary.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libNNGPUBinary.so new file mode 100644 index 0000000..609e6a2 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libNNGPUBinary.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libNNVXCBinary.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libNNVXCBinary.so new file mode 100644 index 0000000..f7c1460 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libNNVXCBinary.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenCL.so.1.2 b/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenCL.so.1.2 new file mode 100644 index 0000000..73e5bfe Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenCL.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..e896ca1 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenVXU.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..ccae677 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libOvx12VXCBinary.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libOvx12VXCBinary.so new file mode 100644 index 0000000..ac8f547 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libOvx12VXCBinary.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libVSC.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libVSC.so new file mode 100644 index 0000000..639013d Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libVSC.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/libneuralnetworks.so b/libs/rklibs/drivers/linux-armhf/usr/lib/libneuralnetworks.so new file mode 100644 index 0000000..2266108 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/libneuralnetworks.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/librknn_runtime.so b/libs/rklibs/drivers/linux-armhf/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..a70f988 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/memory_profile b/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/memory_profile new file mode 100644 index 0000000..04dce87 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/memory_profile differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/plugins/libann_plugin.so b/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/plugins/libann_plugin.so new file mode 100644 index 0000000..a3f8ba4 Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/plugins/libann_plugin.so differ diff --git a/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/plugins/librknn_plugin.so b/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/plugins/librknn_plugin.so new file mode 100644 index 0000000..b1f81ec Binary files /dev/null and b/libs/rklibs/drivers/linux-armhf/usr/lib/npu/rknn/plugins/librknn_plugin.so differ diff --git a/libs/rklibs/drivers/npu_ko/galcore.ko b/libs/rklibs/drivers/npu_ko/galcore.ko new file mode 100644 index 0000000..8ebbbaa Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore.ko differ diff --git a/libs/rklibs/drivers/npu_ko/galcore_fedora.ko b/libs/rklibs/drivers/npu_ko/galcore_fedora.ko new file mode 100644 index 0000000..1a51915 Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore_fedora.ko differ diff --git a/libs/rklibs/drivers/npu_ko/galcore_puma.ko b/libs/rklibs/drivers/npu_ko/galcore_puma.ko new file mode 100644 index 0000000..4644e82 Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore_puma.ko differ diff --git a/libs/rklibs/drivers/npu_ko/galcore_puma_tb.ko b/libs/rklibs/drivers/npu_ko/galcore_puma_tb.ko new file mode 100644 index 0000000..1bebce6 Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore_puma_tb.ko differ diff --git a/libs/rklibs/drivers/npu_ko/galcore_rk1806.ko b/libs/rklibs/drivers/npu_ko/galcore_rk1806.ko new file mode 100644 index 0000000..f69273f Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore_rk1806.ko differ diff --git a/libs/rklibs/drivers/npu_ko/galcore_rk3399pro-npu-pcie.ko b/libs/rklibs/drivers/npu_ko/galcore_rk3399pro-npu-pcie.ko new file mode 100644 index 0000000..3c69e5e Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore_rk3399pro-npu-pcie.ko differ diff --git a/libs/rklibs/drivers/npu_ko/galcore_rk3399pro-npu.ko b/libs/rklibs/drivers/npu_ko/galcore_rk3399pro-npu.ko new file mode 100644 index 0000000..f694c17 Binary files /dev/null and b/libs/rklibs/drivers/npu_ko/galcore_rk3399pro-npu.ko differ diff --git a/libs/rklibs/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_CN.pdf b/libs/rklibs/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_CN.pdf new file mode 100644 index 0000000..9e80b4b --- /dev/null +++ b/libs/rklibs/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_CN.pdf @@ -0,0 +1,1290 @@ + 瑞芯微电子股份有限公司 + +密级状态:绝密( ) 秘密( ) 内部( ) 公开(√ ) + +Rockchip User Guide RKNN API + + (技术部,图形计算平台中心) + +文件状态: 当前版本: 1.7.3 + HPC +[ ] 正在修改 作 者: 2022-8-13 + 熊伟 +[√] 正式发布 完成日期: 2022-8-13 + + 审 核: + + 完成日期: + + 瑞芯微电子股份有限公司 + Rockchips Semiconductor Co . , Ltd + + (版本所有,翻版必究) + 瑞芯微电子股份有限公司 + + 更新记录 + + 版本 修改人 修改日期 修改说明 核定人 +v0.9.6 杨华聪 +V0.9.7 杨华聪 2018-12-20 初始版本 熊伟 +V0.9.8 杨华聪 熊伟 + 2019-01-16 1) 添加 RKNN SDK 开发流程说明 卓鸿添 +V1.3.3 卓鸿添 2019-06-05 2) 添加 RKNN Python API 说明 卓鸿添 +V1.4.0 卓鸿添 2020-06-02 1) 添加 RKNN API 库的说明 卓鸿添 + 2) 更新 rknn example 和 rknn-toolkit 路 +v1.6.0 洪启飞 卓鸿添 + 径 +v1.6.1 洪启飞 3) 修正文档中错误 卓鸿添 +v1.7.0 洪启飞 1)支持 RV1126/RV1109 +v1.7.1 洪启飞 2)删除 python 支持说明,关于 python +v1.7.3 洪启飞 支持见 RKNN Toolkit Lite 相关文档。 + + 2020-09-10 1)增加错误码 + + 2021-1-15 1)增加 Matmul 高级 API 说明 + 2)增加一组设置输入和一组获取输出 + 的接口 + 3)增加做量化和反量化的算法说明 + 4)增加 NPU 驱动说明 + 5)增加 FAQ + + 2021-4-26 1)更新 Matmul 高级 API 说明 + + 2021-8-5 1)更新版本号 卓鸿添 + 2021-12-2 1)rknn_inputs_set 接口单通道输入性能 卓鸿添 + 2022-8-13 优化 卓鸿添 + 2)已知 Bug 修复 + 1)更新版本号 + + 1 + 瑞芯微电子股份有限公司 + +1 主要功能说明 ................................................................................................................................................... 5 +2 硬件平台 ........................................................................................................................................................... 5 +3 使用说明 ........................................................................................................................................................... 5 + + 3.1 RKNN SDK 开发流程 ............................................................................................................................... 5 + 3.2 RKNN C API ...............................................................................................................................................6 + + 3.2.1 RKNN API 库 ...................................................................................................................................... 6 + 3.2.2 EXAMPLE 使用说明 ..........................................................................................................................6 + 3.2.3 API 流程说明 ...................................................................................................................................... 7 + + 3.2.3.1 API 内部处理流程 ..........................................................................................................................................9 + 3.2.3.2 量化和反量化 ..............................................................................................................................................10 + 3.2.3.3 零拷贝 .......................................................................................................................................................... 11 + 3.2.4 API 详细说明 .................................................................................................................................... 12 + 3.2.4.1 rknn_init ........................................................................................................................................................ 12 + 3.2.4.2 rknn_destroy ..................................................................................................................................................13 + 3.2.4.3 rknn_query .................................................................................................................................................... 13 + 3.2.4.4 rknn_inputs_set ............................................................................................................................................. 16 + 3.2.4.5 rknn_inputs_map ...........................................................................................................................................16 + 3.2.4.6 rknn_inputs_sync ...........................................................................................................................................18 + 3.2.4.7 rknn_inputs_unmap .......................................................................................................................................18 + 3.2.4.8 rknn_run ........................................................................................................................................................ 19 + 3.2.4.9 rknn_outputs_get ...........................................................................................................................................19 + 3.2.4.10 rknn_outputs_release .................................................................................................................................. 20 + 3.2.4.11 rknn_outputs_map ....................................................................................................................................... 21 + 3.2.4.12 rknn_outputs_sync .......................................................................................................................................21 + + 2 + 瑞芯微电子股份有限公司 + 3.2.4.13 rknn_outputs_unmap ...................................................................................................................................22 + 3.2.5 RKNN 数据结构定义 ....................................................................................................................... 22 + 3.2.5.1 rknn_input_output_num ................................................................................................................................ 22 + 3.2.5.2 rknn_tensor_attr ............................................................................................................................................23 + 3.2.5.3 rknn_input ..................................................................................................................................................... 24 + 3.2.5.4 rknn_tensor_mem ..........................................................................................................................................24 + 3.2.5.5 rknn_output ................................................................................................................................................... 25 + 3.2.5.6 rknn_perf_detail ............................................................................................................................................ 25 + 3.2.5.7 rknn_sdk_version .......................................................................................................................................... 25 + 3.2.6 RKNN 返回值错误码 ....................................................................................................................... 26 +4 高级 API 使用说明 ........................................................................................................................................ 27 + 4.1 MATMUL 算子库 ........................................................................................................................................27 + 4.1.1 简介 ...................................................................................................................................................27 + 4.1.2 数据结构定义 ...................................................................................................................................27 + 4.1.3 详细 API 说明 .................................................................................................................................. 28 + 4.1.3.1 rknn_matmul_load ........................................................................................................................................ 28 + 4.1.3.2 rknn_matmul_run ..........................................................................................................................................28 + 4.1.3.3 rknn_matmul_unload .................................................................................................................................... 29 + 4.1.4 实现限制 ...........................................................................................................................................29 + 4.1.4.1 维度限制 ......................................................................................................................................................29 + 4.1.4.2 输入数据类型限制 ......................................................................................................................................30 + 4.1.5 基准测试 ...........................................................................................................................................30 +5 NPU 驱动说明 .................................................................................................................................................31 + 5.1.1 NPU 驱动目录说明 ...........................................................................................................................31 + 5.1.2 NPU full driver 与 mini driver 的区别 ..............................................................................................31 + + 3 + 瑞芯微电子股份有限公司 +6 FAQ ..................................................................................................................................................................32 + + 6.1.1 输入输出数据格式问题 ...................................................................................................................32 + 6.1.2 输入输出接口使用问题 ...................................................................................................................33 + 6.1.3 API 调用流程问题 ............................................................................................................................ 33 + 6.1.4 性能问题 ...........................................................................................................................................34 + + 4 + 瑞芯微电子股份有限公司 + +1 主要功能说明 + + RKNN SDK 为 RK1808 等 带 有 NPU 的 平 台 提 供 编 程 接 口 , 能 够 帮 助 用 户 部 署 使 用 +RKNN-Toolkit 导出的 RKNN 模型,加速 AI 应用的落地。 + +2 硬件平台 + +本文档适用如下硬件平台: + 1) RK1808、RK1806 + 2) RV1126、RV1109 + +注意:本文档不适用 RK3399Pro +下面的说明以 RK1808 为例,也同时适用上述其他平台。 + +3 使用说明 + +3.1 RKNN SDK 开发流程 + + 在使用 RKNN SDK 之前,用户首先需要使用 RKNN-Toolkit 工具将用户的模型转换为 RKNN + +模型,用户可以在 https://github.com/rockchip-linux/rknn-toolkit 获取工具的完整 + +安装包及使用文档。 + 成功转换生成 RKNN 模型之后,用户可以先通过 RKNN-Toolkit 连接 RK1808 等开发板进行联 + +机调试,确保模型的精度性能符合要求。 + 得到 RKNN 模型文件之后,用户可以选择使用 C 或 Python 接口在 RK1808 等平台开发应用, + +后续章节将说明如何在 RK1808 等平台上基于 RKNN SDK 进行开发。 + + 5 + 瑞芯微电子股份有限公司 + +3.2 RKNN C API + +3.2.1 RKNN API 库 + + RKNN SDK 所提供的库和头文件位于/external/rknpu/rknn/ +rknn_api/librknn_api 目录下,开发者可以在自己应用中引用即可开发应用。 + + 需要注意的是,RK1808 和 RK3399Pro 平台的 RKNN API 是兼容的,两者开发的应用程序 +可以很方便地移植。但是使用过程中需要注意要区分两个平台的 librknn_api.so,如果开发者使 +用 RK3399Pro 的 librknn_api.so 将无法在 RK1808 平台上运行。开发者可以使用以下方法来区分 +librknn_api.so 的平台: + + $ strings librknn_api.so |grep version + librknn_api version 1.7.3 (cf7f05f build: 2022-08-13 10:59:33) + +3.2.2 EXAMPLE 使用说明 + + SDK 提供了 Linux 平台的 MobileNet 图像分类、MobileNet SSD 目标检测以及 YoloV3 Tiny + 目标检测 Demo。这些 Demo 能够为客户基于 RKNN SDK 开发自己的 AI 应用提供参考。Demo + 代码位于/external/rknpu/rknn/rknn_api/examples 目录。下面以 rknn_mobilenet_demo 为例 + 来讲解如何快速上手运行。 + + 1) 编译 Demo + + cd examples/rknn_mobilenet_demo + # 修改 build.sh 中的 GCC_COMPILER,指向目标平台的编译器 + ./build.sh + 2) 部署到 RK1808 设备 + + adb push install/rknn_mobilenet_demo /userdata/ + + 3) 运行 Demo + + 6 + 瑞芯微电子股份有限公司 + + adb shell + cd /userdata/rknn_mobilenet_demo/ + ./rknn_mobilenet_demo model/mobilenet_v1_rk180x.rknn + model/dog_224x224.jpg #for RK1808 + ./rknn_mobilenet_demo model/mobilenet_v1_rv1109_rv1126.rknn + model/dog_224x224.jpg #for RV1109/RV1126 + +3.2.3 API 流程说明 + + 从 RKNN API V1.6.0 版本开始,新增加了一组设置输入的函数: +  rknn_inputs_map +  rknn_inputs_sync +  rknn_inputs_unmap + 以及一组获取输出的函数: +  rknn_outputs_map +  rknn_outputs_sync +  rknn_outputs_unmap + 在设置输入时,用户可以使用 rknn_inputs_set 或者 rknn_inputs_map 系列函数。获取推理 + 的输出时,使用 rknn_outputs_get 或者 rknn_outputs_map 系列函数。特定场景下,使用 map 系 + 列接口可以减少内存拷贝的次数,提高完整推理的速度。 + rknn_inputs_map 系列接口和 rknn_inputs_set 接口的调用流程不同,rknn_outputs_map 系 + 列接口和 rknn_outputs_get 接口的调用流程也不同。两个系列 API 调用流程差异如图 3-1 所示, + 其中(a)为 set/get 系列接口调用流程,(b)为 map 系列接口调用流程。 + + 7 + 瑞芯微电子股份有限公司 + + 图 3-1 使用 set/get 系列(a)和 map 系列(b)接口流程差异 + 设置输入和获取输出接口没有绑定关系,因此可以混合使用 set/get 系列接口和 map 系列 +接口。如图 3-2(c),用户可以使用 rknn_inputs_map 系列接口设置输入,再通过 rknn_outputs_get +接 口 获 取 输 出 , 或 者 如 图 3-2 ( d ) 通 过 rknn_inputs_set 系 列 接 口 设 置 输 入 , 再 使 用 +rknn_outputs_map 接口获取输出。 + + 8 + 瑞芯微电子股份有限公司 + + 图 3-2 混合使用 set/get 系列和 map 系列接口的调用流程 +3.2.3.1 API 内部处理流程 + + 在推理 RKNN 模型时,原始数据要经过输入处理、NPU 运行模型、输出处理三大流程。 + 在典型的图片推理场景中,假设输入数据 data 是 3 通道的图片且为 NHWC 排布格式,运行时 + (Runtime)对数据处 理的流程如图 3-3 所示。在 API 层面上,rknn_inputs_set 接口(当 + pass_through=0 时,详见 rknn_input 结构体)包含了颜色通道交换、归一化、量化、NHWC 转 + 换成 NCHW 的过程,rknn_outputs_get 接口(当 want_float=1 时,详见 rknn_output 结构体) + + 9 + 瑞芯微电子股份有限公司 + +包含了反量化的过程。 + + 图 3-3 完整的图片数据处理流程 + 实际上,对于某些 RKNN 模型,输入处理的流程没有全部执行,例如,当输入数据不是 + 3 通道图像时或者用 rknn-toolkit 导出模型的 config 函数配置为 reorder=”0 1 2”时,没有颜色通 + 道转换流程。当 RKNN 模型输入 tensor 的属性是 NHWC 布局时,没有 NHWC 转换成 NCHW + 的流程。rknn_inputs_se(t 当 pass_through=1 时)和 rknn_inputs_map 不包含任何输入处理流程, + rknn_outputs_get(当 want_float=0 时)和 rknn_outputs_map 不包含任何输出处理流程。此时, + 虽然两组 API 都不包含对应的处理流程,不过,使用 set/get 系列接口会比 map 系列接口多出 + 数据拷贝过程。 + +3.2.3.2 量化和反量化 + + 当使用 rknn_inputs_set(pass_through=1)和 rknn_inputs_map 时,表明在 NPU 推理之前 + 的流程要用户处理。rknn_outputs_map 获取输出后,用户也要做反量化得到 32 位浮点结果。 + + 量化和反量化用到的量化方式、量化数据类型以及量化参数,可以通过 rknn_query 接口 + 查询。目前,RK1808/RK3399Pro/RV1109/RV1126 的 NPU 有非对称量化和动态定点量化两种 + 量化方式,每种量化方式指定相应的量化数据类型。总共有以下四种数据类型和量化方式组 + 合: + +  uint8(非对称量化) +  int8(动态定点) +  int16(动态定点) +  float16(无) + 通常,归一化后的数据用 32 位浮点数保存,32 位浮点转换成 16 位浮点数请参考 IEEE-754 + 标准。假设归一化后的 32 位浮点数据是 D ,下面介绍量化流程: + + 10 + 瑞芯微电子股份有限公司 + +1)float32 转 uint8 +假设输入 tensor 的非对称量化参数是 Sq , ZP ,数据 D 量化过程表示为下式: + + Dq  round (clamp(D / Sq  ZP,0,255)) (3-1) + +上式中,clamp 表示将数值限制在某个范围。round 表示做舍入处理。 +2)float32 转 int8 +假设输入 tensor 的动态定点量化参数是 fl,数据 D 量化过程表示为下式: + + Dq  round (clamp(D * 2 fl ,-128,127)) (3-2) + +3)float32 转 int16 +假设输入 tensor 的动态定点量化参数是 fl,数据 D 量化过程表示为下式: + + Dq  round (clamp(D * 2 fl ,-32768,32767)) (3-3) + +反量化流程是量化的逆过程,可以根据上述量化公式反推出反量化公式,这里不做赘述。 + +3.2.3.3 零拷贝 + + 在特定的条件下,可以把输入数据拷贝次数减少到零,即零拷贝。比如,当 RKNN 模型 +是非对称量化,量化数据类型是 uint8,3 通道的均值是相同的整数同时缩放因子相同的情况 +下,归一化和量化可以省略。证明如下: + +假设输入图像数据是 D f ,量化参数是 Sq , ZP 。 M i 表示第 i 通道的均值, Si 表示第 i + +通道的归一化因子。则第 i 通道归一化后的数据 Di 如下式子: + + Di  (D f  M i ) / Si (3-4) + +数据 Di 量化过程表示为下式: (3-5) + Dq  clamp(Di / Sq  ZP,0,255) + +上述两个式子合并后,可以得出 + + Dq  clamp((D f  Mi ) /(Si * Sq)  ZP) (3-6) +假设量化图片矫正集数据范围包含 0 到 255 的整数值,当 M1  M 2  M 3 ,S1  S2  S3 时, + + 11 + 瑞芯微电子股份有限公司 + +归一化数值范围表示如下: (3-7) + Dmin  (0 - M i ) / Si  M i / Si (3-8) + Dmax  (255 - M i ) / Si + + 因此,量化参数计算如下: + + Sq  (Dmax  Dmin ) / 255  1/Si (3-9) + + ZP  (0  Dmin ) / Sq  M i (3-10) + +把式(3-9)和式(3-10)代入式(3-6),可以得出 D f  Dq ,即符合零拷贝的条件下:3 + +通道的均值是相同的整数同时归一化的缩放因子相同,输入 uint8 数据等于量化后的 uint8 数 +据。 + + 输入零拷贝能降低 CPU 负载,提高整体的推理速度。针对 RGB 或 BGR 输入数据,实现 +输入零拷贝的步骤如下: + + 1)三个通道的均值是相同的整数同时归一化的缩放因子相同。 + 2)在 rknn-toolkit 的 config 函数中,设置 force_builtin_perm=True,导出 NHWC 输入的 +RKNN 模型。 + 3)使用 rknn_inputs_map 接口,获取输入 tensor 内存地址信息。 + 4)往内存地址填充输入数据,比如调用 RGA 缩放函数,目标地址使用 rknn_inputs_map +获取的物理地址。 + 5)调用 rknn_inputs_sync 接口。 + 6)调用 rknn_run 接口。 + 7)调用获取输出接口。 + +3.2.4 API 详细说明 + +3.2.4.1 rknn_init + + rknn_init 初始化函数将创建 rknn_context 对象、加载 RKNN 模型以及根据 flag 执行特定 +的初始化行为。 + + 12 + 瑞芯微电子股份有限公司 + +API rknn_init +功能 初始化 rknn +参数 rknn_context *context:rknn_context 指针。函数调用之后,context 将会被赋值。 + void *model:RKNN 模型的二进制数据。 + uint32_t size:模型大小。 + uint32_t flag:特定的初始化标志。目前 RK1808 平台仅支持以下标志: + + RKNN_FLAG_COLLECT_PERF_MASK:打开性能收集调试开关,打开之后能够通过 + rknn_query 接口查询网络每层运行时间。需要注意,该标志被设置后 rknn_run 的 + 运行时间将会变长。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + rknn_context ctx; + int ret = rknn_init(&ctx, model_data, model_data_size, 0); + +3.2.4.2 rknn_destroy + + rknn_destroy 函数将释放传入的 rknn_context 及其相关资源。 + +API rknn_destroy + +功能 销毁 rknn_context 对象及其相关资源。 + +参数 rknn_context context:要销毁的 rknn_context 对象。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + int ret = rknn_destroy (ctx); + +3.2.4.3 rknn_query + rknn_query 函数能够查询获取到模型输入输出、运行时间以及 SDK 版本等信息。 + + 13 + 瑞芯微电子股份有限公司 + +API rknn_query + +功能 查询模型与 SDK 的相关信息。 + +参数 rknn_context context:rknn_context 对象。 + + rknn_query_cmd cmd:查询命令。 + + void* info:存放返回结果的结构体变量。 + + uint32_t size:info 对应的结构体变量的大小。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 当前 SDK 支持的查询命令如下表所示: + + 查询命令 返回结果结构体 功能 + +RKNN_QUERY_IN_OUT_NUM rknn_input_output_num 查询输入输出 Tensor 个数 + +RKNN_QUERY_INPUT_ATTR rknn_tensor_attr 查询输入 Tensor 属性 + +RKNN_QUERY_OUTPUT_ATTR rknn_tensor_attr 查询输出 Tensor 属性 + +RKNN_QUERY_PERF_DETAIL rknn_perf_detail 查询网络各层运行时间 + +RKNN_QUERY_SDK_VERSION rknn_sdk_version 查询 SDK 版本 + + 接下来的将依次详解各个查询命令如何使用。 + +1) 查询输入输出 Tensor 个数 + 传入 RKNN_QUERY_IN_OUT_NUM 命令可以查询模型输入输出 Tensor 的个数。其中需 + +要先创建 rknn_input_output_num 结构体对象。 + 示例代码如下: + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, + + sizeof(io_num)); + printf("model input num: %d, output num: %d\n", io_num.n_input, + + io_num.n_output); + +2) 查询输入 Tensor 属性 + 传入 RKNN_QUERY_INPUT_ATTR 命令可以查询模型输入 Tensor 的属性。其中需要先创 + +建 rknn_tensor_attr 结构体对象。 + + 14 + 瑞芯微电子股份有限公司 + +示例代码如下: + +rknn_tensor_attr input_attrs[io_num.n_input]; +memset(input_attrs, 0, sizeof(input_attrs)); +for (int i = 0; i < io_num.n_input; i++) { + + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +3) 查询输出 Tensor 属性 + 传入 RKNN_QUERY_OUTPUT_ATTR 命令可以查询模型输出 Tensor 的属性。其中需要先 + +创建 rknn_tensor_attr 结构体对象。 + 示例代码如下: + +rknn_tensor_attr output_attrs[io_num.n_output]; +memset(output_attrs, 0, sizeof(output_attrs)); +for (int i = 0; i < io_num.n_output; i++) { + + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +4) 查询网络各层运行时间 + 如果在 rknn_init 函数调用时有设置 RKNN_FLAG_COLLECT_PERF_MASK 标志,那么 + +在执行 rknn_run 完成之后,可以传入 RKNN_QUERY_PERF_DETAIL 命令来查询网络每层运 +行时间。其中需要先创建 rknn_perf_detail 结构体对象。 + + 示例代码如下: + +rknn_perf_detail perf_detail; +ret = rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, &perf_detail, + + sizeof(rknn_perf_detail)); +printf("%s", perf_detail.perf_data); + + 注意,用户不需要释放 rknn_perf_detail 中的 perf_data,SDK 会自动管理该 Buffer 内存。 +5) 查询 SDK 版本 + + 传入 RKNN_QUERY_SDK_VERSION 命令可以查询 RKNN SDK 的版本信息。其中需要 +先创建 rknn_sdk_version 结构体对象。 + + 15 + 瑞芯微电子股份有限公司 + + 示例代码如下: + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + + sizeof(rknn_sdk_version)); + printf("sdk api version: %s\n", version.api_version); + printf("driver version: %s\n", version.drv_version); + +3.2.4.4 rknn_inputs_set + + 通过 rknn_inputs_set 函数可以设置模型的输入数据。该函数能够支持多个输入,其中每 + + 个输入是 rknn_input 结构体对象,在传入之前用户需要设置该对象。 + +API rknn_inputs_set + +功能 设置模型输入数据。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + rknn_input inputs[]:输入数据数组,数组每个元素是 rknn_input 结构体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = img_width*img_height*img_channels; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = in_data; + + ret = rknn_inputs_set(ctx, 1, inputs); + +3.2.4.5 rknn_inputs_map + rknn_inputs_map 函数用于获取模型输入 tensor 初始化后的存储状态,存储状态包括虚拟 + + 16 + 瑞芯微电子股份有限公司 + + 地址,物理地址,fd,存储空间大小。它需要和 rknn_inputs_sync 接口(见 rknn_inputs_sync + 函数)配合使用,在模型初始化后,用户通过返回的的内存位置设置输入数据,并且在推理 + + 前调用 rknn_inputs_sync 函数。存储状态使用 rknn_tensor_mem 结构体表示。输入参数 mem 是 + + rknn_tensor_mem 结构体数组。 + 目前 ,在 RK1808/RV1109/RV1126 芯片 上,返回 的 fd 是 -1。当 返回的物 理地址 值是 + + 0xffffffffffffffff(2 的 64 次幂-1),表示无法获取正确的物理地址,而虚拟地址仍然有效。如 + 果有多个模型输入 tensor 的存储空间较大,用户可以在挂载驱动时,适当增加模型输入和输 + 出存储空间或者扩增固件中的 CMA 内存空间。以 RV1109_RV1126 为例,配置驱动存储空间, + 可以参考如下修改: + + 把/etc/init.d/S60NPU_init 文件这一行: + insmod /lib/modules/galcore.ko contiguousSize=0x400000 gpuProfiler=1 + 改成 + insmod /lib/modules/galcore.ko contiguousSize=0x600000 gpuProfiler=1 + 然后重启生效。此配置应该大于用户模型输入和输出总大小,但不超过固件中可用的 + + CMA 空间大小。 + +API rknn_inputs_map + +功能 读取输入存储状态信息。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结 + 构体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + 17 + 瑞芯微电子股份有限公司 + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + +3.2.4.6 rknn_inputs_sync + + rknn_inputs_sync 函数将 CPU 缓存写回内存,让设备能获取正确的数据。 + +API rknn_inputs_sync + +功能 同步输入数据。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + + ret = rknn_inputs_sync(ctx, 1, mem); + +3.2.4.7 rknn_inputs_unmap + + rknn_inputs_unmap 函数将清除 rknn_inputs_map 函数获取的输入 tensor 的存储位置信息和 + + 标志。 + +API rknn_inputs_unmap + +功能 清除 rknn_inputs_map 函数获取的输入 tensor 的存储位置信息和标志。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + 18 + 瑞芯微电子股份有限公司 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + ret = rknn_inputs_sync(ctx, 1, mem); + ret = rknn_run(ctx, NULL); + + ret = rknn_inputs_unmap(ctx, 1, mem); + +3.2.4.8 rknn_run + + rknn_run 函数将执行一次模型推理,调用之前需要先通过 rknn_inputs_set 函数设置输入数 + + 据。 + +API rknn_run + +功能 执行一次模型推理。 + +参数 rknn_context context:rknn_context 对象。 + + rknn_run_extend* extend:保留扩展,当前没有使用,传入 NULL 即可。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + ret = rknn_run(ctx, NULL); + +3.2.4.9 rknn_outputs_get + + rknn_outputs_get 函数可以获取模型推理的输出数据。该函数能够一次获取多个输出数据。 + 其 中 每 个 输 出 是 rknn_output 结 构 体 对 象 , 在 函 数 调 用 之 前 需 要 依 次 创 建 并 设 置 每 个 + rknn_output 对象。 + + 对于输出数据的 buffer 存放可以采用两种方式:一种是用户自行申请和释放,此时 + + 19 + 瑞芯微电子股份有限公司 + + rknn_output 对象的 is_prealloc 需要设置为 1,并且将 buf 指针指向用户申请的 buffer;另一种 + 是由 rknn 来进行分配,此时 rknn_output 对象的 is_prealloc 设置为 0 即可,函数执行之后 buf + 将指向输出数据。 + +API rknn_outputs_get + +功能 获取模型推理输出。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + rknn_output outputs[]:输出数据的数组,其中数组每个元素为 rknn_output 结构体对 + 象,代表模型的一个输出。 + + rknn_output_extend* extend:保留扩展,当前没有使用,传入 NULL 即可 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) { + + outputs[i].want_float = 1; + } + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + +3.2.4.10 rknn_outputs_release + + rknn_outputs_release 函数将释放 rknn_outputs_get 函数得到的输出的相关资源。 + +API rknn_outputs_release + +功能 释放 rknn_output 对象。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + rknn_output outputs[]:要销毁的 rknn_output 数组。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下 + + 20 + 瑞芯微电子股份有限公司 + + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + +3.2.4.11 rknn_outputs_map + + rknn_outputs_map 函 数 获 取 模 型 初 始 化 后 输 出 tensor 的 存 储 状 态 。 需 要 和 + + rknn_outputs_sync 函 数 ( 见 rknn_outputs_sync 函 数 ) 配 合 使 用 , 在 模 型 初 始 化 后 调 用 + rknn_outputs_map 接口,接着每次推理完调用 rknn_outputs_sync 接口。如果用户需要 32 位浮 + 点类型的数据,需要根据量化方式和量化的数据类型做反量化。 + +API rknn_outputs_map + +功能 读取输出存储状态信息。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_map(ctx, 1, mem); + +3.2.4.12 rknn_outputs_sync + + 当使用 rknn_outputs_map 接口映射完模型运行时模型输出 tensor 存储状态信息后,为确 + + 保缓存一致性,使用 rknn_outputs_sync 函数让 CPU 获取推理完最新的数据。 + +API rknn_outputs_sync + +功能 推理完,同步最新的输出数据。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + 21 + 瑞芯微电子股份有限公司 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_sync(ctx, io_num.n_output, mem); + +3.2.4.13 rknn_outputs_unmap + + rknn_outputs_unmap 函数将清除 rknn_outputs_map 函数获取的输出 tensor 的存储状态。 + +API rknn_outputs_unmap + +功能 清除 rknn_outputs_map 函数获取的输出 tensor 的存储状态。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_unmap(ctx, io_num.n_output,mem); + +3.2.5 RKNN 数据结构定义 + +3.2.5.1 rknn_input_output_num + 结构体 rknn_input_output_num 表示输入输出 Tensor 个数,其结构体成员变量如下表所示: + + 22 + 成员变量 数据类型 瑞芯微电子股份有限公司 + n_input uint32_t +n_output uint32_t 含义 + + 输入 Tensor 个数 + 输出 Tensor 个数 + +3.2.5.2 rknn_tensor_attr + +结构体 rknn_tensor_attr 表示模型的 Tensor 的属性,结构体的定义如下表所示: + +成员变量 数据类型 含义 + +index uint32_t 表示输入输出 Tensor 的索引位置。 + +n_dims uint32_t Tensor 维度个数。 + + dims uint32_t[] Tensor 各维度值。 + name char[] Tensor 名称。 +n_elems Tensor 数据元素个数。 + uint32_t + +size uint32_t Tensor 数据所占内存大小。 + +fmt rknn_tensor_format Tensor 维度的格式,有以下格式: + + RKNN_TENSOR_NCHW + RKNN_TENSOR_NHWC + +type rknn_tensor_type Tensor 数据类型,有以下数据类型: + + RKNN_TENSOR_FLOAT32 + RKNN_TENSOR_FLOAT16 + RKNN_TENSOR_INT8 + RKNN_TENSOR_UINT8 + +qnt_type RKNN_TENSOR_INT16 + rknn_tensor_qnt_type Tensor 量化类型,有以下的量化类型: + + RKNN_TENSOR_QNT_NONE:未量化; + RKNN_TENSOR_QNT_DFP:动态定点量化; + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC : 非 + + 23 + fl int8_t 瑞芯微电子股份有限公司 + zp uint32_t + 对称量化。 +scale float RKNN_TENSOR_QNT_DFP 量化类型的参数。 + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC 量 化 + 类型的参数。 + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC 量 化 + 类型的参数。 + +3.2.5.3 rknn_input + + 结构体 rknn_input 表示模型的一个数据输入,用来作为参数传入给 rknn_inputs_set 函数。 +结构体的定义如下表所示: + +成员变量 数据类型 含义 + +index uint32_t 该输入的索引位置。 + +buf void* 输入数据 Buffer 的指针。 + +size uint32_t 输入数据 Buffer 所占内存大小。 + +pass_through uint8_t 设置为 1 时会将 buf 存放的输入数据直接设置给 + 模型的输入节点,不做任何预处理。 + +type rknn_tensor_type 输入数据的类型。 + +fmt rknn_tensor_format 输入数据的格式。 + +3.2.5.4 rknn_tensor_mem + +结构体 rknn_tensor_mem 表示 tensor 初始化后的存储状态信息,用来作为参数传入给 + +rknn_inputs_map 系列和 rknn_outputs_map 系列函数。结构体的定义如下表所示: + +成员变量 数据类型 含义 + +logical_addr void* 该输入的虚拟地址。 + +physical_addr uint64_t 该输入的物理地址。 + +fd int32_t 该输入的 fd。 + + 24 + size uint32_t 瑞芯微电子股份有限公司 + handle uint32_t + priv_data 该输入 tensor 占用的内存大小。 +reserved_flag void* 该输入的 handle。 + uint64_t 保留的数据。 + 保留的标志位。 + +3.2.5.5 rknn_output + +结构体 rknn_output 表示模型的一个数据输出,用来作为参数传入给 rknn_outputs_get 函 + +数,在函数执行后,结构体对象将会被赋值。结构体的定义如下表所示: + +成员变量 数据类型 含义 + +want_float uint8_t 标识是否需要将输出数据转为 float 类型输出。 + +is_prealloc uint8_t 标识存放输出数据的 Buffer 是否是预分配。 + +index uint32_t 该输出的索引位置。 + +buf void* 输出数据 Buffer 的指针。 + +size uint32_t 输出数据 Buffer 所占内存大小。 + +3.2.5.6 rknn_perf_detail + +结构体 rknn_perf_detail 表示模型的性能详情,结构体的定义如下表所示: + +成员变量 数据类型 含义 + +perf_data char* 性能详情包含网络每层运行时间,能够直接打印 + + 出来查看。 + +data_len uint64_t 存放性能详情的字符串数组的长度。 + +3.2.5.7 rknn_sdk_version + +结构体 rknn_sdk_version 用来表示 RKNN SDK 的版本信息,结构体的定义如下: + + 25 + 成员变量 数据类型 瑞芯微电子股份有限公司 + api_version char[] + drv_version char[] 含义 + SDK 的版本信息。 + SDK 所基于的驱动版本信息。 + +3.2.6 RKNN 返回值错误码 + +RKNN API 函数的返回值错误码定义如下表所示 + + 错误码 错误详情 + +RKNN_SUCC(0) 执行成功 + +RKNN_ERR_FAIL(-1) 执行出错 + +RKNN_ERR_TIMEOUT(-2) 执行超时 + +RKNN_ERR_DEVICE_UNAVAILABLE(-3) NPU 设备不可用 + +RKNN_ERR_MALLOC_FAIL(-4) 内存分配失败 + +RKNN_ERR_PARAM_INVALID(-5) 传入参数错误 + +RKNN_ERR_MODEL_INVALID(-6) 传入的 RKNN 模型无效 + +RKNN_ERR_CTX_INVALID(-7) 传入的 rknn_context 无效 +RKNN_ERR_INPUT_INVALID(-8) 传入的 rknn_input 对象无效 +RKNN_ERR_OUTPUT_INVALID(-9) 传入的 rknn_output 对象无效 + +RKNN_ERR_DEVICE_UNMATCH(-10) 版本不匹配 + +RKNN_ERR_INCOMPATILE_PRE_COMPILE_M RKNN 模型使用 pre_compile 模式,但是和当前驱动不 + +ODEL(-11) 兼容 + +RKNN_ERR_INCOMPATILE_OPTIMIZATION_L RKNN 模型设置了优化等级的选项,但是和当前驱动 + +EVEL_VERSION(-12) 不兼容 + +RKNN_ERR_TARGET_PLATFORM_UNMATCH RKNN 模型和当前平台不兼容,一般是将 RK1808 的平 + +(-13) 台的 RKNN 模型放到了 RV1109/RV1126 上。 + +RKNN_ERR_NON_PRE_COMPILED_MODEL_ RKNN 模型不是 pre_compile 模式,在 mini-driver 上无 + +ON_MINI_DRIVER(-14) 法执行 + + 26 + 瑞芯微电子股份有限公司 + +4 高级 API 使用说明 + +4.1 Matmul 算子库 + +4.1.1 简介 + + 高级 API 旨在利用 NPU 高算力特性,执行特定的数学运算,提供简洁的接口调用,达到 +计算加速的效果。其中,Matmul 算子库是一个定点数矩阵乘法的加速库。该操作定义如下: + + C  AT * B + 这里: + A,B 和 C 是 2 维矩阵 + A 是一个 K*M 的矩阵, + B 是一个 K*N 的矩阵 + C 是一个 M*N 的矩阵 + +4.1.2 数据结构定义 + + rknn_matmul_handle_t 表示用于执行 Matmul 算子操作的句柄,它包含了运行时环境的上 +下文和输入 buffer 的信息。结构体的定义如下表所示: + +成员变量 数据类型 含义 + + A void* 运算时第一个矩阵 buffer 的指针。 + + B void* 运算时第二个矩阵 buffer 的指针。 + +M int32_t A 矩阵的低维度元素个数。 + + K int32_t A 和 B 矩阵的高维度元素个数。 + + N int32_t B 矩阵的低维度元素个数。 + +in_dtype rknn_tensor_type 输入数据的类型。 + +rknn_ctx rknn_context 运行时的上下文对象。 + + 27 + 瑞芯微电子股份有限公司 + +4.1.3 详细 API 说明 + +4.1.3.1 rknn_matmul_load + + rknn_matmul_load 加载函数将加载用户创建的输入 buffer,返回 rknn_matmul_handle_t 类 + 型对象。Matmul 算子 API 不负责管理输入 buffer 的生命周期,用户要确保输入 buffer 在 Matmul + + 算子 API 调用内有效。 + +API rknn_matmul_load + +功能 初始化和设置输入 buffer 指针。 + +参数 void *a:用户创建的第一个矩阵 buffer 的指针,只支持输入是 8-bit 无符号整型或 8-bit + 有符号整型的一维数组指针。 + + void *b:用户创建的第二个矩阵 buffer 的指针,只支持输入是 8-bit 无符号整型或 8-bit + 有符号整型的一维数组指针。 + + int32_t M:A 矩阵的低维度元素个数。 + + int32_t K:A 和 B 矩阵的高维度元素个数。 + int32_t N:B 矩阵的低维度元素个数。 + + rknn_tensor_type dtype:用户指定的输入数据类型,只支持 RKNN_TENSOR_INT8 或 + RKNN_TENSOR_UINT8 类型 + +返回值 rknn_matmul_handle_t 对象。 + + 示例代码如下: + + rknn_tensor_type dtype = RKNN_TENSOR_INT8; + int8_t x[256*1] = {0}; + int8_t y[256*4096] = {0}; + rknn_matmul_handle_t handle= rknn_matmul_load(x,y,1,256,4096,dtype); + +4.1.3.2 rknn_matmul_run + + 在 rknn_matmul_load 被调用后和执行 rknn_matmul_run 前,输入 buffer 的数据由外部更新, + 不用重新调用 rknn_matmul_load。 + + 28 + 瑞芯微电子股份有限公司 + +API rknn_matmul_run + +功能 执行 Matmul 操作。 + +参数 rknn_matmul_handle_t matmul_handle:由 rknn_matmul_load 接口返回的句柄。 + + float *c:用户创建的矩阵浮点 buffer 的指针,用于获取输出。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + ... + float out_fp32_buf[4096] = {0}; + rknn_matmul_run(handle,out_fp32_buf); + +4.1.3.3 rknn_matmul_unload + +API rknn_matmul_unload + +功能 销毁 Matmul 算子运行时上下文。 + +参数 rknn_matmul_handle_t matmul_handle:由 rknn_matmul_load 接口返回的句柄。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + rknn_matmul_unload(handle); + +4.1.4 实现限制 + + Matmul 算子库是基于 NPU 的硬件架构实现,为了达到精度和速度的平衡,有一些限制如下。 + +4.1.4.1 维度限制 + + 按照上述操作描述,该库实现了 M=1 的矩阵乘法。具体而言,Matmul 算子输入 A 必须是 Kx1 +形状的 buffer,即用户必须创建一块包含 K 个 8-bit 无符号整型或者 8-bit 有符号整型元素的数据。 +当算子库运行在 Mini driver 上,K 的值只能设置为 128 或 256 或 512,N 固定为 4096,而在 Full driver +上运行,没有此限制,但建议 K 的值为 128,256,512,1024,2048,N 取 2 的偶数次幂,建议 N + + 29 + 瑞芯微电子股份有限公司 + +不大于 4096。 + +4.1.4.2 输入数据类型限制 + + 只支持 8-bit 无符号整型和 8-bit 有符号整型两种输入。 + +4.1.5 基准测试 + + 输入的两个矩阵使用随机数情况下,在 RV1109-EVB 板子上实测结果如表-1 所示。速度是 + +rknn_matmul_run 接口调用循环 100 次后的平均时间,平均相对误差是 NPU 和 CPU 上执行相同算 + +法的结果的误差值,具体公式是: + + (abs(R1  R2 ) / R2 ) / N + + k + + 其中, + + R1 是 Matmul 算子库输出向量,包含 N 个元素。 + R2 是 CPU 输出向量,包含 N 个元素。 + + 表-1 Matmul 算子库速度/精度结果(RV1109,int8) + +K N 速度(ms) 平均相对误差 + +128 1024 1.0 0.00034 + +256 1024 1.6 0.00032 + +512 2024 3.0 -0.00015 + +1024 1024 5.4 0.00047 + +128 4096 3.0 0.00051 + +256 4096 5.6 0.00024 + +512 4096 10.7 0.00024 + +1024 4096 20.9 0.00051 + +注意,速度可能因为 NPU 驱动版本不同而有些许差异。误差值则根据每次测试的随机数不同也可 +能有些许差异。 + + 30 + 瑞芯微电子股份有限公司 + +5 NPU 驱动说明 + +5.1.1 NPU 驱动目录说明 + + NPU 的驱动在$SDK/external/rknpu/drivers/目录下或者 + +https://github.com/rockchip-linux/rknpu/tree/master/drivers + +其中的编译、安装规则参考$SDK/buildroot/package/rockchip/rknpu/rknpu.mk +drivers/ +├── common +├── linux-aarch64 (for RK1808 npu full driver) +├── linux-aarch64-mini (for RK1808 npu mini driver) +├── linux-armhf (for RK1806 npu full driver) +├── linux-armhf-mini(for RK1806 npu mini driver) +├── linux-armhf-puma (for RV1126/RV1109 npu full driver) +├── linux-armhf-puma-mini(for RV1126/RV1109 npu mini driver) +├── npu_ko (NPU kernel driver) + +5.1.2 NPU full driver 与 mini driver 的区别 + + 主要包含以下几点: + 1)Mini driver 只支持预编译的 rknn 模型,如果跑非预编译模型,会出现 +RKNN_ERR_MODEL_INVALID 的错误,从 1.6.0 开始,会返回 +RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER 的错误; + 2)Full driver 支持 RKNN Toolkit 的联机调试功能,mini driver 不支持; + 3)Mini driver 库大小比 full driver 小很多,以 RV1109/RV1126 1.6.0 驱动为例,full driver 大 +小为 87MB,mini driver 大小为 7.1MB,可以有效的节省 flash 大小。 + 4)Mini driver 库运行时占用的内存比 full driver 小。 + + 31 + 瑞芯微电子股份有限公司 + +6 FAQ + +6.1.1 输入输出数据格式问题 + +6.1.1.1 三通道图片数据输入,采用 RGB 还是 BGR 排布? + + 建议用户输入数据统一使用 RGB 排布。在导出 RKNN 模型时,config 函数的 reorder_channel +参数,有以下两种可能: + + 1)如果原始模型使用 BGR 图片训练,reorder_channel='2 1 0'。 + 2)如果原始模型使用 RGB 图片训练,reorder_channel='0 1 2'。 + +6.1.1.2 rknn_input 结构体该设置的 RKNN_TENSOR_NHWC 还是 RKNN_TENSOR_NCHW? + 两种设置耗时为何不同? + + rknn_input 结构体根据用户自己的数据格式而定,C API 内部会自动转换成 NPU 需要的格式。 +耗时不同的原因是不同输入格式计算量不同,优化方式也不同。 + +6.1.1.3 没量化的 RKNN 模型,输出 rknn_tensor_attr 里面 size 和 rknn_outputs_get 接口返 + 回的 rknn_output 的 size 为何不同? + + 没量化 RKNN 模型,NPU 内部输出数据类型是 float16,大小是元素数量*2 字节。当用户设 +置 want_float=1 时,想要的是 float32 数据,float16 会转换成 float32,大小是元素数量*4 字节。 + +6.1.1.4 rknn_output.index 是用户输入还是驱动返回? + +驱动返回。 + +6.1.1.5 rknn_tensor_attr 中的 dims 数组为何会出现 0? + +0 表示该维度无效。rknn_tensor_attr 中的 n_dims 表示 dims 数组的有效维度数量。 + + 32 + 瑞芯微电子股份有限公司 + +6.1.1.6 rknn_tensor_attr 中的 dims 数组顺序与 rknn_toolkit 的获取的 numpy 的顺序相反? + 是。C API 中的数组排布跟 python 相反,比如 rknn-toolkit 的 run()接口获得 numpy 输出形 + +状是[1,255,20,20],C API 中 dims 数组是{20,20,255,1}。 + +6.1.2 输入输出接口使用问题 + +6.1.2.1 pass_through 用法以及使用 rknn_inputs_map 接口时,如何预处理数据? + 请参考 https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples 下的 + +rknn_pass_through_demo 示例。 +6.1.2.2 使用 rknn_inputs_map 或者 rknn_outputs_map 获取的物理地址为什么会无效?如何 + + 获取有效的物理地址? + + 输入/输出无法分配到物理连续的内存,可能的原因有: + 1)输入/输出的占用空间过大,超过了总的物理连续的内存大小(默认是 4MB)。 + 2)系统中没有足够的物理连续的内存可用。 + 3)导出 RKNN 模型时,config 函数添加如下参数:output_optimize=1。 + 用户可以尝试重启系统,或者让 NPU 驱动挂载时配置更大的连续地址空间,配置方法参考 +rknn_inputs_map 接口说明。 + +6.1.3 API 调用流程问题 + +6.1.3.1 rknn_init 成功后,模型文件占用内存是否可以释放? + 可以。 + +6.1.3.2 rknn_output.is_prealloc=1 时,rknn_outputs_release 是否需要调用? + 需要。 + + 33 + 瑞芯微电子股份有限公司 + +6.1.4 性能问题 + +6.1.4.1 rknn_init 耗时过长? + + 使用预编译模型。使用方法参考 https://github.com/rockchip-linux/rknn-toolkit/tree/master/doc 下 +User Guide 文档的相关章节。 + +6.1.4.2 rknn_inputs_set 接口耗时过长? + + 可能原因是数据量大或格式转换耗时长。如果是格式转换耗时长,用户可以尝试 pass_through +用法自己做转换。转换方式请参考 +https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples 下的 +rknn_pass_through_demo 示例。或者尝试在导出 RKNN 模型时,config 函数添加如下参数: +output_optimize=1。 + +6.1.4.3 rknn_outputs_get 接口耗时过长? + + 可能原因是数据量大或转换格式耗时长。如果是转换格式耗时长,用户可以尝试设置 +want_float=0, 再 自 己 做 转 换 。 或 者 尝 试 在 导 出 RKNN 模 型 时 , config 函 数 添 加 如 下 参 数 : +output_optimize=1。 + + 34 + diff --git a/libs/rklibs/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_EN.pdf b/libs/rklibs/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_EN.pdf new file mode 100644 index 0000000..1fe93f2 --- /dev/null +++ b/libs/rklibs/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_EN.pdf @@ -0,0 +1,1473 @@ + www.rock-chips.com + +Classification Level: Top Secret( ) Secret( ) Internal( ) Public(√) + +Rockchip User Guide RKNN API + +(Technology Department, Graphic Compute Platform Center) + +Mark: Version: 1.7.3 + +[ ] Changing Author: HPC + +[√] Released Completed Date: 13/Aug/2022 + + Reviewer: Vincent + + Reviewed Date: 213/Aug/2022 + + 瑞芯微电子股份有限公司 + Rockchip Electronics Co., Ltd. + + (Copyright Reserved) + www.rock-chips.com + + Revision History + +Version Modifier Date Modify Description Reviewer +v0.9.7 Yang Huacong +V0.9.8 Yang Huacong 25/Jan/2019 Initial version Zhuo + 5/Jun/2019 Hongtian +V1.3.3 Randall 2/June/2020 1. Add RKNN API Library description +V1.4.0 Randall 2. Update rknn example and rknn-toolkit Zhuo + Hongtian +v1.6.0 Chifred Hong path + 3. Fix some mistake Randall +v1.6.1 Chifred Hong 1. Support RV1109/RV1126 +v1.7.0 Chifred Hong 2. Remove the python support +v1.7.1 Chifred Hong instructions, see the RKNN Toolkit Lite +v1.7.3 Chifred Hong related documentation for python support. + + 10/Sep/2020 1. Add some error code Randall + + 15/Jan/2021 1. Add Matmul advanced API Randall + 2. Add a set of API for setting inputs and + a set of API for obtaining outputs + 3. Add quantization and dequantization + algorithm description + 4. Add NPU driver description + 5. Add FAQ + + 26/Apr/2021 1. Update Matmul advanced API Randall + + 5/Aug/2021 1. Update Version number Randall + Randall + 2/Dec/2021 1. Single-channel input performance Randall + optimization + 2. Fix bug + + 13/Aug/2022 1. Update Version number + + 1 + www.rock-chips.com + +1 OVERVIEW ..................................................................................................................................................... 5 +2 SUPPORTED HARDWARE PLATFORMS ................................................................................................. 5 +3 INSTRUCTIONS ..............................................................................................................................................5 + + 3.1 RKNN SDK DEVELOPMENT PROCESS ........................................................................................................ 5 + 3.2 RKNN C API ...............................................................................................................................................6 + + 3.2.1 RKNN API Library .............................................................................................................................. 6 + 3.2.2 EXAMPLES .........................................................................................................................................6 + 3.2.3 API process description ........................................................................................................................7 + + 3.2.3.1 API internal processing flow ........................................................................................................................... 9 + 3.2.3.2 Quantification and dequantization ................................................................................................................10 + 3.2.3.3 Zero copy .......................................................................................................................................................12 + 3.2.4 API Reference .................................................................................................................................... 13 + 3.2.4.1 rknn_init ........................................................................................................................................................ 13 + 3.2.4.2 rknn_destroy ..................................................................................................................................................14 + 3.2.4.3 rknn_query .................................................................................................................................................... 15 + 3.2.4.4 rknn_inputs_set ............................................................................................................................................. 17 + 3.2.4.5 rknn_inputs_map ...........................................................................................................................................18 + 3.2.4.6 rknn_inputs_sync ...........................................................................................................................................19 + 3.2.4.7 rknn_inputs_unmap .......................................................................................................................................20 + 3.2.4.8 rknn_run ........................................................................................................................................................ 21 + 3.2.4.9 rknn_outputs_get ...........................................................................................................................................21 + 3.2.4.10 rknn_outputs_release .................................................................................................................................. 22 + 3.2.4.11 rknn_outputs_map ....................................................................................................................................... 23 + 3.2.4.12 rknn_outputs_sync .......................................................................................................................................23 + + 2 + www.rock-chips.com + 3.2.4.13 rknn_outputs_unmap ...................................................................................................................................24 + 3.2.5 RKNN DataStruct Define .................................................................................................................. 25 + 3.2.5.1 rknn_input_output_num ................................................................................................................................ 25 + 3.2.5.2 rknn_tensor_attr ............................................................................................................................................25 + 3.2.5.3 rknn_input ..................................................................................................................................................... 26 + 3.2.5.4 rknn_tensor_mem ..........................................................................................................................................27 + 3.2.5.5 rknn_output ................................................................................................................................................... 27 + 3.2.5.6 rknn_perf_detail ............................................................................................................................................ 28 + 3.2.5.7 rknn_sdk_version .......................................................................................................................................... 28 + 3.2.6 RKNN Error Code ............................................................................................................................. 28 +4 ADVANCED API INSTRUCTIONS .............................................................................................................29 + 4.1 MATMUL OPERATOR LIBRARY ....................................................................................................................29 + 4.1.1 Introduction ........................................................................................................................................29 + 4.1.2 Data structure definition .....................................................................................................................30 + 4.1.3 Detailed API description .................................................................................................................... 31 + 4.1.3.1 rknn_matmul_load ........................................................................................................................................ 31 + 4.1.3.2 rknn_matmul_run ..........................................................................................................................................31 + 4.1.3.3 rknn_matmul_unload .................................................................................................................................... 32 + 4.1.4 Implementation restrictions ................................................................................................................32 + 4.1.4.1 Dimensional restrictions ............................................................................................................................... 33 + 4.1.4.2 Input data type restriction ............................................................................................................................. 33 + 4.1.5 Benchmark ......................................................................................................................................... 33 +5 NPU DRIVER DESCRIPTION .................................................................................................................... 35 + 5.1.1 Directory structure description ...........................................................................................................35 + 5.1.2 The difference between NPU full driver and mini driver .................................................................. 35 + + 3 + www.rock-chips.com +6 FAQ ..................................................................................................................................................................36 + + 6.1.1 Input and output data format issues ................................................................................................... 36 + 6.1.2 Input and output interface usage problems ........................................................................................ 37 + 6.1.3 API call process issues ....................................................................................................................... 38 + 6.1.4 Performance issues .............................................................................................................................38 + + 4 + www.rock-chips.com + +1 Overview + + RKNN SDK provides a programming interface for platforms with NPU such as RK1808, which can +help users deploy RKNN models exported using RKNN-Toolkit. + +2 Supported hardware platforms + +This document applies to the following hardware platforms: + 1) RK1808, RK1806 + 2) RV1126, RV1109 + +The following description takes RK1808 as an example and also applies to other platforms mentioned +above. + +3 Instructions +3.1 RKNN SDK Development Process + + Before using the RKNN SDK, users first need to use the RKNN-Toolkit tool to convert the + user's model to the RKNN model. The user can obtain the tool's complete installation package and + documentation at https://github.com/rockchip-linux/rknn-toolkit . + + After successful conversion to the RKNN model, users can first connect to the RK1808 device + via RKNN-Toolkit for online debugging to ensure that the accuracy and performance of the model + meet the requirements. + + After getting the RKNN model file, users can choose using C or Python interface to develop + the application. The following chapters will explain how to develop application based on the RKNN + SDK on RK1808 platform. + + 5 + www.rock-chips.com + +3.2 RKNN C API + +3.2.1 RKNN API Library + + The libraries and header files provided by the RKNN SDK are located at +/external/rknpu/rknn/rknn_api/librknn_api directory, developers can use to develop +applications. + + It should be noted that the RKNN API of the RK1808 and RK3399Pro platforms is compatible, +and applications developed by both can be easily ported. However, you need to pay attention to +distinguish between the two platforms librknn_api.so, if the developer uses RK3399Pro's +librknn_api.so will not be able to run on the RK1808 platform. Developers can use the following +methods to distinguish the platform of librknn_api.so. + + $ strings librknn_api.so |grep version + librknn_api version 1.7.3 (cf7f05f build: 2022-08-13 10:59:33) + +3.2.2 EXAMPLES + + The SDK provides MobileNet image classification, MobileNet SSD object detection, and Yolo + v3 object detection demos. These demos provide reference for developer to develop applications + based on the RKNN SDK. The emo code is located in the + /external/rknpu/rknn/rknn_api/examples directory. Let's take rknn_mobilenet_demo as an + example to explain how to get started quickly. + + 1) Compile Demo Source Code + + cd examples/rknn_mobilenet_demo + # modify `GCC_COMPILER` on `build.sh` for target platform, then execute + ./build.sh + 2) Deploy to the RK1808 device + + adb push install/rknn_mobilenet_demo /userdata/ + + 3) Run Demo + + 6 + www.rock-chips.com + + adb shell + cd /userdata/rknn_mobilenet_demo/ + ./rknn_mobilenet_demo model/mobilenet_v1_rk180x.rknn + model/dog_224x224.jpg # RK180x + ./rknn_mobilenet_demo model/mobilenet_v1_rv1109_rv1126.rknn + model/dog_224x224.jpg # RV1109/RV1126 + +3.2.3 API process description + + From RKNN API V1.6.0, a new set of functions for setting input have been added: +  rknn_inputs_map +  rknn_inputs_sync +  rknn_inputs_unmap + And a set of functions to get the output: +  rknn_outputs_map +  rknn_outputs_sync +  rknn_outputs_unmap + When setting input, users can use rknn_inputs_set or rknn_inputs_map series of functions. + When obtaining the output of inference, use the rknn_outputs_get or rknn_outputs_map series of + functions. In certain scenarios, using the map series of API can reduce the number of memory copies + and improve the speed of complete inference. + The calling process of the rknn_inputs_map series interface and the rknn_inputs_set interface is + different, and the calling process of the rknn_outputs_map series interface and the rknn_outputs_get + interface are also different. The difference between the two series of API call flow is shown in Figure + 3-1, where (a) is the set/get series interface call flow, (b) is the map series interface call flow. + + 7 + www.rock-chips.com + + Figure 3-1 callling process difference between set/get series (a) and map series (b) + There is no binding relationship between the set input and get output API, so set/get series +interfaces and map series interfaces can be mixed. As shown in Figure 3-2(c), the user can use the +rknn_inputs_map series interface to set the input, and then get the output through the +rknn_outputs_get interface, or as Figure 3-2(d) set the input through the rknn_inputs_set series +interface, and then use the rknn_outputs_map interface to get the output. + + 8 + www.rock-chips.com + + Figure 3-2 The calling process of mixed use of set/get series and map series interfaces +3.2.3.1 API internal processing flow + + During inference of RKNN model, the original data has to go through three processes: input + processing, NPU running model, and output processing. In a typical picture inference scenario, + assuming that the input data is a 3-channel picture and is in the NHWC layout format, the data + processing flow at runtime is shown in Figure 3-3. At the API level, the rknn_inputs_set interface + (when pass_through=0, see the rknn_input structure for details) includes the process of swapping + + 9 + www.rock-chips.com + + color channel, normalization, quantization, and conversion of NHWC to NCHW. The + rknn_outputs_get interface (when want_float=1, see rknn_output structure) contains the process of + dequantization. + + Figure 3-3 Normal mode image data processing flow + In fact, for some RKNN models, the Runtime input processing flow is not all executed. For + example, when the input data is not a 3-channel image or the config function of the rknn-toolkit is + configured as reorder=”0 1 2” when exporting model, there is no color channel swapping process. + When the attribute of RKNN model input tensor is NHWC layout, there is no process for converting + NHWC to NCHW. rknn_inputs_set (when pass_through=1) and rknn_inputs_map do not contain any + input processing flow, rknn_outputs_get (when want_float=0) and rknn_outputs_map do not contain + any output processing flow. At this time, although the two sets of APIs do not include the + corresponding processing flow, using the set/get series interface will have more data copying process + than the map series interface. + +3.2.3.2 Quantification and dequantization + + When using rknn_inputs_set (pass_through=1) and rknn_inputs_map, it indicates that the + process before NPU inference needs to be processed by the user. After rknn_outputs_map gets the + output, the user also needs to dequantize output to get the 32-bit floating point result. + + The quantization method, quantization data type and quantization parameters used in + quantization and dequantization can be queried through the rknn_query interface. Currently, the NPU + of RK1808/RK3399Pro/RV1109/RV1126 has two quantization methods: asymmetric quantization + and dynamic fixed-point quantization. Each quantization method specifies the corresponding + quantized data type. There are a total of four combinations of data types and quantification methods: + + 10 + www.rock-chips.com + +  uint8 (asymmetric quantization) +  int8 (dynamic fixed-point) +  int16 (dynamic fixed-point) +  float16 (none) + Normally, the normalized data is stored in 32-bit floating point data. For conversion of 32-bit +floating point data to 16-bit floating point data, please refer to the IEEE-754 standard. Assuming that +the normalized 32-bit floating point data is D , the following describes the quantization process: + 1) float32 to uint8 + +Assuming that the asymmetric quantization parameter of the input tensor is Sq , ZP , the data + +quantization process is expressed as the following formula: + + Dq  round (clamp(D / Sq  ZP,0,255)) (3-1) + +In the above formula, clamp means to limit the value to a certain range. round means rounding + +processing. + +2) float32 to int8 + +Assuming that the dynamic fixed-point quantization parameter of the input tensor is fl, the data + +quantization process is expressed as the following formula: + + Dq  round (clamp(D * 2 fl ,-128,127)) (3-2) + + 3) float32 to int16 + Assuming that the dynamic fixed-point quantization parameter of the input tensor is fl, the data +quantization process is expressed as the following formula: + + Dq  round (clamp(D * 2 fl ,-32768,32767)) (3-3) + + The dequantization process is the inverse process of quantization, and the inverse quantization +formula can be deduced according to the above quantization formula, which will not be repeated +here. + + 11 + www.rock-chips.com + +3.2.3.3 Zero copy + + In specific case, the number of input data copies can be reduced to 0, that is, zero copy. For +example, when the RKNN model is asymmetric quantization, the quantization data type is uint8, the +mean value of the 3 channels is the same integer and the scaling factor is the same, the normalization +and quantization can be omitted. The proof is as follows: + +Assume that the input image data is D f , and the quantization parameter is Sq , ZP . M i + +Represents the mean value of the i-th channel, and Si represents the normalization factor of the i-th +channel. Then the normalized data of the i-th channel is as follows: + + Di  (D f  M i ) / Si (3-4) + +The data quantification process is expressed as the following formula: + + Dq  clamp(Di / Sq  ZP,0,255) (3-5) + +After combining the above two formulas, we can get (3-6) + Dq  clamp((D f  Mi ) /(Si * Sq)  ZP) + +Assuming that the data range of the calibration data set contains integer values from 0 to 255, + +when M1  M 2  M3 , S1  S2  S3 , the normalized value range is expressed as follows: + + Dmin  (0 - M i ) / Si  M i / Si (3-7) + + Dmax  (255 - M i ) / Si (3-8) + +So, the quantization parameter is calculated as follows: + + Sq  (Dmax  Dmin ) / 255  1/Si (3-9) + + ZP  (0  Dmin ) / Sq  M i (3-10) + + Substituting formula (3-9) and formula (3-10) into formula (3-6), it can be concluded that under +the condition of zero-copy: the mean value of the 3 channels is the same integer and the normalized +scaling factor is the same , The input uint8 data is equal to the quantized uint8 data. + + Input zero copy can reduce the CPU load and improve the speed of complete inference. For + + 12 + www.rock-chips.com + + RGB or BGR input data, the steps to achieve input zero-copy are as follows: + 1) The mean value of the three channels is the same integer and the normalized scaling factor is + + the same. + 2) In the config function of rknn-toolkit, set force_builtin_perm=True to export the RKNN + + model input by NHWC. + 3) Use the rknn_inputs_map interface to obtain the input tensor memory address information. + 4) Fill the memory address with input data, such as calling the RGA resize function, the target + + address uses the physical address obtained by rknn_inputs_map. + 5) Call the rknn_inputs_sync interface. + 6) Call the rknn_run interface. + 7) Call the get output interface. + +3.2.4 API Reference + +3.2.4.1 rknn_init + The rknn_init function will create a rknn_context object, load the RKNN model, and perform + + specific initialization behavior based on the flag. + + 13 + www.rock-chips.com + +API rknn_init + +Description Initialize rknn + +Parameters rknn_context *context:Pointer to rknn_context object. After the function is called, the + + context object will be assigned. + + void *model:Binary data for the RKNN model. + + uint32_t size:Model size + + uint32_t flag:A specific initialization flag. Currently supports following flags: + + RKNN_FLAG_COLLECT_PERF_MASK : Open the performance collection debugging + + switch. After opening, you can query the running time of each layer of the network + + through the rknn_query interface. Note that the running time of rknn_run will be + + longer after this flag is set. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_context ctx; + int ret = rknn_init(&ctx, model_data, model_data_size, 0); + +3.2.4.2 rknn_destroy + + The rknn_destroy function will release the rknn_context object and its associated resources. + +API rknn_destroy + +Description Destroy the rknn_context object and its related resources. + +Parameters rknn_context context: The rknn_context object to be destroyed. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + 14 + www.rock-chips.com + + int ret = rknn_destroy (ctx); + +3.2.4.3 rknn_query + + The rknn_query function can query the information of model input and output tensor attribute, + + performance information and SDK version etc. + +API rknn_query + +Description Query the information about the model and the SDK. + +Parameters rknn_context context: The object of rknn_contex. + + rknn_query_cmd cmd: Query command. + + void* info: Structure object that stores the result of the query. + + uint32_t size: the size of the info Structure object. + +Return int: Error code (See RKNN Error Code). + + Currently, the SDK supports the following query commands: + + Query command Return result structure Function + +RKNN_QUERY_IN_OUT_NUM rknn_input_output_num Query the number of input and output + + Tensor. + +RKNN_QUERY_INPUT_ATTR rknn_tensor_attr Query input Tensor attribute. + +RKNN_QUERY_OUTPUT_ATTR rknn_tensor_attr Query output Tensor attribute. + +RKNN_QUERY_PERF_DETAIL rknn_perf_detail Query the running time of each layer + + of the network. + +RKNN_QUERY_SDK_VERSION rknn_sdk_version Query the SDK version. + + Next we will explain each query command in detail. + + 1) Query the number of input and output Tensor + + The RKNN_QUERY_IN_OUT_NUM command can be used to query the number of model + + input and output Tensor. You need to create the rknn_input_output_num structure object first. + + 15 + www.rock-chips.com + +Sample Code: + +rknn_input_output_num io_num; +ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, + + sizeof(io_num)); +printf("model input num: %d, output num: %d\n", io_num.n_input, + + io_num.n_output); + +2) Query input Tensor attribute + The RKNN_QUERY_INPUT_ATTR command can be used to query the attribute of the model + +input Tensor. You need to create the rknn_tensor_attr structure object first. +Sample Code: + +rknn_tensor_attr input_attrs[io_num.n_input]; +memset(input_attrs, 0, sizeof(input_attrs)); +for (int i = 0; i < io_num.n_input; i++) { + + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +3) Query output Tensor attribute + The RKNN_QUERY_OUTPUT_ATTR command can be used to query the attribute of the model + +output Tensor. You need to create the rknn_tensor_attr structure object first. + Sample Code: + +rknn_tensor_attr output_attrs[io_num.n_output]; +memset(output_attrs, 0, sizeof(output_attrs)); +for (int i = 0; i < io_num.n_output; i++) { + + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +4) Query the running time of each layer of the network + If the RKNN_FLAG_COLLECT_PERF_MASK flag has been set on the rknn_init function, the + +RKNN_QUERY_PERF_DETAIL command can be passed to query the runtime of each layer of the +network after rknn_run function execution completed. You need to create the rknn_perf_detail + + 16 + www.rock-chips.com + + structure object first. + Sample Code: + + rknn_perf_detail perf_detail; + ret = rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, &perf_detail, + + sizeof(rknn_perf_detail)); + printf("%s", perf_detail.perf_data); + + It should be noted that rknn_perf_detail.perf_data does not need to be released. The SDK will + automatically manage this buffer memory. + 5) Query the SDK version + + The RKNN_QUERY_SDK_VERSION command can be used to query the version information + of the RKNN SDK. You need to create the rknn_sdk_version structure object first. + Sample Code: + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + + sizeof(rknn_sdk_version)); + printf("sdk api version: %s\n", version.api_version); + printf("driver version: %s\n", version.drv_version); + +3.2.4.4 rknn_inputs_set + + The input data of the model can be set by the rknn_inputs_set function. This function can + + support multiple inputs, each one is a rknn_input structure object. Developers needs to set these + + object field before passing in. + +API rknn_inputs_set + +Description Set the model input data. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs. + + rknn_input inputs[]: Array of rknn_input. + +Return int: Error code (See RKNN Error Code). + + 17 + www.rock-chips.com + +Sample Code: + +rknn_input inputs[1]; +memset(inputs, 0, sizeof(inputs)); +inputs[0].index = 0; +inputs[0].type = RKNN_TENSOR_UINT8; +inputs[0].size = img_width*img_height*img_channels; +inputs[0].fmt = RKNN_TENSOR_NHWC; +inputs[0].buf = in_data; + +ret = rknn_inputs_set(ctx, 1, inputs); + +3.2.4.5 rknn_inputs_map + + The rknn_inputs_map function is used to obtain the storage state of the model input tensor after + initialization. The storage state includes virtual address, physical address, fd, and storage space size. + It needs to be used in conjunction with the rknn_inputs_sync interface (see the rknn_inputs_sync + function). After the model is initialized, the user sets the input data through the returned memory + location, and calls the rknn_inputs_sync function before inference. The storage state is represented + by the rknn_tensor_mem structure. The input parameter mem is an array of rknn_tensor_mem + structure. + + Currently, on the RK1808/RV1109/RV1126 chip, the returned fd is -1. When the returned + physical address value is 0xffffffffffffffff (2 to the 64th power-1), it means that the correct physical + address cannot be obtained, and the virtual address is still valid. If there are multiple model input + tensors with large storage space, users can appropriately increase the model input and output storage + space when mounting the driver or increase the CMA memory space in the firmware. Take + RV1109_RV1126 as an example to configure drive storage space, you can refer to the following + modifications: + + Find this line in the /etc/init.d/S60NPU_init file: + insmod /lib/modules/galcore.ko contiguousSize=0x400000 gpuProfiler=1 + Change to + + 18 + www.rock-chips.com + + insmod /lib/modules/galcore.ko contiguousSize=0x600000 gpuProfiler=1 + Then restart to take effect. This configuration should be larger than the total size of user model + + input and output, but not more than the available CMA space in the firmware. + +API rknn_inputs_map + +Description Read input storage status information. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_input inputs[1]; + rknn_tensor_mem mem[1]; + memset(inputs, 0, sizeof(inputs)); + + //set input info returned by rknn_query + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = img_width*img_height*img_channels; + inputs[0].fmt = RKNN_TENSOR_NHWC; + + ret = rknn_inputs_map(ctx, 1, inputs, mem); + +3.2.4.6 rknn_inputs_sync + + The rknn_inputs_sync function writes the CPU cache back to the memory so that the device + + can obtain the correct data. + +API rknn_inputs_sync + +Description Synchronize input data. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs. + + 19 + www.rock-chips.com + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + + ret = rknn_inputs_sync(ctx, 1, mem); + +3.2.4.7 rknn_inputs_unmap + + The rknn_inputs_unmap function will clear the storage location information and flags of the + + input tensor obtained by the rknn_inputs_map function. + +API rknn_inputs_unmap + +Description Clear the storage location information and flags of the input tensor obtained by the + + rknn_inputs_map function. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + 20 + www.rock-chips.com + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + ret = rknn_inputs_sync(ctx, 1, mem); + ret = rknn_run(ctx, NULL); + + ret = rknn_inputs_unmap(ctx, 1, inputs, mem); + +3.2.4.8 rknn_run + + The rknn_run function will perform a model reasoning. The input data need to be set by the + + rknn_inputs_set function before rknn_run is called. + +API rknn_run + +Description Perform a model reasoning. + +Parameter rknn_context context: The object of rknn_contex. + + rknn_run_extend* extend: Reserved for extension, currently not used, you can pass + + NULL. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + ret = rknn_run(ctx, NULL); + +3.2.4.9 rknn_outputs_get + + The rknn_outputs_get function can get the output data of the model reasoning. This function + can get multiple output data. Each of these outputs is a rknn_output structure object, which needs to + be created and set in turn before the function is called. + + There are two ways to store buffers for output data: + 1) Developer allocate and release buffers themselves. At this time, the + + rknn_output.is_prealloc needs to be set to 1, and the rknn_output.buf points to users’ + allocated buffer; + + 21 + www.rock-chips.com + + 2) The other is allocated by SDK. At this time, the rknn_output .is_prealloc needs to be set to + + 0. After the function is executed, rknn_output.buf will be created and store the output data. + +API rknn_outputs_get + +Description Get model inference output data. + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_output outputs[]: Array of rknn_output. + + rknn_run_extend* extend: Reserved for extension, currently not used, you can pass + + NULL. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) { + + outputs[i].want_float = 1; + } + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + +3.2.4.10 rknn_outputs_release + + The rknn_outputs_release function will release the relevant resources of the rknn_output object. + +API rknn_outputs_release + +Description Release the rknn_output object + +Parameter rknn_context context: rknn_context object + + uint32_t n_outputs: Number of output. + + rknn_output outputs[]: rknn_output array to be release. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + 22 + www.rock-chips.com + + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + +3.2.4.11 rknn_outputs_map + + The rknn_outputs_map function obtains the storage state of the output tensor after the model is + + initialized. It needs to be used in conjunction with the rknn_outputs_sync function (see + + rknn_outputs_sync function). After the model is initialized, the rknn_outputs_map interface is called, + + and then the rknn_outputs_sync interface is called after each inference. If users need 32-bit floating + + point data, they need to perform dequantization according to the quantization method and quantized + + data type. + +API rknn_outputs_map + +Description Read output storage status information. + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_map(ctx, 1, mem); + +3.2.4.12 rknn_outputs_sync + + After the rknn_outputs_map interface is used to map the model output tensor storage state + information when the model is running, in order to ensure cache consistency, the rknn_outputs_sync + function is used to let the CPU obtain the latest data after the inference. + + 23 + www.rock-chips.com + +API rknn_outputs_sync + +Description After inference, synchronize the latest output data + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + +Sample Code: + + rknn_tensor_mem mem[1]; + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_sync(ctx, io_num.n_output, mem); + +3.2.4.13 rknn_outputs_unmap + + The rknn_outputs_unmap function will clear the storage status of the output tensor obtained by + + the rknn_outputs_map function. + +API rknn_outputs_unmap + +Description Clear the storage state of the output tensor obtained by the rknn_outputs_map + + function. + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + +Sample Code: + + 24 + www.rock-chips.com + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_unmap(ctx, io_num.n_output,mem); + +3.2.5 RKNN DataStruct Define + +3.2.5.1 rknn_input_output_num + + The structure rknn_input_output_num represents the number of input and output Tensor , The + following table shows the definition: + + Field Type Meaning +n_input uint32_t The number of input tensor +n_output uint32_t The number of output tensor + +3.2.5.2 rknn_tensor_attr + +The structure rknn_tensor_attr represents the attribute of the model's Tensor. The following table + +shows the definition: + +Field Type Meaning + +index uint32_t Indicates the index position of the input and output + + Tensor. + +n_dims uint32_t The number of Tensor dimensions. + +dims uint32_t[] Values for each dimension. + +name char[] Tensor name. + +n_elems uint32_t The number of Tensor data elements. + +size uint32_t The memory size of Tensor data. + +fmt rknn_tensor_format The format of Tensor dimension, has the following + + format: + + 25 + www.rock-chips.com + + RKNN_TENSOR_NCHW + + RKNN_TENSOR_NHWC + + type rknn_tensor_type Tensor data type, has the following data types: + +qnt_type RKNN_TENSOR_FLOAT32 + + fl RKNN_TENSOR_FLOAT16 + zp + scale RKNN_TENSOR_INT8 + + RKNN_TENSOR_UINT8 + + RKNN_TENSOR_INT16 + + rknn_tensor_qnt_type Tensor Quantization Type, has the following types + + of quantization: + + RKNN_TENSOR_QNT_NONE: Not quantified; + + RKNN_TENSOR_QNT_DFP: Dynamic fixed point + + quantization; + + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + + Asymmetric quantification. + + int8_t RKNN_TENSOR_QNT_DFP quantization parameter + + uint32_t RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC + + quantization parameter. + + float RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC + + quantization parameter. + +3.2.5.3 rknn_input + + The structure rknn_input represents a data input to the model, used as a parameter to the +rknn_inputs_set function. The following table shows the definition: + + 26 + www.rock-chips.com + + Field Type Meaning + index + buf uint32_t The index position of this input. + size +pass_through void* Point to the input data Buffer. + + type uint32_t The memory size of the input data Buffer. + fmt + uint8_t When set to 1, buf will be directly set to the input + + node of the model without any pre-processing. + + rknn_tensor_type The type of input data. + + rknn_tensor_format The format of input data. + +3.2.5.4 rknn_tensor_mem + +The structure rknn_tensor_mem represents the storage state information after tensor + +initialization, which is used as a parameter to pass in to the rknn_inputs_map series and + +rknn_outputs_map series functions. The definition of the structure is shown in the following table: + +Field Type Meaning + +logical_addr void* The virtual address of this input. + +physical_addr uint64_t The physical address of this input. + +fd int32_t The fd of this input. + +size uint32_t The memory size of the input tensor. + +handle uint32_t The handle of this input. + +priv_data void* Reserved data. + +reserved_flag int32_t Reserved flag. + +3.2.5.5 rknn_output + + The structure rknn_output represents a data output of the model, used as a parameter to the +rknn_outputs_get function. The following table shows the definition: + + 27 + Field Type www.rock-chips.com +want_float uint8_t + Meaning +is_prealloc uint8_t Indicates if the output data needs to be converted + to float type. + index uint32_t Indicates whether the Buffer that stores the output + buf void* data is pre-allocated. + size The index position of this output. + uint32_t Pointer which point to the output data Buffer. + Output data Buffer memory size. + +3.2.5.6 rknn_perf_detail + +The structure rknn_perf_detail represents the performance details of the model. The following + +table shows the definition: + +Field Type Meaning + +perf_data char* Performance details include the run time of each + + layer of the network. + +data_len uint64_t The Length of perf_data. + +3.2.5.7 rknn_sdk_version + +The structure rknn_sdk_version is used to indicate the version information of the RKNN SDK. + +The following table shows the definition: + +Field Type Meaning + +api_version char[] SDK API Version information. + +drv_version char[] Driver version information. + +3.2.6 RKNN Error Code + +The return code of the RKNN API function is defined as shown in the following table. + + 28 + www.rock-chips.com + + Error Code Message + +RKNN_SUCC(0) Execution is successful + +RKNN_ERR_FAIL (-1) Execution error + +RKNN_ERR_TIMEOUT (-2) Execution timeout + +RKNN_ERR_DEVICE_UNAVAILABLE (-3) NPU device is unavailable + +RKNN_ERR_MALLOC_FAIL (-4) Memory allocation is failed + +RKNN_ERR_PARAM_INVALID (-5) Parameter error + +RKNN_ERR_MODEL_INVALID (-6) RKNN model is invalid + +RKNN_ERR_CTX_INVALID (-7) rknn_context is invalid + +RKNN_ERR_INPUT_INVALID (-8) rknn_input object is invalid + +RKNN_ERR_OUTPUT_INVALID (-9) rknn_output object is invalid + +RKNN_ERR_DEVICE_UNMATCH (-10) Version does not match + +RKNN_ERR_INCOMPATILE_PRE_COMPILE_ This RKNN model use pre_compile mode, but not + +MODEL (-11) compatible with current driver. + +RKNN_ERR_INCOMPATILE_OPTIMIZATION_ This RKNN model use optimization level mode, but not + +LEVEL_VERSION (-12) compatible with current driver. + +RKNN_ERR_TARGET_PLATFORM_UNMATC This RKNN model don’t compatible with current + +H (-13) platform. + +RKNN_ERR_NON_PRE_COMPILED_MODEL The RKNN model is not in pre_compile mode and cannot + +_ON_MINI_DRIVER(-14) be executed on mini-driver. + +4 Advanced API instructions +4.1 Matmul Operator library + +4.1.1 Introduction + + The high-level API is designed to use the high computing power of the NPU to perform specific + + 29 + www.rock-chips.com + +mathematical operations, provide a simple interface call, and achieve the effect of computing acceleration. +Among them, the Matmul operator library is an acceleration library for fixed-point matrix multiplication. +The operation is defined as follows: + + C  AT * B + Here: + A, B and C are 2-dimensional matrices + A is an K*M matrix + B is a K*N matrix + C is an M*N matrix + +4.1.2 Data structure definition + +rknn_matmul_handle_t represents the handle used to perform the operation of the Matmul + +operator, which contains the context of the runtime environment and the information of the input + +buffer. The definition of the structure is shown in the following table: + +Field Type Meaning + +A void* The pointer of the first matrix buffer during + + operation. + +B void* The pointer of the second matrix buffer during + + operation. + +M int32_t The low rank dimension of A matrix. + +K int32_t The high rank dimension of A and B matrix. + +N int32_t The low rank dimension of B matrix. + +in_dtype rknn_tensor_type The type of input data. + +rknn_ctx rknn_context The context object at runtime. + + 30 + www.rock-chips.com + +4.1.3 Detailed API description + +4.1.3.1 rknn_matmul_load + + The rknn_matmul_load loading function will load the input buffer created by the user and + + return an object of type rknn_matmul_handle_t.The Matmul operator API is not responsible for + + managing the life cycle of the input buffer, and the user must ensure that the input buffer is valid + + within the Matmul operator API call. + +API rknn_matmul_load + +Description Initialize and set the input buffer pointer. + +Parameter void *a:The pointer of the first matrix buffer created by the user can only support the + + input of 8-bit unsigned integer or 8-bit signed integer one-dimensional array pointer. + + void *b:The pointer of the second matrix buffer created by the user only supports the + + input of 8-bit unsigned integer or 8-bit signed integer one-dimensional array pointer. + + int32_t M: The low rank dimension of A matrix. + + int32_t K: The high rank dimension of A and B matrix. + + int32_t N: The low rank dimension of B matrix. + + rknn_tensor_type dtype : The input data type specified by the user, only supports + + RKNN_TENSOR_INT8 or RKNN_TENSOR_UINT8 type + +Return rknn_matmul_handle_t object。 + + The sample code is as follows: + + rknn_tensor_type dtype = RKNN_TENSOR_INT8; + int8_t x[256*1] = {0}; + int8_t y[256*4096] = {0}; + rknn_matmul_handle_t handle= rknn_matmul_load(x,y,1,256,4096,dtype); + +4.1.3.2 rknn_matmul_run + After rknn_matmul_load is called and before rknn_matmul_run is executed, the data in the + + 31 + www.rock-chips.com + + input buffer is updated externally, without calling rknn_matmul_load again. + +API rknn_matmul_run + +Description Perform Matmul operations. + +Parameter rknn_matmul_handle_t matmul_handle: The handle returned by the + + rknn_matmul_load interface. + + float *c: The pointer to the matrix floating-point buffer created by the user, used to + + obtain the output. + +Return int: Error code (See RKNN Error Code). + + The sample code is as follows: + + ... + float out_fp32_buf[4096] = {0}; + rknn_matmul_run(handle,out_fp32_buf); + +4.1.3.3 rknn_matmul_unload + +API rknn_matmul_unload + +Description Destroy the Matmul operator runtime context. + +Parameter rknn_matmul_handle_t matmul_handle: The handle returned by the + + rknn_matmul_load interface. + +Return int: Error code (See RKNN Error Code). + + The sample code is as follows: + + ... + rknn_matmul_unload(handle); + +4.1.4 Implementation restrictions + + The Matmul operator library is implemented based on the hardware architecture of the NPU. In +order to achieve a balance between accuracy and speed, there are some restrictions as follows. + + 32 + www.rock-chips.com + +4.1.4.1 Dimensional restrictions + + According to the above operation description, the library realizes the matrix multiplication of M=1. +Specifically, the input A of the Matmul operator must be a Kx1 buffer(row-major), that is, the user must +create a piece of data that contains K 8-bit unsigned integers or 8-bit signed integers. When the operator +library is running on the Mini driver, the value of K can only be set to 128 or 256 or 512, and N is fixed +at 4096. When running on the Full driver, there is no such limit, but the recommended number of K is +128, 256, 512, 1024, 2048, N is an even power of 2. It is recommended that N should not be greater than +4096. + +4.1.4.2 Input data type restriction + + Only supports 8-bit unsigned integer and 8-bit signed integer input. + +4.1.5 Benchmark + + When the two input matrices use random numbers, the measured results on the RV1109-EVB board +are shown in Table-1. The speed is the average time after the rknn_matmul_run interface is called 100 +times. The average relative error is the error value of the result of executing the same algorithm on the +NPU and CPU. The specific formula is: + + (abs(R1  R2 ) / R2 ) / N + + k + + among them, + R1 is the output vector of the Matmul operator library, containing N elements. + R2 is the CPU output vector, containing N elements. + + 33 + www.rock-chips.com + + Table-1 Speed/accuracy results of Matmul operator library (RV1109, int8) + +K N Speed(ms) Average relative error + +128 1024 1.0 0.00034 + +256 1024 1.6 0.00032 + +512 2024 3.0 -0.00015 + +1024 1024 5.4 0.00047 + +128 4096 3.0 0.00051 + +256 4096 5.6 0.00024 + +512 4096 10.7 0.00024 + +1024 4096 20.9 0.00051 + +Note: the speed may vary slightly due to different NPU driver versions. The error value may varies + +slightly according to the random number of each test. + + 34 + www.rock-chips.com + +5 NPU driver description + +5.1.1 Directory structure description + + The NPU driver is in the $SDK/external/rknpu/drivers/ directory or +https://github.com/rockchip-linux/rknpu/tree/master/drivers +The compilation and installation rules refer to $SDK/buildroot/package/rockchip/rknpu/rknpu.mk +drivers/ +├── common +├── linux-aarch64 (for RK1808 npu full driver) +├── linux-aarch64-mini (for RK1808 npu mini driver) +├── linux-armhf (for RK1806 npu full driver) +├── linux-armhf-mini(for RK1806 npu mini driver) +├── linux-armhf-puma (for RV1126/RV1109 npu full driver) +├── linux-armhf-puma-mini(for RV1126/RV1109 npu mini driver) +├── npu_ko (NPU kernel driver) + +5.1.2 The difference between NPU full driver and mini driver + + Include the following points: + 1) Mini driver only supports pre-compiled rknn model. If you run non-pre-compiled model, +RKNN_ERR_MODEL_INVALID error will appear. Starting from 1.6.0, it will return +RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER error; + 2) The full driver supports the online debugging function of RKNN Toolkit, but the mini driver does +not; + 3) Mini driver library size is much smaller than full driver. Taking RV1109/RV1126 1.6.0 driver as +an example, full driver size is 87MB, mini driver size is 7.1MB, which can effectively save flash size. + 4) Mini driver library occupies less memory than full driver when running. + + 35 + www.rock-chips.com + +6 FAQ + +6.1.1 Input and output data format issues + +6.1.1.1 How to choose from RGB or BGR format when given three-channel image data + input? + + It is recommended that using RGB format input data uniformly. When exporting the RKNN model, +there are two possibilities for the reorder_channel parameter of the config function: + + 1) If the original model is trained using BGR images, reorder_channel = '2 1 0'. + 2) If the original model is trained using RGB images, reorder_channel = '0 1 2'. + +6.1.1.2 Which RKNN_TENSOR_NHWC or RKNN_TENSOR_NCHW should be set in the + rknn_input structure? Why are the two settings time-consuming different? + + The rknn_input structure is determined according to the user's own data format, and the C API will +automatically convert it into the format required by the NPU. + + The reason for the different time-consuming is that different input formats have different calculation +amounts and different optimization methods. + +6.1.1.3 For the non-quantized RKNN model, why is the size in the output + rknn_tensor_attr different from the size of the rknn_output returned by the + rknn_outputs_get interface? + + The non-quantized RKNN model, the internal output data type of the NPU is float16, and the size is +the number of elements * 2 bytes. When the user sets want_float = 1, what they want is float32 data, +float16 will be converted to float32, and the size is the number of elements * 4 bytes. + + 36 + www.rock-chips.com + +6.1.1.4 Is rknn_output.index set by user or return by driver? + +Return by driver. + +6.1.1.5 Why does the dims array of the rknn_tensor_attr have 0? + +0 means the size is invalid. n_dims in rknn_tensor_attr represents the number of valid size in dims. + +6.1.1.6 The order of dims in rknn_tensor_attr is opposite to the order of numpy obtained + by rknn_toolkit? + + The layout of arrays in C API is the opposite of python. For example, the numpy output shape +obtained by the run() interface of rknn-toolkit is [1,255,20,20], and the dims array in C API is +{20,20,255,1}. + +6.1.2 Input and output interface usage problems + +6.1.2.1 How to preprocess the data when using pass_through and using + rknn_inputs_map interface? + + Please refer to the rknn_pass_through_demo example under +https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples. + +6.1.2.2 Why is the physical address obtained using rknn_inputs_map or + rknn_outputs_map invalid? How to obtain a valid physical address? + + Input/output cannot be allocated to physically contiguous memory. The possible reasons are: + 1) The input/output size is too large, exceeding the total physically contiguous memory size (the +default is 4MB). + 2) There is not enough physically contiguous memory available in the system. + 3) When exporting the RKNN model, add the following parameter to the config function: + + 37 + www.rock-chips.com + +output_optimize=1. + Users can try to restart the system, or configure a larger physically continuous memory space when + +the NPU driver is mounted. For the configuration method, refer to the rknn_inputs_map interface +description. + +6.1.3 API call process issues + +6.1.3.1 After rknn_init succeeds, can the memory occupied by the model file be + released? + + Yes,you can. + +6.1.3.2 When rknn_output.is_prealloc=1, does rknn_outputs_release need to be called? + + Yes, it needs. + +6.1.4 Performance issues + +6.1.4.1 rknn_init takes too long? + + Use pre-compiled models. Refer to the relevant chapters of the User Guide document under +https://github.com/rockchip-linux/rknn-toolkit/tree/master/doc for usage. + +6.1.4.2 rknn_inputs_set takes too long? + + The possible reason is the large amount of data or the time-consuming format conversion. If it takes +a long time to convert the format, users can try the pass_through usage to do the conversion themselves. +For conversion method, please refer to rknn_pass_through_demo example under +https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples, or try to export the RKNN +model by adding the following parameter to the config function: output_optimize=1. + + 38 + www.rock-chips.com + +6.1.4.3 rknn_outputs_get takes too long? + + The possible reason is the large amount of data or the time-consuming format conversion. If it takes +a long time to convert the format, users can try to set want_float=0 and do the conversion themselves, or +try to export the RKNN model by adding the following parameter to the config function: +output_optimize=1. + + 39 + diff --git a/libs/rklibs/rknn/include/rknn_runtime.h b/libs/rklibs/rknn/include/rknn_runtime.h new file mode 100644 index 0000000..a114c1f --- /dev/null +++ b/libs/rklibs/rknn/include/rknn_runtime.h @@ -0,0 +1,504 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + + +#ifndef _RKNN_RUNTIME_H +#define _RKNN_RUNTIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + Definition of extended flag for rknn_init. +*/ +/* set high priority context. */ +#define RKNN_FLAG_PRIOR_HIGH 0x00000000 + +/* set medium priority context */ +#define RKNN_FLAG_PRIOR_MEDIUM 0x00000001 + +/* set low priority context. */ +#define RKNN_FLAG_PRIOR_LOW 0x00000002 + +/* asynchronous mode. + when enable, rknn_outputs_get will not block for too long because it directly retrieves the result of + the previous frame which can increase the frame rate on single-threaded mode, but at the cost of + rknn_outputs_get not retrieves the result of the current frame. + in multi-threaded mode you do not need to turn this mode on. */ +#define RKNN_FLAG_ASYNC_MASK 0x00000004 + +/* collect performance mode. + when enable, you can get detailed performance reports via rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, ...), + but it will reduce the frame rate. */ +#define RKNN_FLAG_COLLECT_PERF_MASK 0x00000008 + +/* + save pre-compile model. +*/ +#define RKNN_FLAG_PRECOMPILE_MASK 0x00000020 + +/* + Error code returned by the RKNN API. +*/ +#define RKNN_SUCC 0 /* execute succeed. */ +#define RKNN_ERR_FAIL -1 /* execute failed. */ +#define RKNN_ERR_TIMEOUT -2 /* execute timeout. */ +#define RKNN_ERR_DEVICE_UNAVAILABLE -3 /* device is unavailable. */ +#define RKNN_ERR_MALLOC_FAIL -4 /* memory malloc fail. */ +#define RKNN_ERR_PARAM_INVALID -5 /* parameter is invalid. */ +#define RKNN_ERR_MODEL_INVALID -6 /* model is invalid. */ +#define RKNN_ERR_CTX_INVALID -7 /* context is invalid. */ +#define RKNN_ERR_INPUT_INVALID -8 /* input is invalid. */ +#define RKNN_ERR_OUTPUT_INVALID -9 /* output is invalid. */ +#define RKNN_ERR_DEVICE_UNMATCH -10 /* the device is unmatch, please update rknn sdk + and npu driver/firmware. */ +#define RKNN_ERR_INCOMPATILE_PRE_COMPILE_MODEL -11 /* This RKNN model use pre_compile mode, but not compatible with current driver. */ +//add by chifred: for reporting optimization version bug info +#define RKNN_ERR_INCOMPATILE_OPTIMIZATION_LEVEL_VERSION -12 /* This RKNN model set optimization level, but not compatible with current driver. */ +#define RKNN_ERR_TARGET_PLATFORM_UNMATCH -13 /* This RKNN model set target platform, but not compatible with current platform. */ +//chifred add end +#define RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER -14 /* This RKNN model is not a pre-compiled model, but the npu driver is mini driver. */ + +/* + Definition for tensor +*/ +#define RKNN_MAX_DIMS 16 /* maximum dimension of tensor. */ +#define RKNN_MAX_NAME_LEN 256 /* maximum name lenth of tensor. */ + + + +#ifdef __arm__ +typedef uint32_t rknn_context; +#else +typedef uint64_t rknn_context; +#endif + +/* + The query command for rknn_query +*/ +typedef enum _rknn_query_cmd { + RKNN_QUERY_IN_OUT_NUM = 0, /* query the number of input & output tensor. */ + RKNN_QUERY_INPUT_ATTR, /* query the attribute of input tensor. */ + RKNN_QUERY_OUTPUT_ATTR, /* query the attribute of output tensor. */ + RKNN_QUERY_PERF_DETAIL, /* query the detail performance, need set + RKNN_FLAG_COLLECT_PERF_MASK when call rknn_init. */ + RKNN_QUERY_PERF_RUN, /* query the time of run. */ + RKNN_QUERY_SDK_VERSION, /* query the sdk & driver version */ + RKNN_QUERY_PRE_COMPILE, /* query the pre compile model */ + + RKNN_QUERY_CMD_MAX +} rknn_query_cmd; + +/* + the tensor data type. +*/ +typedef enum _rknn_tensor_type { + RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */ + RKNN_TENSOR_FLOAT16, /* data type is float16. */ + RKNN_TENSOR_INT8, /* data type is int8. */ + RKNN_TENSOR_UINT8, /* data type is uint8. */ + RKNN_TENSOR_INT16, /* data type is int16. */ + + RKNN_TENSOR_TYPE_MAX +} rknn_tensor_type; + +/* + the quantitative type. +*/ +typedef enum _rknn_tensor_qnt_type { + RKNN_TENSOR_QNT_NONE = 0, /* none. */ + RKNN_TENSOR_QNT_DFP, /* dynamic fixed point. */ + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC, /* asymmetric affine. */ + + RKNN_TENSOR_QNT_MAX +} rknn_tensor_qnt_type; + +/* + the tensor data format. +*/ +typedef enum _rknn_tensor_format { + RKNN_TENSOR_NCHW = 0, /* data format is NCHW. */ + RKNN_TENSOR_NHWC, /* data format is NHWC. */ + + RKNN_TENSOR_FORMAT_MAX +} rknn_tensor_format; + +/* + the information for RKNN_QUERY_IN_OUT_NUM. +*/ +typedef struct _rknn_input_output_num { + uint32_t n_input; /* the number of input. */ + uint32_t n_output; /* the number of output. */ +} rknn_input_output_num; + +/* + the information for RKNN_QUERY_INPUT_ATTR / RKNN_QUERY_OUTPUT_ATTR. +*/ +typedef struct _rknn_tensor_attr { + uint32_t index; /* input parameter, the index of input/output tensor, + need set before call rknn_query. */ + + uint32_t n_dims; /* the number of dimensions. */ + uint32_t dims[RKNN_MAX_DIMS]; /* the dimensions array. */ + char name[RKNN_MAX_NAME_LEN]; /* the name of tensor. */ + + uint32_t n_elems; /* the number of elements. */ + uint32_t size; /* the bytes size of tensor. */ + + rknn_tensor_format fmt; /* the data format of tensor. */ + rknn_tensor_type type; /* the data type of tensor. */ + rknn_tensor_qnt_type qnt_type; /* the quantitative type of tensor. */ + int8_t fl; /* fractional length for RKNN_TENSOR_QNT_DFP. */ + uint32_t zp; /* zero point for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ + float scale; /* scale for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ +} rknn_tensor_attr; + +/* + the information for RKNN_QUERY_PERF_DETAIL. +*/ +typedef struct _rknn_perf_detail { + char* perf_data; /* the string pointer of perf detail. don't need free it by user. */ + uint64_t data_len; /* the string length. */ +} rknn_perf_detail; + +/* + the information for RKNN_QUERY_PERF_RUN. +*/ +typedef struct _rknn_perf_run { + int64_t run_duration; /* real inference time (us) */ +} rknn_perf_run; + +/* + the information for RKNN_QUERY_SDK_VERSION. +*/ +typedef struct _rknn_sdk_version { + char api_version[256]; /* the version of rknn api. */ + char drv_version[256]; /* the version of rknn driver. */ +} rknn_sdk_version; + +/* + The flags of rknn_tensor_mem. +*/ +typedef enum _rknn_tensor_mem_flags { + RKNN_TENSOR_MEMORY_FLAGS_UNKNOWN = 0, + RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE = 1, /*Used to mark in rknn_destroy_mem() whether it is necessary to release the "mem" pointer itself. + If the flag RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE is set, rknn_destroy_mem() will call free(mem).*/ + +} rknn_tensor_mem_flags; + + +/* + the memory information of tensor. +*/ +typedef struct _rknn_tensor_memory { + void* logical_addr; /* the virtual address of tensor buffer. */ + uint64_t physical_addr; /* the physical address of tensor buffer. */ + int32_t fd; /* the fd of tensor buffer. */ + uint32_t size; /* the size of tensor buffer. */ + uint32_t handle; /* the handle tensor buffer. */ + void * priv_data; /* the data which is reserved. */ + uint64_t reserved_flag; /* the flag which is reserved. */ +} rknn_tensor_mem; + +/* + the input information for rknn_input_set. +*/ +typedef struct _rknn_input { + uint32_t index; /* the input index. */ + void* buf; /* the input buf for index. */ + uint32_t size; /* the size of input buf. */ + uint8_t pass_through; /* pass through mode. + if TRUE, the buf data is passed directly to the input node of the rknn model + without any conversion. the following variables do not need to be set. + if FALSE, the buf data is converted into an input consistent with the model + according to the following type and fmt. so the following variables + need to be set.*/ + rknn_tensor_type type; /* the data type of input buf. */ + rknn_tensor_format fmt; /* the data format of input buf. + currently the internal input format of NPU is NCHW by default. + so entering NCHW data can avoid the format conversion in the driver. */ +} rknn_input; + +/* + the output information for rknn_outputs_get. +*/ +typedef struct _rknn_output { + uint8_t want_float; /* want transfer output data to float */ + uint8_t is_prealloc; /* whether buf is pre-allocated. + if true, the following variables need to be set. + if false, The following variables do not need to be set. */ + uint32_t index; /* the output index. */ + void* buf; /* the output buf for index. + when is_prealloc = FALSE and rknn_outputs_release called, + this buf pointer will be free and don't use it anymore. */ + uint32_t size; /* the size of output buf. */ +} rknn_output; + +/* + the extend information for rknn_run. +*/ +typedef struct _rknn_run_extend { + uint64_t frame_id; /* output parameter, indicate current frame id of run. */ +} rknn_run_extend; + +/* + the extend information for rknn_outputs_get. +*/ +typedef struct _rknn_output_extend { + uint64_t frame_id; /* output parameter, indicate the frame id of outputs, corresponds to + struct rknn_run_extend.frame_id.*/ +} rknn_output_extend; + +/* + the information for RKNN_QUERY_RKNN_PRECOMPILE. +*/ +typedef struct _rknn_precompile { + void* model_data; /* the pointer of precompile model. don't need free it by user. */ + uint32_t data_len; /* the model length. */ +} rknn_precompile; + + +/* rknn_init + + initial the context and load the rknn model. + + input: + rknn_context* context the pointer of context handle. + void* model pointer to the rknn model. + uint32_t size the size of rknn model. + uint32_t flag extend flag, see the define of RKNN_FLAG_XXX_XXX. + return: + int error code. +*/ +int rknn_init(rknn_context* context, void* model, uint32_t size, uint32_t flag); + + +/* rknn_destroy + + unload the rknn model and destroy the context. + + input: + rknn_context context the handle of context. + return: + int error code. +*/ +int rknn_destroy(rknn_context context); + + +/* rknn_query + + query the information about model or others. see rknn_query_cmd. + + input: + rknn_context context the handle of context. + rknn_query_cmd cmd the command of query. + void* info the buffer point of information. + uint32_t size the size of information. + return: + int error code. +*/ +int rknn_query(rknn_context context, rknn_query_cmd cmd, void* info, uint32_t size); + + +/* rknn_inputs_set + + set inputs information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_input inputs[] the arrays of inputs information, see rknn_input. + return: + int error code +*/ +int rknn_inputs_set(rknn_context context, uint32_t n_inputs, rknn_input inputs[]); + + +/* rknn_inputs_map + + map inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_map(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_sync + + synchronize inputs tensor buffer by input index of rknn model. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_sync(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_unmap + + unmap inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_unmap(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_run + + run the model to execute inference. + + input: + rknn_context context the handle of context. + rknn_run_extend* extend the extend information of run. + return: + int error code. +*/ +int rknn_run(rknn_context context, rknn_run_extend* extend); + + +/* rknn_outputs_get + + wait the inference to finish and get the outputs. + this function will block until inference finish. + the results will set to outputs[]. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_output outputs[] the arrays of output, see rknn_output. + rknn_output_extend* the extend information of output. + return: + int error code. +*/ +int rknn_outputs_get(rknn_context context, uint32_t n_outputs, rknn_output outputs[], rknn_output_extend* extend); + + +/* rknn_outputs_release + + release the outputs that get by rknn_outputs_get. + after called, the rknn_output[x].buf get from rknn_outputs_get will + also be free when rknn_output[x].is_prealloc = FALSE. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_output outputs[] the arrays of output. + return: + int error code +*/ +int rknn_outputs_release(rknn_context context, uint32_t n_ouputs, rknn_output outputs[]); + + +/* rknn_outputs_map + + map the model output tensors memory information. + The difference between this function and "rknn_outputs_get" is + that it directly maps the model output tensor memory location to the user. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_map(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_sync + + synchronize the output tensors buffer to ensure cache cohenrency, wait the inference to finish. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_sync(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_unmap + + unmap the outputs memory information that get by rknn_outputs_map. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_outputs_unmap(rknn_context context, uint32_t n_ouputs, rknn_tensor_mem mem[]); + +/* rknn_create_mem (memory allocated inside) + + Create tensor memory. This API require libdrm support! + + input: + rknn_context ctx the handle of context. + uint64_t size the size of tensor buffer. + return: + rknn_tensor_mem the pointer of tensor memory information. +*/ +rknn_tensor_mem* rknn_create_mem(rknn_context ctx, uint64_t size); + +/* rknn_destroy_mem (support allocate inside and outside) + + destroy tensor memory. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the pointer of tensor memory information. + return: + int error code +*/ +int rknn_destroy_mem(rknn_context ctx, rknn_tensor_mem *mem); + + + +/* rknn_set_io_mem + + set the input and output tensors buffer. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the array of tensor memory information. + rknn_tensor_attr *attr the attribute of input or output tensor buffer. + return: + int error code. +*/ +int rknn_set_io_mem(rknn_context ctx, rknn_tensor_mem *mem, rknn_tensor_attr *attr); + +#ifdef __cplusplus +} //extern "C" +#endif + +#endif //_RKNN_RUNTIME_H diff --git a/libs/rklibs/rknn/python/example/mobilenet_v1/dog_224x224.jpg b/libs/rklibs/rknn/python/example/mobilenet_v1/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknn/python/example/mobilenet_v1/dog_224x224.jpg differ diff --git a/libs/rklibs/rknn/python/example/mobilenet_v1/mobilenet_v1.rknn b/libs/rklibs/rknn/python/example/mobilenet_v1/mobilenet_v1.rknn new file mode 100644 index 0000000..1a1ff1b Binary files /dev/null and b/libs/rklibs/rknn/python/example/mobilenet_v1/mobilenet_v1.rknn differ diff --git a/libs/rklibs/rknn/python/example/mobilenet_v1/test.py b/libs/rklibs/rknn/python/example/mobilenet_v1/test.py new file mode 100644 index 0000000..2484b68 --- /dev/null +++ b/libs/rklibs/rknn/python/example/mobilenet_v1/test.py @@ -0,0 +1,49 @@ +import numpy as np +import cv2 +from rknn.api import RKNN + +def show_outputs(outputs): + output = outputs[0][0] + output_sorted = sorted(output, reverse=True) + top5_str = 'mobilenet_v1\n-----TOP 5-----\n' + for i in range(5): + value = output_sorted[i] + index = np.where(output == value) + for j in range(len(index)): + if (i + j) >= 5: + break + if value > 0: + topi = '{}: {}\n'.format(index[j], value) + else: + topi = '-1: 0.0\n' + top5_str += topi + print(top5_str) + +if __name__ == '__main__': + + # Create RKNN object + rknn = RKNN() + + # Direct load rknn model + print('--> Loading RKNN model') + ret = rknn.load_rknn('./mobilenet_v1.rknn') + if ret != 0: + print('Load mobilenet_v1.rknn failed!') + exit(ret) + print('done') + + # Set inputs + img = cv2.imread('./dog_224x224.jpg') + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # Init Runtime + rknn.init_runtime() + + # Inference + print('--> Running model') + outputs = rknn.inference(inputs=[img]) + show_outputs(outputs) + print('done') + + rknn.release() + diff --git a/libs/rklibs/rknn/python/rknn/README b/libs/rklibs/rknn/python/rknn/README new file mode 100644 index 0000000..7beab1a --- /dev/null +++ b/libs/rklibs/rknn/python/rknn/README @@ -0,0 +1 @@ +# RKNN \ No newline at end of file diff --git a/libs/rklibs/rknn/python/rknn/VERSION b/libs/rklibs/rknn/python/rknn/VERSION new file mode 100644 index 0000000..d612b7f --- /dev/null +++ b/libs/rklibs/rknn/python/rknn/VERSION @@ -0,0 +1 @@ +0.9.7.1 diff --git a/libs/rklibs/rknn/python/rknn/__init__.py b/libs/rklibs/rknn/python/rknn/__init__.py new file mode 100644 index 0000000..cfc82a4 --- /dev/null +++ b/libs/rklibs/rknn/python/rknn/__init__.py @@ -0,0 +1 @@ +from rknn import api diff --git a/libs/rklibs/rknn/python/rknn/api/__init__.py b/libs/rklibs/rknn/python/rknn/api/__init__.py new file mode 100644 index 0000000..5b98fb2 --- /dev/null +++ b/libs/rklibs/rknn/python/rknn/api/__init__.py @@ -0,0 +1 @@ +from rknn.api.rknn import RKNN diff --git a/libs/rklibs/rknn/python/rknn/api/rknn.py b/libs/rklibs/rknn/python/rknn/api/rknn.py new file mode 100644 index 0000000..42b7e69 --- /dev/null +++ b/libs/rklibs/rknn/python/rknn/api/rknn.py @@ -0,0 +1,130 @@ +# -*- coding:utf-8 -*- +import os +import sys +import traceback +import re + +from argparse import Namespace +from .rknn_base import RKNNBase +from .rknn_runtime import RKNNRuntime + +class RKNN: + """ + Rockchip NN SDK + """ + def __init__(self): + cur_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + self.rknn_base = RKNNBase(cur_path) + + def load_rknn(self, path): + """ + Load RKNN model + :param path: RKNN model file path + :return: success: 0, failure: -1 + """ + if not os.path.exists(path): + print('RKNN model file {} not exists!'.format(path)) + return -1 + try: + ret = self.rknn_base.import_rknn(path) + except: + print('Catch exception when loading RKNN model [{}]!'.format(path)) + RKNN._print_traceback(traceback.format_exc()) + return -1 + else: + if ret != 0: + print('Load RKNN model [{}] failed!'.format(path)) + return ret + + def init_runtime(self, perf_debug=False): + """ + Init run time environment. Needed by called before inference or eval performance. + :param perf_debug: enable dump layer performance. + :return: success: 0, failure: -1 + """ + try: + self.rknn_base.init_runtime(target=None, device_id=None, perf_debug=perf_debug) + except: + print('Catch exception when init runtime!') + RKNN._print_traceback(traceback.format_exc()) + return -1 + + return 0 + + def inference(self, inputs, data_type='uint8', data_format='nhwc', outputs=None): + """ + Run model inference + :param inputs: Input data List (ndarray list) + :param data_type: Input data type + :param data_format: Input data format + :param outputs: Output data list, for determine shape and dtype (empty ndarray) + :return: Output data (ndarray list) + """ + try: + results = self.rknn_base.inference(inputs=inputs, data_type=data_type, + data_format=data_format, outputs=outputs) + except: + print('Catch exception when inference!') + RKNN._print_traceback(traceback.format_exc()) + results = None + finally: + useless_file = os.path.join(os.getcwd(), 'viv_vir_cl_intrinsic_ImgLS.lib') + if os.path.exists(useless_file): + os.remove(useless_file) + return results + + + def eval_perf(self, inputs, data_type='uint8', data_format='nhwc', is_print=True): + """ + Evaluate model performance + :param inputs: Input data list (ndarray list) + :param data_type: Input data type + :param data_format: Input data format + :param is_print: Format print perf result + :return: Performance Result (dict) + """ + try: + perfs = self.rknn_base.eval_performance(inputs, data_type, data_format) + if perfs is None: + print('Error: Performance information is empty, some mistakes may happen.') + return perfs + if is_print: + print(self.rknn_base.format_perf_detail(detail=perfs)) + except: + print('Catch exception when evaluating model performance!') + RKNN._print_traceback(traceback.format_exc()) + return None + finally: + useless_file = os.path.join(os.getcwd(), 'viv_vir_cl_intrinsic_ImgLS.lib') + if os.path.exists(useless_file): + os.remove(useless_file) + + return perfs + + def get_sdk_version(self): + """ + Get SDK version + :return: sdk_version + """ + try: + sdk_version = self.rknn_base.get_sdk_version() + except Exception: + print('Catch exception when get sdk version') + RKNN._print_traceback(traceback.format_exc()) + return None + + return sdk_version + + def release(self): + """ + Release RKNN resource + :return: None + """ + self.rknn_base.release() + + @staticmethod + def _print_traceback(traceback_info): + reg = re.compile(re.escape(RKNNBase.redirect_src), re.IGNORECASE) + traceback_info = reg.sub(RKNNBase.redirect_dst, traceback_info) + print(traceback_info) + diff --git a/libs/rklibs/rknn/python/rknn/api/rknn_base.cpython-36m-aarch64-linux-gnu.so b/libs/rklibs/rknn/python/rknn/api/rknn_base.cpython-36m-aarch64-linux-gnu.so new file mode 100644 index 0000000..f94360c Binary files /dev/null and b/libs/rklibs/rknn/python/rknn/api/rknn_base.cpython-36m-aarch64-linux-gnu.so differ diff --git a/libs/rklibs/rknn/python/rknn/api/rknn_model.cpython-36m-aarch64-linux-gnu.so b/libs/rklibs/rknn/python/rknn/api/rknn_model.cpython-36m-aarch64-linux-gnu.so new file mode 100644 index 0000000..533a9bb Binary files /dev/null and b/libs/rklibs/rknn/python/rknn/api/rknn_model.cpython-36m-aarch64-linux-gnu.so differ diff --git a/libs/rklibs/rknn/python/rknn/api/rknn_runtime.cpython-36m-aarch64-linux-gnu.so b/libs/rklibs/rknn/python/rknn/api/rknn_runtime.cpython-36m-aarch64-linux-gnu.so new file mode 100644 index 0000000..f98b431 Binary files /dev/null and b/libs/rklibs/rknn/python/rknn/api/rknn_runtime.cpython-36m-aarch64-linux-gnu.so differ diff --git a/libs/rklibs/rknn/python/rknn1808-1.2.0-cp36-cp36m-linux_aarch64.whl b/libs/rklibs/rknn/python/rknn1808-1.2.0-cp36-cp36m-linux_aarch64.whl new file mode 100644 index 0000000..0b99684 Binary files /dev/null and b/libs/rklibs/rknn/python/rknn1808-1.2.0-cp36-cp36m-linux_aarch64.whl differ diff --git a/libs/rklibs/rknn/python/rknn1808-1.2.0-cp37-cp37m-linux_aarch64.whl b/libs/rklibs/rknn/python/rknn1808-1.2.0-cp37-cp37m-linux_aarch64.whl new file mode 100644 index 0000000..08ca084 Binary files /dev/null and b/libs/rklibs/rknn/python/rknn1808-1.2.0-cp37-cp37m-linux_aarch64.whl differ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/CImg/CImg.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/CImg/CImg.h new file mode 100644 index 0000000..2bfe567 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/CImg/CImg.h @@ -0,0 +1,65138 @@ +/* + # + # File : CImg.h + # ( C++ header file ) + # + # Description : C++ Template Image Processing Toolkit. + # This file is the main component of the CImg Library project. + # ( http://cimg.eu ) + # + # Project manager : David Tschumperlé + # ( http://tschumperle.users.greyc.fr/ ) + # + # A complete list of contributors is available in file 'README.txt' + # distributed within the CImg package. + # + # Licenses : This file is 'dual-licensed', you have to choose one + # of the two licenses below to apply. + # + # CeCILL-C + # The CeCILL-C license is close to the GNU LGPL. + # ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html ) + # + # or CeCILL v2.1 + # The CeCILL license is compatible with the GNU GPL. + # ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html ) + # + # This software is governed either by the CeCILL or the CeCILL-C license + # under French law and abiding by the rules of distribution of free software. + # You can use, modify and or redistribute the software under the terms of + # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA + # at the following URL: "http://cecill.info". + # + # As a counterpart to the access to the source code and rights to copy, + # modify and redistribute granted by the license, users are provided only + # with a limited warranty and the software's author, the holder of the + # economic rights, and the successive licensors have only limited + # liability. + # + # In this respect, the user's attention is drawn to the risks associated + # with loading, using, modifying and/or developing or reproducing the + # software by the user in light of its specific status of free software, + # that may mean that it is complicated to manipulate, and that also + # therefore means that it is reserved for developers and experienced + # professionals having in-depth computer knowledge. Users are therefore + # encouraged to load and test the software's suitability as regards their + # requirements in conditions enabling the security of their systems and/or + # data to be ensured and, more generally, to use and operate it in the + # same conditions as regards security. + # + # The fact that you are presently reading this means that you have had + # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. + # +*/ + +// Set version number of the library. +#ifndef cimg_version +#define cimg_version 298 + +/*----------------------------------------------------------- + # + # Test and possibly auto-set CImg configuration variables + # and include required headers. + # + # If you find that the default configuration variables are + # not adapted to your system, you can override their values + # before including the header file "CImg.h" + # (use the #define directive). + # + ------------------------------------------------------------*/ + +// Include standard C++ headers. +// This is the minimal set of required headers to make CImg-based codes compile. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define cimg_str(x) #x +#define cimg_str2(x) cimg_str(x) + +// Detect/configure OS variables. +// +// Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies). +// '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). +// '2' for Microsoft Windows. +// (auto-detection is performed if 'cimg_OS' is not set by the user). +#ifndef cimg_OS +#if defined(unix) || defined(__unix) || defined(__unix__) \ + || defined(linux) || defined(__linux) || defined(__linux__) \ + || defined(sun) || defined(__sun) \ + || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \ + || defined(__FreeBSD__) || defined (__DragonFly__) \ + || defined(sgi) || defined(__sgi) \ + || defined(__OSX__) || defined(__MACOSX__) || defined(__APPLE__) \ + || defined(__CYGWIN__) +#define cimg_OS 1 +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +#define cimg_OS 2 +#else +#define cimg_OS 0 +#endif +#elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) +#error CImg Library: Invalid configuration variable 'cimg_OS'. +#error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows'). +#endif +#ifndef cimg_date +#define cimg_date __DATE__ +#endif +#ifndef cimg_time +#define cimg_time __TIME__ +#endif + +// Disable silly warnings on some Microsoft VC++ compilers. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#pragma warning(disable:4244) +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#pragma warning(disable:4319) +#pragma warning(disable:4512) +#pragma warning(disable:4571) +#pragma warning(disable:4640) +#pragma warning(disable:4706) +#pragma warning(disable:4710) +#pragma warning(disable:4800) +#pragma warning(disable:4804) +#pragma warning(disable:4820) +#pragma warning(disable:4996) + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif +#endif + +// Define correct string functions for each compiler and OS. +#if cimg_OS==2 && defined(_MSC_VER) +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#include +#if defined(__MACOSX__) || defined(__APPLE__) +#define cimg_sscanf cimg::_sscanf +#define cimg_sprintf cimg::_sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf snprintf +#define cimg_vsnprintf vsnprintf +#endif +#endif + +// Include OS-specific headers. +#if cimg_OS==1 +#include +#include +#include +#include +#include +#include +#elif cimg_OS==2 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif +#include +#include +#include +enum {FALSE_WIN = 0}; +#endif + +// Look for C++11 features. +#ifndef cimg_use_cpp11 +#if __cplusplus>201100 +#define cimg_use_cpp11 1 +#else +#define cimg_use_cpp11 0 +#endif +#endif +#if cimg_use_cpp11==1 +#include +#include +#endif + +// Convenient macro to define pragma +#ifdef _MSC_VER +#define cimg_pragma(x) __pragma(x) +#else +#define cimg_pragma(x) _Pragma(#x) +#endif + +// Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability. +// ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ). +#if cimg_OS==2 + +#define cimg_uint64 unsigned __int64 +#define cimg_int64 __int64 +#define cimg_ulong UINT_PTR +#define cimg_long INT_PTR +#ifdef _MSC_VER +#define cimg_fuint64 "%I64u" +#define cimg_fint64 "%I64d" +#else +#define cimg_fuint64 "%llu" +#define cimg_fint64 "%lld" +#endif + +#else + +#if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)) +#define cimg_uint64 unsigned long long +#define cimg_int64 long long +#define cimg_fuint64 "%llu" +#define cimg_fint64 "%lld" +#else +#define cimg_uint64 unsigned long +#define cimg_int64 long +#define cimg_fuint64 "%lu" +#define cimg_fint64 "%ld" +#endif + +#if defined(__arm__) || defined(_M_ARM) +#define cimg_ulong unsigned long long +#define cimg_long long long +#else +#define cimg_ulong unsigned long +#define cimg_long long +#endif + +#endif + +// Configure filename separator. +// +// Filename separator is set by default to '/', except for Windows where it is '\'. +#ifndef cimg_file_separator +#if cimg_OS==2 +#define cimg_file_separator '\\' +#else +#define cimg_file_separator '/' +#endif +#endif + +// Configure verbosity of output messages. +// +// Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode). +// '1' to output library messages on the console. +// '2' to output library messages on a basic dialog window (default behavior). +// '3' to do as '1' + add extra warnings (may slow down the code!). +// '4' to do as '2' + add extra warnings (may slow down the code!). +// +// Define 'cimg_strict_warnings' to replace warning messages by exception throwns. +// +// Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals. +#ifndef cimg_verbosity +#if cimg_OS==2 +#define cimg_verbosity 2 +#else +#define cimg_verbosity 1 +#endif +#elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) +#error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. +#error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). +#endif + +// Configure OpenMP support. +// (http://www.openmp.org) +// +// Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+). +// +// OpenMP directives are used in many CImg functions to get +// advantages of multi-core CPUs. +#if !defined(cimg_use_openmp) +#ifdef _OPENMP +#define cimg_use_openmp 1 +#else +#define cimg_use_openmp 0 +#endif +#endif +#if cimg_use_openmp!=0 +#include +#define cimg_pragma_openmp(p) cimg_pragma(omp p) +#else +#define cimg_pragma_openmp(p) +#endif + +// Configure the 'abort' signal handler (does nothing by default). +// A typical signal handler can be defined in your own source like this: +// #define cimg_abort_test if (is_abort) throw CImgAbortException("") +// +// where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method. +// 'cimg_abort_test2' does the same but is called more often (in inner loops). +#if defined(cimg_abort_test) && cimg_use_openmp!=0 + +// Define abort macros to be used with OpenMP. +#ifndef _cimg_abort_init_openmp +#define _cimg_abort_init_openmp bool _cimg_abort_go_openmp = true; cimg::unused(_cimg_abort_go_openmp) +#endif +#ifndef _cimg_abort_try_openmp +#define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try +#endif +#ifndef _cimg_abort_catch_openmp +#define _cimg_abort_catch_openmp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#endif +#ifndef _cimg_abort_catch_fill_openmp +#define _cimg_abort_catch_fill_openmp \ + catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg::string(e._message).move_to(is_error); \ + cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#endif +#ifdef cimg_abort_test2 +#ifndef _cimg_abort_try_openmp2 +#define _cimg_abort_try_openmp2 _cimg_abort_try_openmp +#endif +#ifndef _cimg_abort_catch_openmp2 +#define _cimg_abort_catch_openmp2 _cimg_abort_catch_openmp +#endif +#endif +#endif + +#ifndef _cimg_abort_init_openmp +#define _cimg_abort_init_openmp +#endif +#ifndef _cimg_abort_try_openmp +#define _cimg_abort_try_openmp +#endif +#ifndef _cimg_abort_catch_openmp +#define _cimg_abort_catch_openmp +#endif +#ifndef _cimg_abort_try_openmp2 +#define _cimg_abort_try_openmp2 +#endif +#ifndef _cimg_abort_catch_openmp2 +#define _cimg_abort_catch_openmp2 +#endif +#ifndef _cimg_abort_catch_fill_openmp +#define _cimg_abort_catch_fill_openmp +#endif +#ifndef cimg_abort_init +#define cimg_abort_init +#endif +#ifndef cimg_abort_test +#define cimg_abort_test +#endif +#ifndef cimg_abort_test2 +#define cimg_abort_test2 +#endif + +// Configure display framework. +// +// Define 'cimg_display' to: '0' to disable display capabilities. +// '1' to use the X-Window framework (X11). +// '2' to use the Microsoft GDI32 framework. +#ifndef cimg_display +#if cimg_OS==0 +#define cimg_display 0 +#elif cimg_OS==1 +#define cimg_display 1 +#elif cimg_OS==2 +#define cimg_display 2 +#endif +#elif !(cimg_display==0 || cimg_display==1 || cimg_display==2) +#error CImg Library: Configuration variable 'cimg_display' is badly defined. +#error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }). +#endif + +// Include display-specific headers. +#if cimg_display==1 +#include +#include +#include +#include +#ifdef cimg_use_xshm +#include +#include +#include +#endif +#ifdef cimg_use_xrandr +#include +#endif +#endif +#ifndef cimg_appname +#define cimg_appname "CImg" +#endif + +// Configure OpenCV support. +// (http://opencv.willowgarage.com/wiki/) +// +// Define 'cimg_use_opencv' to enable OpenCV support. +// +// OpenCV library may be used to access images from cameras +// (see method 'CImg::load_camera()'). +#ifdef cimg_use_opencv +#ifdef True +#undef True +#define _cimg_redefine_True +#endif +#ifdef False +#undef False +#define _cimg_redefine_False +#endif +#ifdef Status +#undef Status +#define _cimg_redefine_Status +#endif +#include +#include +#if CV_MAJOR_VERSION>=3 +#define _cimg_fourcc cv::VideoWriter::fourcc +#define _cimg_cap_prop_frame_width cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH +#define _cimg_cap_prop_frame_height cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT +#define _cimg_cap_prop_frame_count cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT +#else +#define _cimg_fourcc CV_FOURCC +#define _cimg_cap_prop_frame_width CV_CAP_PROP_FRAME_WIDTH +#define _cimg_cap_prop_frame_height CV_CAP_PROP_FRAME_HEIGHT +#define _cimg_cap_prop_frame_count CV_CAP_PROP_FRAME_COUNT +#endif +#endif + +// Configure LibPNG support. +// (http://www.libpng.org) +// +// Define 'cimg_use_png' to enable LibPNG support. +// +// PNG library may be used to get a native support of '.png' files. +// (see methods 'CImg::{load,save}_png()'. +#ifdef cimg_use_png +extern "C" { +#include "png.h" +} +#endif + +// Configure LibJPEG support. +// (http://en.wikipedia.org/wiki/Libjpeg) +// +// Define 'cimg_use_jpeg' to enable LibJPEG support. +// +// JPEG library may be used to get a native support of '.jpg' files. +// (see methods 'CImg::{load,save}_jpeg()'). +#ifdef cimg_use_jpeg +extern "C" { +#include "jpeglib.h" +#include "setjmp.h" +} +#endif + +// Configure LibTIFF support. +// (http://www.libtiff.org) +// +// Define 'cimg_use_tiff' to enable LibTIFF support. +// +// TIFF library may be used to get a native support of '.tif' files. +// (see methods 'CImg[List]::{load,save}_tiff()'). +#ifdef cimg_use_tiff +extern "C" { +#define uint64 uint64_hack_ +#define int64 int64_hack_ +#include "tiffio.h" +#undef uint64 +#undef int64 +} +#endif + +// Configure HEIF support +// (https://github.com/strukturag/libheif) +// +// Define 'cimg_use_heif' to enable HEIF support. +// +// HEIF library may be used to get a native support of '.heic' and '.avif' files. +// (see method 'CImg::load_heif()'). +#ifdef cimg_use_heif +#include +#endif + +// Configure LibMINC2 support. +// (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference) +// +// Define 'cimg_use_minc2' to enable LibMINC2 support. +// +// MINC2 library may be used to get a native support of '.mnc' files. +// (see methods 'CImg::{load,save}_minc2()'). +#ifdef cimg_use_minc2 +#include "minc_io_simple_volume.h" +#include "minc_1_simple.h" +#include "minc_1_simple_rw.h" +#endif + +// Configure Zlib support. +// (http://www.zlib.net) +// +// Define 'cimg_use_zlib' to enable Zlib support. +// +// Zlib library may be used to allow compressed data in '.cimgz' files +// (see methods 'CImg[List]::{load,save}_cimg()'). +#ifdef cimg_use_zlib +extern "C" { +#include "zlib.h" +} +#endif + +// Configure libcurl support. +// (http://curl.haxx.se/libcurl/) +// +// Define 'cimg_use_curl' to enable libcurl support. +// +// Libcurl may be used to get a native support of file downloading from the network. +// (see method 'cimg::load_network()'.) +#ifdef cimg_use_curl +#include "curl/curl.h" +#endif + +// Configure Magick++ support. +// (http://www.imagemagick.org/Magick++) +// +// Define 'cimg_use_magick' to enable Magick++ support. +// +// Magick++ library may be used to get a native support of various image file formats. +// (see methods 'CImg::{load,save}()'). +#ifdef cimg_use_magick +#include "Magick++.h" +#endif + +// Configure FFTW3 support. +// (http://www.fftw.org) +// +// Define 'cimg_use_fftw3' to enable libFFTW3 support. +// +// FFTW3 library may be used to efficiently compute the Fast Fourier Transform +// of image data, without restriction on the image size. +// (see method 'CImg[List]::FFT()'). +#ifdef cimg_use_fftw3 +extern "C" { +#include "fftw3.h" +} +#endif + +// Configure LibBoard support. +// (http://libboard.sourceforge.net/) +// +// Define 'cimg_use_board' to enable Board support. +// +// Board library may be used to draw 3D objects in vector-graphics canvas +// that can be saved as '.ps' or '.svg' files afterwards. +// (see method 'CImg::draw_object3d()'). +#ifdef cimg_use_board +#include "Board.h" +#endif + +// Configure OpenEXR support. +// (http://www.openexr.com/) +// +// Define 'cimg_use_openexr' to enable OpenEXR support. +// +// OpenEXR library may be used to get a native support of '.exr' files. +// (see methods 'CImg::{load,save}_exr()'). +#ifdef cimg_use_openexr +#if __GNUC__>=5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" +#pragma GCC diagnostic ignored "-Wdeprecated-copy" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#include "ImfRgbaFile.h" +#include "ImfInputFile.h" +#include "ImfChannelList.h" +#include "ImfMatrixAttribute.h" +#include "ImfArray.h" +#if __GNUC__>=5 +#pragma GCC diagnostic pop +#endif +#endif + +// Configure TinyEXR support. +// (https://github.com/syoyo/tinyexr) +// +// Define 'cimg_use_tinyexr' to enable TinyEXR support. +// +// TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images. +#ifdef cimg_use_tinyexr +#ifndef TINYEXR_IMPLEMENTATION +#define TINYEXR_IMPLEMENTATION +#endif +#include "tinyexr.h" +#endif + +// Lapack configuration. +// (http://www.netlib.org/lapack) +// +// Define 'cimg_use_lapack' to enable LAPACK support. +// +// Lapack library may be used in several CImg methods to speed up +// matrix computations (eigenvalues, inverse, ...). +#ifdef cimg_use_lapack +extern "C" { + extern void sgetrf_(int*, int*, float*, int*, int*, int*); + extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); + extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); + extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); + extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); + extern void dgetrf_(int*, int*, double*, int*, int*, int*); + extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); + extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); + extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, + int*, double*, int*, double*, int*, int*); + extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); + extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*); + extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*); +} +#endif + +// Check if min/max/PI macros are defined. +// +// CImg does not compile if macros 'min', 'max' or 'PI' are defined, +// because it redefines functions min(), max() and const variable PI in the cimg:: namespace. +// so it '#undef' these macros if necessary, and restore them to reasonable +// values at the end of this file. +#ifdef min +#undef min +#define _cimg_redefine_min +#endif +#ifdef max +#undef max +#define _cimg_redefine_max +#endif +#ifdef PI +#undef PI +#define _cimg_redefine_PI +#endif + +// Define 'cimg_library' namespace suffix. +// +// You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work +// with several versions of the library at the same time. +#ifdef cimg_namespace_suffix +#define __cimg_library_suffixed(s) cimg_library_##s +#define _cimg_library_suffixed(s) __cimg_library_suffixed(s) +#define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix) +#else +#define cimg_library_suffixed cimg_library +#endif + +/*------------------------------------------------------------------------------ + # + # Define user-friendly macros. + # + # These CImg macros are prefixed by 'cimg_' and can be used safely in your own + # code. They are useful to parse command line options, or to write image loops. + # + ------------------------------------------------------------------------------*/ + +// Macros to define program usage, and retrieve command line arguments. +#define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) +#define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,_default,usage) cimg_library_suffixed::cimg::option(name,argc,argv,_default,usage) + +// Macros to define and manipulate local neighborhoods. +#define CImg_2x2(I,T) T I[4]; \ + T& I##cc = I[0]; T& I##nc = I[1]; \ + T& I##cn = I[2]; T& I##nn = I[3]; \ + I##cc = I##nc = \ + I##cn = I##nn = 0 + +#define CImg_3x3(I,T) T I[9]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ + T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ + T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ + I##pp = I##cp = I##np = \ + I##pc = I##cc = I##nc = \ + I##pn = I##cn = I##nn = 0 + +#define CImg_4x4(I,T) T I[16]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ + T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ + T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ + T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ + I##pp = I##cp = I##np = I##ap = \ + I##pc = I##cc = I##nc = I##ac = \ + I##pn = I##cn = I##nn = I##an = \ + I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_5x5(I,T) T I[25]; \ + T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \ + T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \ + T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \ + T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \ + T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \ + I##bb = I##pb = I##cb = I##nb = I##ab = \ + I##bp = I##pp = I##cp = I##np = I##ap = \ + I##bc = I##pc = I##cc = I##nc = I##ac = \ + I##bn = I##pn = I##cn = I##nn = I##an = \ + I##ba = I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_2x2x2(I,T) T I[8]; \ + T& I##ccc = I[0]; T& I##ncc = I[1]; \ + T& I##cnc = I[2]; T& I##nnc = I[3]; \ + T& I##ccn = I[4]; T& I##ncn = I[5]; \ + T& I##cnn = I[6]; T& I##nnn = I[7]; \ + I##ccc = I##ncc = \ + I##cnc = I##nnc = \ + I##ccn = I##ncn = \ + I##cnn = I##nnn = 0 + +#define CImg_3x3x3(I,T) T I[27]; \ + T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ + T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ + T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ + T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ + T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ + T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ + T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ + T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ + T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ + I##ppp = I##cpp = I##npp = \ + I##pcp = I##ccp = I##ncp = \ + I##pnp = I##cnp = I##nnp = \ + I##ppc = I##cpc = I##npc = \ + I##pcc = I##ccc = I##ncc = \ + I##pnc = I##cnc = I##nnc = \ + I##ppn = I##cpn = I##npn = \ + I##pcn = I##ccn = I##ncn = \ + I##pnn = I##cnn = I##nnn = 0 + +#define cimg_def2x2(img,x,y) \ + int _n1##x = x<(img).width() - 1?x + 1:(img).width() - 1, \ + _n1##y = y<(img).height() - 1?y + 1:(img).height() - 1 + +#define cimg_def3x3(img,x,y) \ + cimg_def2x2(img,x,y); \ + int _p1##x = x>1?x - 1:0, \ + _p1##y = y>1?y - 1:0 + +#define cimg_def4x4(img,x,y) \ + cimg_def3x3(img,x,y); \ + int _n2##x = x<(img).width() - 2?x + 2:(img).width() - 1, \ + _n2##y = y<(img).height() - 2?y + 2:(img).height() - 1 + +#define cimg_def5x5(img,x,y) \ + cimg_def4x4(img,x,y); \ + int _p2##x = x>2?x - 2:0, \ + _p2##y = y>2?y - 2:0 + +#define cimg_def6x6(img,x,y) \ + cimg_def5x5(img,x,y); \ + int _n3##x = x<(img).width() - 3?x + 3:(img).width() - 1, \ + _n3##y = y<(img).height() - 3?y + 3:(img).height() - 1 + +#define cimg_def7x7(img,x,y) \ + cimg_def6x6(img,x,y); \ + int _p3##x = x>3?x - 3:0, \ + _p3##y = y>3?y - 3:0 + +#define cimg_def8x8(img,x,y) \ + cimg_def7x7(img,x,y); \ + int _n4##x = x<(img).width() - 4?x + 4:(img).width() - 1, \ + _n4##y = y<(img).height() - 4?y + 4:(img).height() - 1 + +#define cimg_def9x9(img,x,y) \ + cimg_def8x8(img,x,y); \ + int _p4##x = x>4?x - 4:0, \ + _p4##y = y>4?y - 4:0 + +#define cimg_def2x2x2(img,x,y,z) \ + cimg_def2x2(img,x,y); \ + int _n1##z = z<(img).depth() - 1?z + 1:(img).depth() - 1 + +#define cimg_def3x3x3(img,x,y,z) \ + cimg_def2x2x2(img,x,y,z); \ + int _p1##x = x>1?x - 1:0, \ + _p1##y = y>1?y - 1:0, \ + _p1##z = z>1?z - 1:0 + +#define cimg_get2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ + I[3] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \ + I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get4x4(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \ + I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \ + I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \ + I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[15] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get5x5(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ + I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \ + I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), \ + I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \ + I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get6x6(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ + I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \ + I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \ + I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), \ + I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \ + I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \ + I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), \ + I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \ + I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \ + I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get7x7(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ + I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \ + I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), \ + I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \ + I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \ + I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), \ + I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \ + I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), \ + I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \ + I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[48] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get8x8(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ + I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \ + I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \ + I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), \ + I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), \ + I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \ + I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \ + I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \ + I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \ + I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \ + I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), \ + I[63] = (T)(img)(_n4##x,_n4##y,z,c); + +#define cimg_get9x9(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), \ + I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \ + I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \ + I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \ + I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), \ + I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \ + I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \ + I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), \ + I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), \ + I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \ + I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \ + I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \ + I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \ + I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \ + I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \ + I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), \ + I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \ + I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c) + +#define cimg_get2x2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ + I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \ + I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +#define cimg_get3x3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \ + I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \ + I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \ + I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \ + I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \ + I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \ + I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \ + I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \ + I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \ + I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +// Macros to perform various image loops. +// +// These macros are simpler to use than loops with C++ iterators. +#define cimg_for(img,ptrs,T_ptrs) \ + for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs) +#define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs) +#define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off) +#define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off) + +#define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) +#define cimg_forX(img,x) cimg_for1((img)._width,x) +#define cimg_forY(img,y) cimg_for1((img)._height,y) +#define cimg_forZ(img,z) cimg_for1((img)._depth,z) +#define cimg_forC(img,c) cimg_for1((img)._spectrum,c) +#define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) +#define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) +#define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) +#define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x) +#define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y) +#define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z) +#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) +#define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y) +#define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z) +#define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z) +#define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z) + +#define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i) +#define cimg_rofX(img,x) cimg_rof1((img)._width,x) +#define cimg_rofY(img,y) cimg_rof1((img)._height,y) +#define cimg_rofZ(img,z) cimg_rof1((img)._depth,z) +#define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c) +#define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x) +#define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x) +#define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y) +#define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x) +#define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y) +#define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z) +#define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y) +#define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y) +#define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z) +#define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z) +#define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z) + +#define cimg_for_in1(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i) +#define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x) +#define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y) +#define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z) +#define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c) +#define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z) +#define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) +#define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x) +#define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y) +#define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z) +#define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c) +#define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) +#define cimg_for_insideXYZ(img,x,y,z,n) \ + cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) +#define cimg_for_insideXYZC(img,x,y,z,c,n) \ + cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) + +#define cimg_for_out1(boundi,i0,i1,i) \ + for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i) +#define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \ + for (int j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ + ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \ + for (int k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ + ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \ + for (int l = 0; l<(int)(boundl); ++l) \ + for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \ + i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x) +#define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y) +#define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z) +#define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c) +#define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y) +#define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z) +#define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c) +#define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z) +#define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c) +#define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c) +#define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \ + cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \ + cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c) +#define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \ + cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c) +#define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \ + cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c) +#define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) +#define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x) +#define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y) +#define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z) +#define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c) +#define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) +#define cimg_for_borderXYZ(img,x,y,z,n) \ + cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) +#define cimg_for_borderXYZC(img,x,y,z,c,n) \ + cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \ + (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c) + +#define cimg_for_spiralXY(img,x,y) \ + for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \ + --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\ + ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1) + +#define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ + for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ + _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \ + _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \ + _counter = _dx, \ + _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ + _counter>=0; \ + --_counter, x+=_steep? \ + (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ + (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) + +#define cimg_for2(bound,i) \ + for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + ++i, ++_n1##i) +#define cimg_for2X(img,x) cimg_for2((img)._width,x) +#define cimg_for2Y(img,y) cimg_for2((img)._height,y) +#define cimg_for2Z(img,z) cimg_for2((img)._depth,z) +#define cimg_for2C(img,c) cimg_for2((img)._spectrum,c) +#define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) +#define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) +#define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x) +#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) +#define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y) +#define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z) +#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) +#define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z) +#define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z) +#define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z) + +#define cimg_for_in2(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + ++i, ++_n1##i) +#define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x) +#define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y) +#define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z) +#define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c) +#define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z) +#define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i) +#define cimg_for3X(img,x) cimg_for3((img)._width,x) +#define cimg_for3Y(img,y) cimg_for3((img)._height,y) +#define cimg_for3Z(img,z) cimg_for3((img)._depth,z) +#define cimg_for3C(img,c) cimg_for3((img)._spectrum,c) +#define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) +#define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) +#define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x) +#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) +#define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y) +#define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z) +#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) +#define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z) +#define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z) +#define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z) + +#define cimg_for_in3(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + _p1##i = i++, ++_n1##i) +#define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x) +#define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y) +#define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z) +#define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c) +#define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z) +#define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for4(bound,i) \ + for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for4X(img,x) cimg_for4((img)._width,x) +#define cimg_for4Y(img,y) cimg_for4((img)._height,y) +#define cimg_for4Z(img,z) cimg_for4((img)._depth,z) +#define cimg_for4C(img,c) cimg_for4((img)._spectrum,c) +#define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) +#define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) +#define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x) +#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) +#define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y) +#define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z) +#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) +#define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z) +#define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z) +#define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z) + +#define cimg_for_in4(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x) +#define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y) +#define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z) +#define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c) +#define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z) +#define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for5(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for5X(img,x) cimg_for5((img)._width,x) +#define cimg_for5Y(img,y) cimg_for5((img)._height,y) +#define cimg_for5Z(img,z) cimg_for5((img)._depth,z) +#define cimg_for5C(img,c) cimg_for5((img)._spectrum,c) +#define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) +#define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) +#define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x) +#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) +#define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y) +#define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z) +#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) +#define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z) +#define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z) +#define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z) + +#define cimg_for_in5(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x) +#define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y) +#define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z) +#define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c) +#define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z) +#define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for6(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for6X(img,x) cimg_for6((img)._width,x) +#define cimg_for6Y(img,y) cimg_for6((img)._height,y) +#define cimg_for6Z(img,z) cimg_for6((img)._depth,z) +#define cimg_for6C(img,c) cimg_for6((img)._spectrum,c) +#define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) +#define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) +#define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x) +#define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) +#define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y) +#define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z) +#define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) +#define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z) +#define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z) +#define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z) + +#define cimg_for_in6(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ + i<=(int)(i1) && \ + (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x) +#define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y) +#define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z) +#define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c) +#define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z) +#define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for7(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for7X(img,x) cimg_for7((img)._width,x) +#define cimg_for7Y(img,y) cimg_for7((img)._height,y) +#define cimg_for7Z(img,z) cimg_for7((img)._depth,z) +#define cimg_for7C(img,c) cimg_for7((img)._spectrum,c) +#define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) +#define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) +#define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x) +#define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) +#define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y) +#define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z) +#define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) +#define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z) +#define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z) +#define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z) + +#define cimg_for_in7(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ + i<=(int)(i1) && \ + (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x) +#define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y) +#define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z) +#define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c) +#define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z) +#define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for8(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3, \ + _n4##i = 4>=(bound)?(int)(bound) - 1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for8X(img,x) cimg_for8((img)._width,x) +#define cimg_for8Y(img,y) cimg_for8((img)._height,y) +#define cimg_for8Z(img,z) cimg_for8((img)._depth,z) +#define cimg_for8C(img,c) cimg_for8((img)._spectrum,c) +#define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x) +#define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x) +#define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x) +#define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) +#define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y) +#define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z) +#define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) +#define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z) +#define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z) +#define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z) + +#define cimg_for_in8(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ + _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x) +#define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y) +#define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z) +#define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c) +#define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z) +#define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for9(bound,i) \ + for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \ + _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for9X(img,x) cimg_for9((img)._width,x) +#define cimg_for9Y(img,y) cimg_for9((img)._height,y) +#define cimg_for9Z(img,z) cimg_for9((img)._depth,z) +#define cimg_for9C(img,c) cimg_for9((img)._spectrum,c) +#define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x) +#define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x) +#define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x) +#define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) +#define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y) +#define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z) +#define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) +#define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z) +#define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z) +#define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z) + +#define cimg_for_in9(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p4##i = i - 4<0?0:i - 4, \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ + _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x) +#define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y) +#define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z) +#define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c) +#define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z) +#define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[4] = (T)(img)(x,y,z,c)), \ + (I[7] = (T)(img)(x,_n1##y,z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for4x4(img,x,y,z,c,I,T) \ + cimg_for4((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = I[5] = (T)(img)(0,y,z,c)), \ + (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width() - 1:2); \ + (_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = (T)(img)(_p1##x,y,z,c)), \ + (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[5] = (T)(img)(x,y,z,c)), \ + (I[9] = (T)(img)(x,_n1##y,z,c)), \ + (I[13] = (T)(img)(x,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for5x5(img,x,y,z,c,I,T) \ + cimg_for5((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \ + (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \ + (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width() - 1:2); \ + (_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[10] = (T)(img)(_p2##x,y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[11] = (T)(img)(_p1##x,y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[7] = (T)(img)(x,_p1##y,z,c)), \ + (I[12] = (T)(img)(x,y,z,c)), \ + (I[17] = (T)(img)(x,_n1##y,z,c)), \ + (I[22] = (T)(img)(x,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for6x6(img,x,y,z,c,I,T) \ + cimg_for6((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = 2>=(img)._width?(img).width() - 1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \ + (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \ + (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \ + (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width() - 1:3); \ + (_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p2##x,y,z,c)), \ + (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_p1##x,y,z,c)), \ + (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[8] = (T)(img)(x,_p1##y,z,c)), \ + (I[14] = (T)(img)(x,y,z,c)), \ + (I[20] = (T)(img)(x,_n1##y,z,c)), \ + (I[26] = (T)(img)(x,_n2##y,z,c)), \ + (I[32] = (T)(img)(x,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for7x7(img,x,y,z,c,I,T) \ + cimg_for7((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = 2>=(img)._width?(img).width() - 1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \ + (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \ + (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \ + (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \ + (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \ + (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width() - 1:3); \ + (_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[21] = (T)(img)(_p3##x,y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[22] = (T)(img)(_p2##x,y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[23] = (T)(img)(_p1##x,y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[10] = (T)(img)(x,_p2##y,z,c)), \ + (I[17] = (T)(img)(x,_p1##y,z,c)), \ + (I[24] = (T)(img)(x,y,z,c)), \ + (I[31] = (T)(img)(x,_n1##y,z,c)), \ + (I[38] = (T)(img)(x,_n2##y,z,c)), \ + (I[45] = (T)(img)(x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for8x8(img,x,y,z,c,I,T) \ + cimg_for8((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ + _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ + _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \ + (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \ + (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \ + (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \ + (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \ + (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \ + (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width() - 1:4); \ + (_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ + _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[24] = (T)(img)(_p3##x,y,z,c)), \ + (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_p2##x,y,z,c)), \ + (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_p1##x,y,z,c)), \ + (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[11] = (T)(img)(x,_p2##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,z,c)), \ + (I[27] = (T)(img)(x,y,z,c)), \ + (I[35] = (T)(img)(x,_n1##y,z,c)), \ + (I[43] = (T)(img)(x,_n2##y,z,c)), \ + (I[51] = (T)(img)(x,_n3##y,z,c)), \ + (I[59] = (T)(img)(x,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x + 4>=(img).width()?(img).width() - 1:x + 4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for9x9(img,x,y,z,c,I,T) \ + cimg_for9((img)._height,y) for (int x = 0, \ + _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ + _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ + _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \ + (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \ + (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \ + (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \ + (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \ + (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \ + (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \ + (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width() - 1:4); \ + (_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ + I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ + I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ + I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p4##x = x - 4<0?0:x - 4, \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ + _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \ + (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \ + (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \ + (I[36] = (T)(img)(_p4##x,y,z,c)), \ + (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \ + (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \ + (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \ + (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \ + (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[37] = (T)(img)(_p3##x,y,z,c)), \ + (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \ + (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[38] = (T)(img)(_p2##x,y,z,c)), \ + (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[39] = (T)(img)(_p1##x,y,z,c)), \ + (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[4] = (T)(img)(x,_p4##y,z,c)), \ + (I[13] = (T)(img)(x,_p3##y,z,c)), \ + (I[22] = (T)(img)(x,_p2##y,z,c)), \ + (I[31] = (T)(img)(x,_p1##y,z,c)), \ + (I[40] = (T)(img)(x,y,z,c)), \ + (I[49] = (T)(img)(x,_n1##y,z,c)), \ + (I[58] = (T)(img)(x,_n2##y,z,c)), \ + (I[67] = (T)(img)(x,_n3##y,z,c)), \ + (I[76] = (T)(img)(x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x + 4>=(img).width()?(img).width() - 1:x + 4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ + I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ + I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ + I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for2x2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + (I[4] = (T)(img)(0,y,_n1##z,c)), \ + (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ + cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + (I[4] = (T)(img)(x,y,_n1##z,c)), \ + (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for3x3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \ + (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \ + (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \ + (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \ + (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ + cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \ + (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,y,z,c)), \ + (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \ + (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \ + (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \ + (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \ + (I[4] = (T)(img)(x,y,_p1##z,c)), \ + (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \ + (I[10] = (T)(img)(x,_p1##y,z,c)), \ + (I[13] = (T)(img)(x,y,z,c)), \ + (I[16] = (T)(img)(x,_n1##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \ + (I[22] = (T)(img)(x,y,_n1##z,c)), \ + (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l) +#define cimglist_rof(list,l) for (int l = (int)(list)._width - 1; l>=0; --l) +#define cimglist_for_in(list,l0,l1,l) \ + for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \ + l<=_max##l; ++l) + +#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn + +// Macros used to display error messages when exceptions are thrown. +// You should not use these macros is your own code. +#define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::" +#define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']' +#define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::" +#define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type() +#define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::" +#define cimglist_instance _width,_allocated_width,_data,pixel_type() + +/*------------------------------------------------ + # + # + # Define cimg_library:: namespace + # + # + -------------------------------------------------*/ +//! Contains all classes and functions of the \CImg library. +/** + This namespace is defined to avoid functions and class names collisions + that could happen with the inclusion of other C++ header files. + Anyway, it should not happen often and you should reasonably start most of your + \CImg-based programs with + \code + #include "CImg.h" + using namespace cimg_library; + \endcode + to simplify the declaration of \CImg Library objects afterwards. +**/ +namespace cimg_library_suffixed { + + // Declare the four classes of the CImg Library. + template struct CImg; + template struct CImgList; + struct CImgDisplay; + struct CImgException; + + // Declare cimg:: namespace. + // This is an incomplete namespace definition here. It only contains some + // necessary stuff to ensure a correct declaration order of the classes and functions + // defined afterwards. + namespace cimg { + + // Define character sequences for colored terminal output. +#ifdef cimg_use_vt100 + static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; + static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; + static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; + static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; + static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; + static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; + static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; + static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; + static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; + static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; + static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; +#else + static const char t_normal[] = { 0 }; + static const char *const t_black = cimg::t_normal, + *const t_red = cimg::t_normal, + *const t_green = cimg::t_normal, + *const t_yellow = cimg::t_normal, + *const t_blue = cimg::t_normal, + *const t_magenta = cimg::t_normal, + *const t_cyan = cimg::t_normal, + *const t_white = cimg::t_normal, + *const t_bold = cimg::t_normal, + *const t_underscore = cimg::t_normal; +#endif + + inline std::FILE* output(std::FILE *file=0); + inline void info(); + + //! Avoid warning messages due to unused parameters. Do nothing actually. + template + inline void unused(const T&, ...) {} + + // [internal] Lock/unlock a mutex for managing concurrent threads. + // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }. + // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg. + inline int mutex(const unsigned int n, const int lock_mode=1); + + inline unsigned int& exception_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = cimg_verbosity; + if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } + return mode; + } + + // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. + inline FILE* _stdin(const bool throw_exception=true); + inline FILE* _stdout(const bool throw_exception=true); + inline FILE* _stderr(const bool throw_exception=true); + + // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character + // at the end of the string. +#if cimg_OS==2 && defined(_MSC_VER) + inline int _snprintf(char *const s, const size_t size, const char *const format, ...) { + va_list ap; + va_start(ap,format); + const int result = _vsnprintf(s,size,format,ap); + va_end(ap); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) { + int result = -1; + cimg::mutex(6); + if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); + if (result==-1) result = _vscprintf(format,ap); + cimg::mutex(6,0); + return result; + } + + // Mutex-protected version of sscanf, sprintf and snprintf. + // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX. +#elif defined(__MACOSX__) || defined(__APPLE__) + inline int _sscanf(const char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsscanf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _sprintf(char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsprintf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _snprintf(char *const s, const size_t n, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsnprintf(s,n,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) { + cimg::mutex(6); + const int result = std::vsnprintf(s,size,format,ap); + cimg::mutex(6,0); + return result; + } +#endif + + //! Set current \CImg exception mode. + /** + The way error messages are handled by \CImg can be changed dynamically, using this function. + \param mode Desired exception mode. Possible values are: + - \c 0: Hide library messages (quiet mode). + - \c 1: Print library messages on the console. + - \c 2: Display library messages on a dialog window. + - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!). + - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!). + **/ + inline unsigned int& exception_mode(const unsigned int mode) { + return exception_mode(mode,true); + } + + //! Return current \CImg exception mode. + /** + \note By default, return the value of configuration macro \c cimg_verbosity + **/ + inline unsigned int& exception_mode() { + return exception_mode(0,false); + } + + inline unsigned int openmp_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = 2; + if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } + return mode; + } + + //! Set current \CImg openmp mode. + /** + The way openmp-based methods are handled by \CImg can be changed dynamically, using this function. + \param mode Desired openmp mode. Possible values are: + - \c 0: Never parallelize. + - \c 1: Always parallelize. + - \c 2: Adaptive parallelization mode (default behavior). + **/ + inline unsigned int openmp_mode(const unsigned int mode) { + return openmp_mode(mode,true); + } + + //! Return current \CImg openmp mode. + inline unsigned int openmp_mode() { + return openmp_mode(0,false); + } + +#ifndef cimg_openmp_sizefactor +#define cimg_openmp_sizefactor 1 +#endif +#define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond)))) +#define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size)) +#ifdef _MSC_VER +// Disable 'collapse()' directive for MSVC (supports only OpenMP 2.0). +#define cimg_openmp_collapse(k) +#else +#define cimg_openmp_collapse(k) collapse(k) +#endif + +#if cimg_OS==2 +// Disable parallelization of simple loops on Windows, due to noticed performance drop. +#define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr); +#else +#define cimg_openmp_for(instance,expr,min_size) \ + cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \ + cimg_rof((instance),ptr,T) *ptr = (T)(expr); +#endif + + // Display a simple dialog box, and wait for the user's response. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label="OK", const char *const button2_label=0, + const char *const button3_label=0, const char *const button4_label=0, + const char *const button5_label=0, const char *const button6_label=0, + const bool centering=false); + + // Evaluate math expression. + inline double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0); + + } // namespace cimg { ... + + /*--------------------------------------- + # + # Define the CImgException structures + # + --------------------------------------*/ + //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call. + /** + \par Overview + + CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException). + CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. + These classes can be: + + - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. + This is the only \c non-derived exception class. + + - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. + This is probably one of the most thrown exception by \CImg. + For instance, the following example throws a \c CImgArgumentException: + \code + CImg img(100,100,1,3); // Define a 100x100 color image with float-valued pixels + img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis + \endcode + + - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances. + + - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit + the function requirements. For instance, the following example throws a \c CImgInstanceException: + \code + const CImg img; // Define an empty image + const float value = img.at(0); // Try to read first pixel value (does not exist) + \endcode + + - \b CImgIOException: Thrown when an error occurred when trying to load or save image files. + This happens when trying to read files that do not exist or with invalid formats. + For instance, the following example throws a \c CImgIOException: + \code + const CImg img("missing_file.jpg"); // Try to load a file that does not exist + \endcode + + - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and + when a \CImg function has to display a warning message (see cimg::warn()). + + It is not recommended to throw CImgException instances by yourself, + since they are expected to be thrown only by \CImg. + When an error occurs in a library function call, \CImg may display error messages on the screen or on the + standard output, depending on the current \CImg exception mode. + The \CImg exception mode can be get and set by functions cimg::exception_mode() and + cimg::exception_mode(unsigned int). + + \par Exceptions handling + + In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown. + This may lead the program to break (this is the default behavior), but you can bypass this behavior by + handling the exceptions by yourself, + using a usual try { ... } catch () { ... } bloc, as in the following example: + \code + #define "CImg.h" + using namespace cimg_library; + int main() { + cimg::exception_mode(0); // Enable quiet exception mode + try { + ... // Here, do what you want to stress CImg + } catch (CImgException& e) { // You succeeded: something went wrong! + std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message + ... // Do what you want now to save the ship! + } + } + \endcode + **/ + struct CImgException : public std::exception { +#define _cimg_exception_err(etype,disp_flag) \ + std::va_list ap, ap2; \ + va_start(ap,format); va_start(ap2,format); \ + int size = cimg_vsnprintf(0,0,format,ap2); \ + if (size++>=0) { \ + delete[] _message; \ + _message = new char[(size_t)size]; \ + cimg_vsnprintf(_message,(size_t)size,format,ap); \ + if (cimg::exception_mode()) { \ + std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ + if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \ + catch (CImgException&) {} \ + if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ + } \ + } \ + va_end(ap); va_end(ap2); + + char *_message; + CImgException() { _message = new char[1]; *_message = 0; } + CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } + CImgException(const CImgException& e):std::exception(e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgException() throw() { delete[] _message; } + CImgException& operator=(const CImgException& e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; // struct CImgException { ... + + // The CImgAbortException class is used to throw an exception when + // a computationally-intensive function has been aborted by an external signal. + struct CImgAbortException : public std::exception { + char *_message; + CImgAbortException() { _message = new char[1]; *_message = 0; } + CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); } + CImgAbortException(const CImgAbortException& e):std::exception(e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgAbortException() throw() { delete[] _message; } + CImgAbortException& operator=(const CImgAbortException& e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; // struct CImgAbortException { ... + + // The CImgArgumentException class is used to throw an exception related + // to invalid arguments encountered in a library function call. + struct CImgArgumentException : public CImgException { + CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); } + }; // struct CImgArgumentException { ... + + // The CImgDisplayException class is used to throw an exception related + // to display problems encountered in a library function call. + struct CImgDisplayException : public CImgException { + CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); } + }; // struct CImgDisplayException { ... + + // The CImgInstanceException class is used to throw an exception related + // to an invalid instance encountered in a library function call. + struct CImgInstanceException : public CImgException { + CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + }; // struct CImgInstanceException { ... + + // The CImgIOException class is used to throw an exception related + // to input/output file problems encountered in a library function call. + struct CImgIOException : public CImgException { + CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); } + }; // struct CImgIOException { ... + + // The CImgWarningException class is used to throw an exception for warnings + // encountered in a library function call. + struct CImgWarningException : public CImgException { + CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); } + }; // struct CImgWarningException { ... + + /*------------------------------------- + # + # Define cimg:: namespace + # + -----------------------------------*/ + //! Contains \a low-level functions and variables of the \CImg Library. + /** + Most of the functions and variables within this namespace are used by the \CImg library for low-level operations. + You may use them to access specific const values or environment variables internally used by \CImg. + \warning Never write using namespace cimg_library::cimg; in your source code. Lot of functions in the + cimg:: namespace have the same names as standard C functions that may be defined in the global + namespace ::. + **/ + namespace cimg { + + // Define traits that will be used to determine the best data type to work in CImg functions. + // + template struct type { + static const char* string() { + static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", + "unknown32", "unknown40", "unknown48", "unknown56", + "unknown64", "unknown72", "unknown80", "unknown88", + "unknown96", "unknown104", "unknown112", "unknown120", + "unknown128" }; + return s[(sizeof(T)<17)?sizeof(T):0]; + } + static bool is_float() { return false; } + static bool is_inf(const T) { return false; } + static bool is_nan(const T) { return false; } + static bool is_finite(const T) { return true; } + static T min() { return ~max(); } + static T max() { return (T)1<<(8*sizeof(T) - 1); } + static T inf() { return max(); } + static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } + static const char* format() { return "%s"; } + static const char* format_s() { return "%s"; } + static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "bool"; return s; } + static bool is_float() { return false; } + static bool is_inf(const bool) { return false; } + static bool is_nan(const bool) { return false; } + static bool is_finite(const bool) { return true; } + static bool min() { return false; } + static bool max() { return true; } + static bool inf() { return max(); } + static bool is_inf() { return false; } + static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } + static const char* format() { return "%s"; } + static const char* format_s() { return "%s"; } + static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned char) { return false; } + static bool is_nan(const unsigned char) { return false; } + static bool is_finite(const unsigned char) { return true; } + static unsigned char min() { return 0; } + static unsigned char max() { return (unsigned char)-1; } + static unsigned char inf() { return max(); } + static unsigned char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned char val) { return (unsigned int)val; } + }; + +#if defined(CHAR_MAX) && CHAR_MAX==255 + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static bool is_finite(const char) { return true; } + static char min() { return 0; } + static char max() { return (char)-1; } + static char inf() { return max(); } + static char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const char val) { return (unsigned int)val; } + }; +#else + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static bool is_finite(const char) { return true; } + static char min() { return ~max(); } + static char max() { return (char)((unsigned char)-1>>1); } + static char inf() { return max(); } + static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const char val) { return (int)val; } + }; +#endif + + template<> struct type { + static const char* string() { static const char *const s = "signed char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const signed char) { return false; } + static bool is_nan(const signed char) { return false; } + static bool is_finite(const signed char) { return true; } + static signed char min() { return ~max(); } + static signed char max() { return (signed char)((unsigned char)-1>>1); } + static signed char inf() { return max(); } + static signed char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(signed char)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const signed char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned short) { return false; } + static bool is_nan(const unsigned short) { return false; } + static bool is_finite(const unsigned short) { return true; } + static unsigned short min() { return 0; } + static unsigned short max() { return (unsigned short)-1; } + static unsigned short inf() { return max(); } + static unsigned short cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned short val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const short) { return false; } + static bool is_nan(const short) { return false; } + static bool is_finite(const short) { return true; } + static short min() { return ~max(); } + static short max() { return (short)((unsigned short)-1>>1); } + static short inf() { return max(); } + static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const short val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned int) { return false; } + static bool is_nan(const unsigned int) { return false; } + static bool is_finite(const unsigned int) { return true; } + static unsigned int min() { return 0; } + static unsigned int max() { return (unsigned int)-1; } + static unsigned int inf() { return max(); } + static unsigned int cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const int) { return false; } + static bool is_nan(const int) { return false; } + static bool is_finite(const int) { return true; } + static int min() { return ~max(); } + static int max() { return (int)((unsigned int)-1>>1); } + static int inf() { return max(); } + static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_uint64) { return false; } + static bool is_nan(const cimg_uint64) { return false; } + static bool is_finite(const cimg_uint64) { return true; } + static cimg_uint64 min() { return 0; } + static cimg_uint64 max() { return (cimg_uint64)-1; } + static cimg_uint64 inf() { return max(); } + static cimg_uint64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } + static const char* format() { return cimg_fuint64; } + static const char* format_s() { return cimg_fuint64; } + static cimg_uint64 format(const cimg_uint64 val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_int64) { return false; } + static bool is_nan(const cimg_int64) { return false; } + static bool is_finite(const cimg_int64) { return true; } + static cimg_int64 min() { return ~max(); } + static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } + static cimg_int64 inf() { return max(); } + static cimg_int64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; + } + static const char* format() { return cimg_fint64; } + static const char* format_s() { return cimg_fint64; } + static long format(const long val) { return (long)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const double val) { // Custom version that works with '-ffast-math' + if (sizeof(double)==8) { + cimg_uint64 u; + std::memcpy(&u,&val,sizeof(double)); + return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000; + } +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const double val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static double min() { return -DBL_MAX; } + static double max() { return DBL_MAX; } + static double inf() { +#ifdef INFINITY + return (double)INFINITY; +#else + return max()*max(); +#endif + } + static double nan() { +#ifdef NAN + return (double)NAN; +#else + const double val_nan = -std::sqrt(-1.); return val_nan; +#endif + } + static double cut(const double val) { return val; } + static const char* format() { return "%.17g"; } + static const char* format_s() { return "%g"; } + static double format(const double val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "float"; return s; } + static bool is_float() { return true; } + static bool is_inf(const float val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const float val) { // Custom version that works with '-ffast-math' + if (sizeof(float)==4) { + unsigned int u; + std::memcpy(&u,&val,sizeof(float)); + return (u&0x7fffffff)>0x7f800000; + } +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const float val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static float min() { return -FLT_MAX; } + static float max() { return FLT_MAX; } + static float inf() { return (float)cimg::type::inf(); } + static float nan() { return (float)cimg::type::nan(); } + static float cut(const double val) { return (float)val; } + static float cut(const float val) { return (float)val; } + static const char* format() { return "%.9g"; } + static const char* format_s() { return "%g"; } + static double format(const float val) { return (double)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "long double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const long double val) { +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const long double val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static long double min() { return -LDBL_MAX; } + static long double max() { return LDBL_MAX; } + static long double inf() { return max()*max(); } + static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; } + static long double cut(const long double val) { return val; } + static const char* format() { return "%.17g"; } + static const char* format_s() { return "%g"; } + static double format(const long double val) { return (double)val; } + }; + +#ifdef cimg_use_half + template<> struct type { + static const char* string() { static const char *const s = "half"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const half val) { // Custom version that works with '-ffast-math' + if (sizeof(half)==2) { + short u; + std::memcpy(&u,&val,sizeof(short)); + return (bool)((u&0x7fff)>0x7c00); + } + return cimg::type::is_nan((float)val); + } + static bool is_finite(const half val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static half min() { return (half)-65504; } + static half max() { return (half)65504; } + static half inf() { return max()*max(); } + static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; } + static half cut(const double val) { return (half)val; } + static const char* format() { return "%.9g"; } + static const char* format_s() { return "%g"; } + static double format(const half val) { return (double)val; } + }; +#endif + + template struct superset { typedef T type; }; + template<> struct superset { typedef unsigned char type; }; + template<> struct superset { typedef char type; }; + template<> struct superset { typedef signed char type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + +#ifdef cimg_use_half + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; +#endif + + template struct superset2 { + typedef typename superset::type>::type type; + }; + + template struct superset3 { + typedef typename superset::type>::type type; + }; + + template struct last { typedef t2 type; }; + +#define _cimg_Tt typename cimg::superset::type +#define _cimg_Tfloat typename cimg::superset::type +#define _cimg_tfloat typename cimg::superset::type +#define _cimg_Ttfloat typename cimg::superset2::type +#define _cimg_Ttdouble typename cimg::superset2::type + + // Define variables used internally by CImg. +#if cimg_display==1 + struct X11_static { + unsigned int nb_wins; + pthread_t *events_thread; + pthread_cond_t wait_event; + pthread_mutex_t wait_event_mutex; + CImgDisplay **wins; + Display *display; + unsigned int nb_bits; + bool is_blue_first; + bool is_shm_enabled; + bool byte_order; + +#ifdef cimg_use_xrandr + XRRScreenSize *resolutions; + Rotation curr_rotation; + unsigned int curr_resolution; + unsigned int nb_resolutions; +#endif + X11_static():nb_wins(0),events_thread(0),display(0), + nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) { +#ifdef __FreeBSD__ + XInitThreads(); +#endif + wins = new CImgDisplay*[1024]; + pthread_mutex_init(&wait_event_mutex,0); + pthread_cond_init(&wait_event,0); + +#ifdef cimg_use_xrandr + resolutions = 0; + curr_rotation = 0; + curr_resolution = nb_resolutions = 0; +#endif + } + + ~X11_static() { + delete[] wins; + /* + if (events_thread) { + pthread_cancel(*events_thread); + delete events_thread; + } + if (display) { } // XCloseDisplay(display); } + pthread_cond_destroy(&wait_event); + pthread_mutex_unlock(&wait_event_mutex); + pthread_mutex_destroy(&wait_event_mutex); + */ + } + }; // struct X11_static { ... +#if defined(cimg_module) + X11_static& X11_attr(); +#elif defined(cimg_main) + X11_static& X11_attr() { static X11_static val; return val; } +#else + inline X11_static& X11_attr() { static X11_static val; return val; } +#endif + +#elif cimg_display==2 + struct Win32_static { + HANDLE wait_event; + Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); } + }; // struct Win32_static { ... +#if defined(cimg_module) + Win32_static& Win32_attr(); +#elif defined(cimg_main) + Win32_static& Win32_attr() { static Win32_static val; return val; } +#else + inline Win32_static& Win32_attr() { static Win32_static val; return val; } +#endif +#endif +#define cimg_lock_display() cimg::mutex(15) +#define cimg_unlock_display() cimg::mutex(15,0) + + struct Mutex_static { +#if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1) + pthread_mutex_t mutex[32]; + Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); } + void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); } + void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); } + int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); } +#elif cimg_OS==2 + HANDLE mutex[32]; + Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); } + void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); } + void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); } + int trylock(const unsigned int) { return 0; } +#else + Mutex_static() {} + void lock(const unsigned int) {} + void unlock(const unsigned int) {} + int trylock(const unsigned int) { return 0; } +#endif + }; // struct Mutex_static { ... +#if defined(cimg_module) + Mutex_static& Mutex_attr(); +#elif defined(cimg_main) + Mutex_static& Mutex_attr() { static Mutex_static val; return val; } +#else + inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; } +#endif + +#if defined(cimg_use_magick) + struct Magick_static { + Magick_static() { + Magick::InitializeMagick(""); + } + }; // struct Magick_static { ... + static Magick_static _Magick_static; +#endif + +#if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread) + struct FFTW3_static { + FFTW3_static() { + fftw_init_threads(); + } + }; // struct FFTW3_static { ... + static FFTW3_static _FFTW3_static; +#endif + +#if cimg_display==1 + // Define keycodes for X11-based graphical systems. + const unsigned int keyESC = XK_Escape; + const unsigned int keyF1 = XK_F1; + const unsigned int keyF2 = XK_F2; + const unsigned int keyF3 = XK_F3; + const unsigned int keyF4 = XK_F4; + const unsigned int keyF5 = XK_F5; + const unsigned int keyF6 = XK_F6; + const unsigned int keyF7 = XK_F7; + const unsigned int keyF8 = XK_F8; + const unsigned int keyF9 = XK_F9; + const unsigned int keyF10 = XK_F10; + const unsigned int keyF11 = XK_F11; + const unsigned int keyF12 = XK_F12; + const unsigned int keyPAUSE = XK_Pause; + const unsigned int key1 = XK_1; + const unsigned int key2 = XK_2; + const unsigned int key3 = XK_3; + const unsigned int key4 = XK_4; + const unsigned int key5 = XK_5; + const unsigned int key6 = XK_6; + const unsigned int key7 = XK_7; + const unsigned int key8 = XK_8; + const unsigned int key9 = XK_9; + const unsigned int key0 = XK_0; + const unsigned int keyBACKSPACE = XK_BackSpace; + const unsigned int keyINSERT = XK_Insert; + const unsigned int keyHOME = XK_Home; + const unsigned int keyPAGEUP = XK_Page_Up; + const unsigned int keyTAB = XK_Tab; + const unsigned int keyQ = XK_q; + const unsigned int keyW = XK_w; + const unsigned int keyE = XK_e; + const unsigned int keyR = XK_r; + const unsigned int keyT = XK_t; + const unsigned int keyY = XK_y; + const unsigned int keyU = XK_u; + const unsigned int keyI = XK_i; + const unsigned int keyO = XK_o; + const unsigned int keyP = XK_p; + const unsigned int keyDELETE = XK_Delete; + const unsigned int keyEND = XK_End; + const unsigned int keyPAGEDOWN = XK_Page_Down; + const unsigned int keyCAPSLOCK = XK_Caps_Lock; + const unsigned int keyA = XK_a; + const unsigned int keyS = XK_s; + const unsigned int keyD = XK_d; + const unsigned int keyF = XK_f; + const unsigned int keyG = XK_g; + const unsigned int keyH = XK_h; + const unsigned int keyJ = XK_j; + const unsigned int keyK = XK_k; + const unsigned int keyL = XK_l; + const unsigned int keyENTER = XK_Return; + const unsigned int keySHIFTLEFT = XK_Shift_L; + const unsigned int keyZ = XK_z; + const unsigned int keyX = XK_x; + const unsigned int keyC = XK_c; + const unsigned int keyV = XK_v; + const unsigned int keyB = XK_b; + const unsigned int keyN = XK_n; + const unsigned int keyM = XK_m; + const unsigned int keySHIFTRIGHT = XK_Shift_R; + const unsigned int keyARROWUP = XK_Up; + const unsigned int keyCTRLLEFT = XK_Control_L; + const unsigned int keyAPPLEFT = XK_Super_L; + const unsigned int keyALT = XK_Alt_L; + const unsigned int keySPACE = XK_space; + const unsigned int keyALTGR = XK_Alt_R; + const unsigned int keyAPPRIGHT = XK_Super_R; + const unsigned int keyMENU = XK_Menu; + const unsigned int keyCTRLRIGHT = XK_Control_R; + const unsigned int keyARROWLEFT = XK_Left; + const unsigned int keyARROWDOWN = XK_Down; + const unsigned int keyARROWRIGHT = XK_Right; + const unsigned int keyPAD0 = XK_KP_0; + const unsigned int keyPAD1 = XK_KP_1; + const unsigned int keyPAD2 = XK_KP_2; + const unsigned int keyPAD3 = XK_KP_3; + const unsigned int keyPAD4 = XK_KP_4; + const unsigned int keyPAD5 = XK_KP_5; + const unsigned int keyPAD6 = XK_KP_6; + const unsigned int keyPAD7 = XK_KP_7; + const unsigned int keyPAD8 = XK_KP_8; + const unsigned int keyPAD9 = XK_KP_9; + const unsigned int keyPADADD = XK_KP_Add; + const unsigned int keyPADSUB = XK_KP_Subtract; + const unsigned int keyPADMUL = XK_KP_Multiply; + const unsigned int keyPADDIV = XK_KP_Divide; + +#elif cimg_display==2 + // Define keycodes for Windows. + const unsigned int keyESC = VK_ESCAPE; + const unsigned int keyF1 = VK_F1; + const unsigned int keyF2 = VK_F2; + const unsigned int keyF3 = VK_F3; + const unsigned int keyF4 = VK_F4; + const unsigned int keyF5 = VK_F5; + const unsigned int keyF6 = VK_F6; + const unsigned int keyF7 = VK_F7; + const unsigned int keyF8 = VK_F8; + const unsigned int keyF9 = VK_F9; + const unsigned int keyF10 = VK_F10; + const unsigned int keyF11 = VK_F11; + const unsigned int keyF12 = VK_F12; + const unsigned int keyPAUSE = VK_PAUSE; + const unsigned int key1 = '1'; + const unsigned int key2 = '2'; + const unsigned int key3 = '3'; + const unsigned int key4 = '4'; + const unsigned int key5 = '5'; + const unsigned int key6 = '6'; + const unsigned int key7 = '7'; + const unsigned int key8 = '8'; + const unsigned int key9 = '9'; + const unsigned int key0 = '0'; + const unsigned int keyBACKSPACE = VK_BACK; + const unsigned int keyINSERT = VK_INSERT; + const unsigned int keyHOME = VK_HOME; + const unsigned int keyPAGEUP = VK_PRIOR; + const unsigned int keyTAB = VK_TAB; + const unsigned int keyQ = 'Q'; + const unsigned int keyW = 'W'; + const unsigned int keyE = 'E'; + const unsigned int keyR = 'R'; + const unsigned int keyT = 'T'; + const unsigned int keyY = 'Y'; + const unsigned int keyU = 'U'; + const unsigned int keyI = 'I'; + const unsigned int keyO = 'O'; + const unsigned int keyP = 'P'; + const unsigned int keyDELETE = VK_DELETE; + const unsigned int keyEND = VK_END; + const unsigned int keyPAGEDOWN = VK_NEXT; + const unsigned int keyCAPSLOCK = VK_CAPITAL; + const unsigned int keyA = 'A'; + const unsigned int keyS = 'S'; + const unsigned int keyD = 'D'; + const unsigned int keyF = 'F'; + const unsigned int keyG = 'G'; + const unsigned int keyH = 'H'; + const unsigned int keyJ = 'J'; + const unsigned int keyK = 'K'; + const unsigned int keyL = 'L'; + const unsigned int keyENTER = VK_RETURN; + const unsigned int keySHIFTLEFT = VK_SHIFT; + const unsigned int keyZ = 'Z'; + const unsigned int keyX = 'X'; + const unsigned int keyC = 'C'; + const unsigned int keyV = 'V'; + const unsigned int keyB = 'B'; + const unsigned int keyN = 'N'; + const unsigned int keyM = 'M'; + const unsigned int keySHIFTRIGHT = VK_SHIFT; + const unsigned int keyARROWUP = VK_UP; + const unsigned int keyCTRLLEFT = VK_CONTROL; + const unsigned int keyAPPLEFT = VK_LWIN; + const unsigned int keyALT = VK_LMENU; + const unsigned int keySPACE = VK_SPACE; + const unsigned int keyALTGR = VK_CONTROL; + const unsigned int keyAPPRIGHT = VK_RWIN; + const unsigned int keyMENU = VK_APPS; + const unsigned int keyCTRLRIGHT = VK_CONTROL; + const unsigned int keyARROWLEFT = VK_LEFT; + const unsigned int keyARROWDOWN = VK_DOWN; + const unsigned int keyARROWRIGHT = VK_RIGHT; + const unsigned int keyPAD0 = 0x60; + const unsigned int keyPAD1 = 0x61; + const unsigned int keyPAD2 = 0x62; + const unsigned int keyPAD3 = 0x63; + const unsigned int keyPAD4 = 0x64; + const unsigned int keyPAD5 = 0x65; + const unsigned int keyPAD6 = 0x66; + const unsigned int keyPAD7 = 0x67; + const unsigned int keyPAD8 = 0x68; + const unsigned int keyPAD9 = 0x69; + const unsigned int keyPADADD = VK_ADD; + const unsigned int keyPADSUB = VK_SUBTRACT; + const unsigned int keyPADMUL = VK_MULTIPLY; + const unsigned int keyPADDIV = VK_DIVIDE; + +#else + // Define random keycodes when no display is available. + // (should rarely be used then!). + const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent) + const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent) + const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent) + const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent) + const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent) + const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent) + const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent) + const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent) + const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent) + const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent) + const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent) + const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent) + const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent) + const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent) + const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent) + const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent) + const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent) + const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent) + const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent) + const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent) + const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent) + const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent) + const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent) + const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent) + const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent) + const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent) + const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent) + const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent) + const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent) + const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent) + const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent) + const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent) + const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent) + const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent) + const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent) + const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent) + const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent) + const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent) + const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent) + const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent) + const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent) + const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent) + const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent) + const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent) + const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent) + const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent) + const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent) + const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent) + const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent) + const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent) + const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent) + const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent) + const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent) + const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent) + const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent) + const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent) + const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent) + const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent) + const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent) + const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent) + const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent) + const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent) + const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent) + const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent) + const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent) + const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent) + const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent) + const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent) + const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent) + const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent) + const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent) + const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent) + const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent) + const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent) + const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent) + const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent) + const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent) + const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent) + const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent) + const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent) + const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent) + const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent) + const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent) + const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent) + const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent) + const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent) + const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent) + const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent) +#endif + + const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI + + // Define a 10x13 binary font (small sans). + static const char *const data_font_small[] = { + " UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw Mw (wnwnwuwpwuypwuwoy" + "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y Hw cwnw >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w " + "ry qw %wuw !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow" + "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq" + "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd" + "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp" + "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw " + "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwswy~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~" + "}y|xy|}w~| s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x" + "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w" + "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f" + "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|" + "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{" + "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~} Pw~}y{|x~}cY~ i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~| ;{}`~} -" + "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~ +{|w~Xs~y}s~|\\m~}X{}" + "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~ hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{" + "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|" + "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~ " + " Ov~ !{}w~}Mw~|N{|v~ :{}v~|s{|v~V{|t}|V{|t~s}w~| p{|v~ {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~" + "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~} t{}" + "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{" + "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_" + "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{" + "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}" + "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{" + "|t}|Lw~|xw~c{|[~} iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~ ={|^~} .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x" + "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{} 4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{" + "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~} iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y" + "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}" + "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc" + "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~ Ov~ !{}w~|Mw~|M{}w~ :v~|q{}w~|Xp~}X{}v~|p{|" + "}| o{}w~| v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p" + "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~| w{|w~}|o{|}w~|(x~}tw~ rw~K{}x" + "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|" + "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~" + "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`" + "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|" + "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~} j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~} b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|" + "P{|w~|xx|av~|fv~| j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~} ?{}t~}y| u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{" + "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}| `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~" + "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~| jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n" + "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|" + "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a" + "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{|w~" + "}p{|w~}Xn~|Zv~ _{|v~ !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|" + "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~| x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}" + "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w" + "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|" + "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v" + "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v" + "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy| g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~} d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}" + "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~| A{}u~} q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^" + "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~ a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o" + "{}v~|a{|v~}p{}v~ j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|" + "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|" + "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~| vw~} `{|w~|$" + "w~} w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{}w~|ow~}Xm~|[v~ ^v~| \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}" + "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~} y{|" + "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~" + "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|" + "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" + "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|" + "x}rx}| 6w|Nw|Ox|rw| Nw~} e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|" + "Oy|Uw|jw|Vu|Wv|kw|b{}v~} p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}" + "w~}xx~x{}w~}|U{}w~ a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~| \\{}w~}" + "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}" + "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}" + "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~| ww~} `{|w~|$w~} w~} >w~}Dv~ " + "Ov~ !v~Lw~|M{|w~| <{}w~|ow~}Xy~}w|}t~[v~| _{}w~} #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~" + "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\" + "{|}p~} {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}" + "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~" + "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}" + "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~ Py~}|u{|v~} 2w~} f{" + "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}" + "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~| ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{" + "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~ aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}" + "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}" + "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v" + "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~| xw~} `{|w~|$w" + "~} w~} >w~}Dv~ Ov~ !w~}Lw~|M{|w~| {|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{" + "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v" + "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|" + "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|" + "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|" + "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~| n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{" + "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|" + "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|" + "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~" + "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv" + "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z" + "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{" + "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|" + "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|" + "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}" + "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}" + "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~ lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~" + "}H{}w~|Q{|t~|N{}w~ c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v" + "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w" + "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~" + "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m" + "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}" + "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w" + "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c" + "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}" + "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|" + "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]" + "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}" + "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}" + "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}[" + "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~" + "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a" + "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}" + "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~} pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v" + "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~ c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v" + "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m" + "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~" + "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~" + "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}" + "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}" + "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u" + "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t" + "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r" + "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w" + "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~" + "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv" + "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d" + "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v" + "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}" + "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~" + "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~" + "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o" + "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~ pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v" + "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~ c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|" + "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{" + "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n" + "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{" + "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|" + "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|" + "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w" + "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w" + "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v" + "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~" + "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]" + "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}" + "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{" + "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r" + "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|" + "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v" + "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{" + "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}" + "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~} q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw" + "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~ cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|" + "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{" + "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{" + "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w" + "~|H{|w~| s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}" + "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|" + "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s" + "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{" + "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}" + "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}" + "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t" + "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w" + "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|" + "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m" + "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~" + "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~" + "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~" + "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~" + "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~| q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x" + "x~x{|}w~}U{}w~ d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{" + "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{" + "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~" + "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~| bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U" + "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w" + "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_" + "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}" + "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|" + "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v" + "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} " + "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~" + "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v" + "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~" + "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}" + "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|" + "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}" + "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~" + "r{}w~}dw~| lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~ d{}x~}Y{|v~k{}w~}W{}w" + "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}" + "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|" + "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}" + "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~| b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T" + "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~" + "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o" + "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a" + "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{" + "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~" + "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v" + "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}" + "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|" + "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|" + "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}" + "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`" + "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u" + "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~" + "|yv~|X{}w~|sv~|dV~} 2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ " + " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}" + "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}" + "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u" + "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~| aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~" + "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{" + "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`" + "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~" + "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|" + "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~" + "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}" + "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w" + "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v" + "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}[" + "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V" + "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{" + "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}" + "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~} 2v~| k{}w~| {{|x~" + "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~ e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~" + "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w" + "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b" + "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~| a{}w~^v~|m{|" + "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c" + "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}" + "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m" + "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}" + "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|" + "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|" + "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\" + "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w" + "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}" + "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~" + "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}" + "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}" + "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~} 2v~| k{}w~| {{}" + "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~} t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{" + "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}" + "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv" + "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I" + "{}w~I{|w~| a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{" + "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K" + "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~" + "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|" + "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X" + "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}" + "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o", + "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}" + "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|" + "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~" + "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}" + "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|" + "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{" + "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~} 2v~| k{}w~| {{w~|tw~|W{|}m~}T{" + "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{" + "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|" + "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~" + "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~| " + "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v" + "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|" + "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~" + "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~" + "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~" + "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~ p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~| jv~}" + "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|" + "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}" + "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w" + "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}" + "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}" + "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd" + "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw" + "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z| 5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox" + "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}" + "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{" + "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w" + "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~| p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~" + "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~" + "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~" + "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{" + "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c" + "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~" + "}8{|y~|sw~vw~}q{|y~| Q{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{" + "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}" + "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}" + "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~" + "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v" + "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j" + "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{" + "|w~}b{}x~} pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k" + "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}" + "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u" + "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~" + "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~| r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~" + "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|" + "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}" + "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv" + "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]" + "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{" + "|w~|r{}y~ P{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|" + "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}" + "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_" + "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|" + "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}" + "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~" + "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{" + "}w~|b{}x~} q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w" + "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv" + "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|" + "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|" + "v~|Ru~|M{|w~}H{|w~J{|w~| s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}" + "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|" + "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{" + "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v" + "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}" + "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~ p{|" + "w~|m{}w~|Ux~}w{|x~} w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|" + "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{" + "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{" + "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|" + "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m" + "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|" + "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~| " + " r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}" + "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k" + "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~" + "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|" + "v~|S{}v~|L{|w~}Gw~|K{|w~| t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~" + "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~" + "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~" + "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y" + "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\" + "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~" + "} P{}w~ p{|w~|m{}w~|Ux~}w{|x~} w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b" + "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v" + "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" + "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u" + "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{" + "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}" + "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x" + "{|v~`w~} m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~ g{}w~Uv~|lv~|W{}w~}P{}v~" + "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{" + "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv" + "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{" + "|w~}G{}x~}K{|w~| tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~" + "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L" + "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~" + "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v" + "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|" + "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~ " + "p{|w~|m{}w~|Ux~}w{|x~} w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{" + "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k" + "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V" + "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~" + "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}" + "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~" + "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|" + " mv~| o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~ gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~" + "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}" + "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}" + "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w" + "~| u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~" + "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|" + "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{" + "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}" + "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W" + "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~ p{|w~|m{}w" + "~|Ux~}w{|x~} B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~" + "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}" + "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v" + "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{" + "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v" + "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|" + "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~} mv~} g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw" + "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~ h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|" + " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}" + "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v" + "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~| u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv" + "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~" + "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{" + "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~" + "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}" + "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|" + "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~ p{|w~|m{}w~|Ux~}w{|x~} C{}w" + "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~" + "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~" + "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv" + "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m" + "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w" + "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}" + "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X" + "{|v~{|v~^{}w~} n{}v~ gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~ h{|w~T{|v~m{}w~}V{}w~}" + "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}" + "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g" + "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w" + "~}F{}x~}L{|w~| u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv" + "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w" + "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~" + "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~" + "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{" + "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|" + "w~|y{|y~} N{}w~ p{|w~}m{}w~|Ux~}w{|x~} Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|" + "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{" + "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v" + "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{" + "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w" + "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y" + "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w" + "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~| o{|v~| hw" + "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~ h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~" + "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~" + "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y" + "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~| u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}" + "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv" + "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a" + "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u" + "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}" + "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x" + "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~| >{|w~}mv~|Ux~}w{|x~} Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w" + "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w" + "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}" + "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m" + "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv" + "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}" + "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y" + "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~| o{}v~j{} {|x~}t{|" + "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~ hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|" + "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~" + "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}" + "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~| u{}w~|nu~|_" + "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv" + "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~" + "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|" + "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|" + "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x" + "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~| ={|v~n{|v~|Ux~}w{|x~} Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}" + "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w" + "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{" + "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n" + "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v" + "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}" + "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|" + "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~} p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~" + "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{" + "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a" + "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}" + "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~| u{}w~|o{}u~|_t~|q{|v~}_" + "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u" + "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~" + "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_" + "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y" + "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x" + "~|g{|x~} <{|v~|o{}v~|Ux~}w{|x~} Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f" + "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~" + "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~" + "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{" + "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{" + "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~" + "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p" + "{|v~X{|r~}Z{}u~} q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{" + "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|" + "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~" + "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u" + "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~| u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{" + "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~" + "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~| r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w" + "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}" + "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U" + "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~} ;{|u~p{|u~|Ux~}w{|x~} Ey~|s{|~}T" + "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u" + "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u" + "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v" + "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{" + "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{" + "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} " + "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y| tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|" + "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}" + "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}" + "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~" + "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|" + "v~|[{|u~}b|^{|w~}E{|w~|N{|w~| tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}" + "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|" + "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~| qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~" + "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}" + "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#" + "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{" + "|t~}Ux~}w{|x~} E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f" + "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}" + "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~" + "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}" + "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}" + "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v" + "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M" + "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~} -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o" + "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z" + "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y" + "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~" + "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~| t{}u~y" + "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv" + "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{" + "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~| q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}" + "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{" + "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~" + "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~| E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~" + "} l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|" + "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}" + "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}" + "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}" + "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{" + "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{" + "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~" + "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~} -{|h~|$v~}#{}x~}t{}x" + "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~" + "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv" + "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}" + "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~| sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw" + "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d" + "~Uw~}Lw~|M{|w~| p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~" + "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~" + "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~} A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~} m{|x~}b{}x~hw~lk~k{|x~}b{}x~" + "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}" + "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}" + "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~" + "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|" + "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~} +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~" + "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~| " + "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{" + "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~" + "| s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{" + "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~| o{|n~|w{}u~b" + "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|" + "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${" + "}m~} ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~} mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d" + "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}[" + "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~" + "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~" + "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V" + "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~| i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~} *{|}p~" + "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}" + "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~ h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w" + "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}" + "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~| q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}" + "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|" + "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~| n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~" + "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V" + "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y| 9y|}u~}y| " + "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}| q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|" + "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{" + "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}" + "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U" + "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\" + "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~} 3{|~} ;f| '{|y}w~}y| 8{|y~|X{|x~}" + "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|" + "w~| B{|v~| 1{|y}u~y}| o{|x}u~y}y| Fv~| 7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y| a{|w~}C{}x~}O{|w~| oy}" + "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}" + "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~| ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~" + "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}| Ry|y}v~y}|" + " Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~ 5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~} r{|y}|Kw~|L{|y}|Hv~| E" + "{|y}u~y}| qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y" + "}y|S{|y}v~y}y| oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}" + "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~" + "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~| Mw~| K{|y~| e{|w~Nw~" + "| ?{}w~ Cw~} .{}w~ @{|v~|d{}| Kv~| !u~| J{|w~}C{|w~O{|w~| 9w~} Iv~ bw~}9{|w~| X{|v~ rv" + "~Lw~|M{}w~| w~| D{|w~| .w~| ?{|v~}g{|x~| M{|v~ {|u~| K{|w~}Bw~|P{|w~| :{}w~} Iw~} bw~}9{" + "|w~| X{}w~| r{}w~|Mw~|Mv~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|l~| 4{|w~" + "|Ax~}w{|x~} {{}y~} /v~| ?x~| f{|x~ M{} %{}w~|Uw~}D{}w~| Lw~| K" + "{|y~| d{|w~Pw~| ?{|w~ C{}w~ .{|w~ ={|u~}|l{|u~| N{}v~ {{|u~| L{|q~}H{}x~}V{}q~| :v~| Iw~}" + " bw~}9{|w~| Xv~ q{}w~}Mw~|N{|v~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|}o~}| " + " 3{|w~|Ax~}w{|x~} {{|x~| 0v~}m{} N{|x~ e{}y~} Rv~Tw~}Dv~ S{}x~x{|w~| " + " K{|y~| c{}x~}R{}x~} >{|x~| Cw~} .{|x~| ;{}t~}|sy|}t~| N{|v~} y{|u~| M{|q~}H{|w~V" + "{}q~| ;{}v~ I{|w~} bw~}9{|w~| Y{}w~} q{|v~}|Ow~|P{|}v~} ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} " + " )v~}Iy~} gw~|Q{|y}v~y}| 1{|w~|Ax~}w{|x~} yx~| 0{}v~|p{|~} N{|x~| f{|x~ " + " S{}w~}Tw~}E{}w~} S{}x~|y{|w~ J{|y~| bw~|Sw~| >{}y~} K{}y~} 9{|p~x}q~}| N{|u~" + "| x{|u~ M{|q~} y{}q~| K{|}|p{|u~| I{}w~| bw~}9{|w~| Z{|v~ o{}q~}Tw~|U{|p~ :v~ S{|w~}W{|w~|#{|" + "w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~|Aw|vx| y{|x~} 0{|u~|s{}x~} N{|x~| " + " f{|x~| U{|v~Sw~}F{|v~ R{|x~}y{}w~ J{|y~| b{|x}|T{|x}| w{}g~}| Q" + "x|y}u~} v{|u~ N{|p} yp}| K{|x~}y|wy|}u~} J{|}v~ aw~}9{|w~| \\{|}v~} nq~}Tw~|U{|q~| :v~ S{|w~}" + "W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~| :{|}w|}w~| /t~y}x|y}v~} U{|}|x{|w~| " + " f{}x~| W{|}v~}Sw~}H{|}v~} Qq~| J{|y} *{|}l~}| O{}q" + "~ tt| `{|i~} Lr~| aw~}9{|w~| `{}q~ l{}s~}Tw~|U{|s~}| 9v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~" + "} )v~}Iy~} gw~| W{|w~| :{|q~ .{|i~} U{|q~ ly}w|}w~| [{}q~Rw~}" + "L{}q~ P{}r~ M{|y}u~y}y| L{}r~| R{|j~} Ks~} `w~}9{|w~| " + " `{}r~| jy|v}|Tw~|U{|u}| 6v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy}| gw~| W{|w~| :{|r~| " + " -{|k~}| U{|r~} l{}r~} Z{}r~|Rw~}L{}r~| O{}t~ " + " k{}t~} -{|`}| `{|}m~}| Jt~} _w~}9{|w~| `{}s~| :w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}" + "w~Uw~} )v~} d{|w~| 9y}w~y} ){}o~}| S{|}u~}| k{}r~ Y{}s~|Qw~" + "}L{}s~| M{}w~} j{}w~}| +{}`~} ]{|x}v~y}| Gw~y} ]w~}9{|w~" + "| `{}v~}| 8w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} g{|w~| 8{|}v~y}| Ly| " + " g{|y}w~}| X{}v~}|Ow~}L{}v~}| Iy| " + "l{}`~} Ww~| " + " L{}`~} Ww}| " + " r{" }; + + // Define a 104x128 binary font (huge sans). + static const char *const data_font_huge[] = { + " " + " " + " " + " " + " " + " " + " " + " " + " FY AY " + "'Z ;W @Y @Y 'Z Y @Y (Z :Y ?Y (Z 0Y ?Y (Z >X " + " " + " " + " " + " " + " )X AX '\\ )XAV 7YDY -] BY BY '[ +YEY 2X AY (\\ -YDY 'XAU 3Y AY (\\ )XAV 8YD" + "Y LY AY (\\ ,YEY #Y " + " " + " " + " " + " (X CX '^ +[CU 6ZEY .` C" + "X CY '] -ZEZ 2X CY (^ .ZEZ )[CU 2Y CY (] *[CU 7ZEZ LY CY (] -ZEZ %Y " + " " + " " + " " + " " + " 'Y EY '^ ,^FV 6ZEY /b CX DX '_ .ZEZ 2Y DX '_ /ZEZ +_FV 1X CX (_ ,^FV 7ZEZ " + " KX CX (_ .ZEZ &Y " + " " + " " + " " + " %Y GY '` .aHV 6ZEY 1e DY FX" + " 'a /ZEZ 1Y FX '` /ZEZ +aHV 0X EX '` .aHV 7ZEZ JX EX (a /ZEZ &X " + " " + " " + " " + " " + " #X GX 'XNX 0dKW 6ZEY 1f DY HX &WMX 0ZEZ 0X GX 'XMW 0ZEZ ,dLX /X GX 'WMX 0dLX 7ZEZ" + " IX GX 'WMX 0ZEZ 'X :T " + " " + " " + " " + " ;X IX 'XLX 1o 5ZEY 2ZLY " + " CX IX &WKW 0ZEZ /X HX (XLX 1ZEZ ,o .Y HX (WKX 1o 6ZEZ IY IY (WKW 0ZEZ (X X MX &WH" + "W 3VHa 4ZEY 3WDW CX LX 'WGW 2ZEZ -X LX 'WHW 2ZEZ -VHa +X KX (XHW 3VHa 5ZEZ GX KX (WGW 2ZEZ )X " + " ?b " + " " + " " + " " + " ?W MW &WFW 4VF^ 3ZEY 4WBV BW MX 'WEW 3ZEZ ,W M" + "X 'WFW 3ZEZ -VF^ )X MX 'WFW 4VF^ 4ZEZ FX MX 'WFW 3ZEZ *X ?d " + " " + " " + " " + " " + " ?W X 'WDW 5UC[ 2ZEY 4VAV AW X &WDW 4ZEZ +W NW 'WDW 4ZEZ -UC[ 'W MW 'WDW 5UC[ 3ZEZ " + "EW MW 'WDW 4ZEZ +X ?f " + " " + " " + " " + " @X \"X 'WBW 6UAW 0ZEY 4V@V B" + "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ .VAW $W W 'WBW 6UAW 1ZEZ DW W 'WBV 4ZEZ +W >f " + " " + " " + " " + " " + " ?X #W 'W@W U?V AX #W &W@V NX #W &V@W 9W \"W 'W@V .W " + "\"W 'W@V !W >XHX " + " 3Y " + " " + " " + " 6W $W &V>V U?V @W $W &W>V " + " NW $X 'V>V 8W $X (W>V /X $W 'W>V #W >XFX " + " 5Z " + " " + " ,Z " + " GZ " + " #U?V NY 7Z ,X CVCW MY " + " 7Z ,X $Z 7Z ,X >Z 6Y ,X 4Z 7Y +W 7Y @Z " + " " + " +Z " + " " + " HY \"U?V " + " MY 8Y ,Y CVBV LY 9Z ,Y #Z 9Z ,Z >Z 8Y ,Y 3Y 8Z ,Y 9Y " + " ?Z " + " *Y " + " " + " IY !U?V " + " LY :Y ,[ $R>U ,V@V MZ :Y +Z #Y 9Y +Z ?R" + ">U 8Y 9Y +Z %S?U HY :Z ,[ ;Y ?[ " + " " + " )Y " + " 8U " + " 9Y V@U JY Y @Y /X 0Y K` .X " + " ^ =ZEY @Y " + " NVAV

Y E^ /X 0_ %f 1] 'c " + " @ZEZ AY MV" + "CW X *^ +]DU 7ZEZ 5U>U JY ?Y *^ -YEZ 4Y " + " ?Y *^ .ZEZ 5[ ]DU 5Y >Y +^ ,]DU 6ZEZ Y ?Y +_ .ZEZ \"Y Z G[ G\\ @e !f JX !Y " + "LY %d :Y Y Ha /X 0b *j L] D_ " + " +g A[ LY 8Z -ZEZ \"Y 1o )V FX NZ FY " + "%Y ,X NX*Z NW 3WEW H\\ #[ !Z \"[ \"[ \"[ G[7T 8g 0Y " + "@Y +_ ,_FV 7ZEZ 5U>U IY @Y +` .YEZ 3X ?X *` /ZEZ 4[:P 8_FV 4X ?Y +` ._EU 6ZEZ NX @Y *_ .ZEZ #Y ;Y" + " FYEZ ;] GU W ,X " + " FV a \"d -g >d (d +b %b 4f Bg Ie \"e \"h " + " Ge !f IX \"Y LY &e :Y Y Jc /X 0c " + " -n $g I` .j >a ;e HU .U +b Ac 2ZEZ 'b " + " 5o -] Na (c KY .Y #_ 8Y!W'Y\"X.c$X 3XGX Mf -e +d " + ",e ,e ,e \"e=V ;k 1Y BY +XNW .aGV 7ZEZ 5V@V HX AY +XNW .YEZ 3Y AY *WNW /ZEZ 4\\>T 9`GV 3" + "X AY +XNW .`GV 6ZEZ NY AX *XNW /ZEZ $Y :Y FYEZ <_ IU (Q LZ 4Z2Z 1Q " + " &g %Z +XCX MT Y Kd /X 0e 0p " + " (m Lb 1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik LW DX DW /i ?Y(Y 4h 5ZEZ" + " ,\\ ,h 7\\ -o .` $f -h NY No %_ %c @_\"X-_\"W0h&W .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S " + "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ Im 1X CY *WMX /bHV 7ZEZ 5V@V G" + "X CY *WLW /YEZ 2Y CY *WLW 0ZEZ 3[AW :bHV 3Y BX *WLW 0bHV 6ZEZ MY CX *XMX 0ZEZ $X 9Y FYEZ " + " =a M~i 7U (Q N_ 9_8_ 3R )k 'Z +XCX +X@X 4T >e,X Cl &X IX *X GV " + " GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j /l N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z" + " Ep =t 5o Au 1u N~d'Z(Z)Z MZY " + " Le /X 0e 1r +r c 3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko " + " Y EX EY 2m @Y)Y 6l 7ZEZ 0e 2k >e 1o 0c 'j /i X !r (b 'g Eb\"W0c#X0i(W -" + "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ Kp" + " 1X DX *WKW /WMYJV 6ZEZ 5V@V GY EY *WKX 0YEZ 1Y EY *XKW 1ZEZ 2[EZ :WMZKV 1Y DX *WKX 1WLYKW 6ZEZ L" + "Y EY *WKW 0ZEZ %X 8Y FYEZ >c M~h 7T (S !a Y >X 8f /X 0f 3t -s c " + " 4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms #[ FX F[ 4n @Y*Y 6m 7ZEZ 3k 5l Bk 4o 1f )k 0k #" + "X #u (b (i Fb#X0c#W/k+X .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t " + " 0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ Ls 2X FX *WIW 1WJc 6ZEZ 4VBV EY FX *XJW 0YEZ 0X EX )WJW 1ZEZ 1[I^ x %_ ?y 5r F~S Ct :p" + " 6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ " + "@e 2X Gf +a MX %Y LY *i :Y Y >Y 9f /X 0g 5v " + " 0u d 6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w &] GX G] 6U &o ?Y+Y 7X )n 7ZEZ " + "6p 7m Eo 6o 2h *l 1l %X #v (b )k Gb$X/c$X/l,W -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z " + "!Z \"Z :~ D_-Y Hw =v >w >w >w 4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ Mt 1X HX )WHW 2VHb 6ZEZ 4WDW DX GX )WHW 1YE" + "Z /X GX )WHW 2ZEZ 0[M` ;VHb /X GY *WHW 3VHb 5ZEZ JX GX )WHW 2ZEZ 'Y 7Y FYEZ ?e M~f " + " 7U )U %g Bh@g :W .~T 't +Z +XCX ,X@X 3T Ak1X Er (X JX 'X IV HX 8q" + " =m 7y ?y '` ?y 6s F~S Dv Y >Y " + " :] %X &] 5]C\\ 1v Nc 7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y (_ H" + "X H_ 7U 'p ?Y,Y 6X *o 7ZEZ 8t 9YH] Ht 9o 3i *XG[ 1VE[ &Y %x (b *[I[ Hb$W.c%X.VE[-X " + " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az 7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ Na" + "J_ 2X IX )WGW 2VG` 5ZEZ 4XFX CX IX )WFW 2YEZ .X IX )WFW 3ZEZ /j 8VG` -X HX *WFW 4VG` 4ZEZ IX IX " + ")WGW 2ZEZ 'X 6Y FYEZ ?XKX M~f 7T )W 'i DiAi ;X 1~V (w -Z " + "+XCX ,X@X 3T AZI[2W Es (X KX &X IV HX 9s >m 7z @z )a ?y 7t F~R Dx >t 9v 8s 2` :~P <~Q&~S" + " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X ;Y -e MX 'Y " + "LY +[ +Y Y >Y :[ #X #Z 6\\?[ 2v F\\ " + " 8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#} +` HX Ia 8U (q >Y-Y 6X +p 7ZEZ 9bMb ;U@Y JbMb :" + "n 3ZIZ +T@Y 2R>Y 'X %y (XLV +ZEZ IXMW%X.YMW%W-R>Y.W -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z " + "!Z \"Z :~S Ha/Y K| B| C| D} D| 9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ N]B\\ 2X JX *WEW 3UE_ 5ZEZ 3YJY AX JW )WE" + "W 2YEZ -X KX (WFW 3ZEZ .f 5UE_ ,X JX )WFW 4VF_ 4ZEZ HX KX )WEW 3ZEZ (X 5Y FYEZ @YJW M~" + "e 7U *X (j EkCk =Y 3~X )x -Z +XCX ,W?X 3T BYEY3X Ft (X KX %X JV " + " IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D" + "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X ;Y .g MW 'Y LY +Y )Y Y " + " >Y :Z \"X \"Z 7[=Z 3aE[ E[ 9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$" + "~P -b IX Jc 9U )r >Y.Y 5X ,]DX 7ZEZ ;\\>\\ \\ 0XDX ,R=Y MX (X %hEW (SG" + "V ,YAY JSHW%W-SGW&X GX/W ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P" + " <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ \\>Z 1X LX )VCW 4UD] 4ZEZ 2f ?X LX )WDW 3YEZ ,W KX )WDW 4ZEZ -b 2UD] *W" + " KX )WDW 5UD] 3ZEZ GW LX (VCW 4ZEZ )X 4Y FYEZ @XIX M~d 7U *Y *l GmDl ?[ " + " 6~Z *`C\\ -Z +XCX ,W?W 2T CYCY5X E]CZ (X LX $X JV IX 9]E^ @m 7aGb B^Ec ,b ?y " + "9aF[ F~R E_C_ B_E^ ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa" + "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X ;Y /i MW (Y LY ,Y (Y Y >Y " + " :Y !X !Y 8[;Z 1\\ 0\\:U D[ ;ZbCh%Z(Z" + "#Z(Z$Z(Z$Y'Y![.Z HZ Z;Z 1X NX )WBV 5VBZ $e >W MX )WBW !X MX )WBW #` /UBZ (W MX )WBW 6UBZ " + " 9X MW (WCW MX 3Y GXHW M~d 8U *[ +m HnFn A] 9~\\ +^=Y" + " -Z +XCX -X@X 2U DXAX5W E\\=V (X LX #X .R@V?Q ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@] " + " j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`" + "2Z0[4[ LZ/[\"~^ @X #X Y >Y ;Z " + "!X !Y 8Z9Y 6d 4[5R CZ ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=` 1f KX Lg " + " ;U *\\=T =Y0Y 4X ,Z;R 5Z3Y &W !Y3Y 3W@W EW LX *W %jEW KV -X=X @W'X W'X EX1W ,b " + "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=` @`=e%Z(Z#Z(Z$Z(Z$Y'Y" + " Z/[ HZ !Z9Y 0W X )WAW 6VAW \"d Y >Y ;Y X !Y " + " 8Y8Y 6f 6Z2P BY j BZ(Z+[;Z;Z'_9_ 3h LX Mi <" + "U *[:R V EW KW +W %kEW KV .X;W @W'W NW(X CW2X -c *c )b " + "*c )c +c 7ZHZ 2_5[ NZ !Z Z !Z >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_ B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z" + "8Y 0W !W (V?W I` :X !W (V?W X \"X (W@W *d EX !W (W@W 0X \"X (V?W !W 1Y #d ," + "e +d +d ,e #XHW LZ#Z 7U +] -o KqHp C_ X #X " + " Y >Y ;Y X X 9Z7X 6g 7Y" + " #Z =Y8Z 7d 7[ Z )_7_ Bp EZ(Z+[;Z;Z(^5^ 5j MX Nk =U +[7P Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5] C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V H^" + " 8X #W (W>V NW \"W (W>W .h EW \"X )W>W 0W #X (V=V \"W 0Y &j 1i 0j 1j 1i &X ` .\\5U -Z +XCX -W?W =r'X>W8X EZ ;X NY !X 1XDVDX 2X " + " &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y L\\ 2Z E[7[ G\\9[ >S5[ F`7` ?YNY Y >Y ;Y X Y :Y6Y 7i 9Y \"Y " + " >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2] 6l NX m >U +Z !Y4Z 3X -Y NW(W (W " + " &X)X 8VZ !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2] E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'VR4[ G^1^ AZNY Y >Y ;Y X Y :Y6Y 7j :Y \"Y " + " >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\ 8n X !o ?U ,[ Y5Y 2X -Y W&W )W 'W%W 9V" + "Z " + "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0] F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X +P " + " %_K[ CY *r 9q 8r 9r 9q *X ;Z%Z >Q JT ,b 0q MsKs Ge " + "C^ *[0R -Z +XCX .X@X @v)X=X:W CY :X Y NX 1[HVH[ 1X 'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y M[" + " 1Z EZ4[ I[5Z ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5" + "\\!\\-\\ Z4[ GZ ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z [ ;X \"X =Y 5\\C[ #Y LY -Y 'Y 8X >Y " + " >Y ;Y X Y :Y6Y 7k ;Y \"Z @Z5Y 9YLY 5[ #Z +\\.] J| KZ" + "(Z+[;Z;Z*\\-\\ :p !X \"q @U ,Z NY6Y 1X -X W#V *W (W#W :U;V +X DW LW )mEW KV" + " /X9X BW*X LW*X BW3W +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z Z !Z >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\" + "#\\-\\#\\-\\#\\-\\ H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y /[G[ " + " DY +u =u S LU ,c 1q MtLt Hf E] )[.Q " + " -Z +XCX .W?X Bx)X=X;X DZ :X X MY 0ZIVIZ /X 'X ;Z7[ 1Z AZ ![ 4XKZ ?Y MZ 0Z EZ3Z I[5Z " + "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[ ;Y 9Z(Z$Z" + "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X =Y 6\\A[ $Y LY -Y 'Y 8X >Y >Y " + " ;Y X Y :Y6Y 7l Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP" + " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X /XC[ EY " + " -x @x >x ?x @x -X :Z'Z ?U MU -e 2q MtLt Ig E[ 'Z,P -Z +XCX .W?W By)" + "XZ0Z" + " J\\#Q'Z*\\ KZ MZ +[ Q$Z(Z :Z \"Z 4Z<] Y 7[>[ %Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" + "5Y 7UH_ Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z" + "(Z$Z(Z$Y'Y K[9[ Ct =Y3X /U@[ \"Q EY .z B{ " + "B{ Az B{ /X :Z'Y >V U -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[ '[ 8Z +XCX /X@X C`MTL_)W;" + "WZ0Z " + "J[ 'Z)\\ LZ MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !" + "\\ 9X !X >Y 8[<[ &Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y5Y " + "7RB] =\\ $Z BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\ ?\\MXL[ $X %\\LXM\\ CU" + " ,Y *Q\"R DY9Y 0X -Y #V=_?V Cm *V LV Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z(" + "Z$Z(Z$Y'Y J[:Z Bw @Y6[ .Q<[ #S GY /`Da E`C" + "` DaD` C`Da E`C` 0X 9Y(Z ?X !U .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[ &Z 7Z +XCX /X@X C\\" + "ITFY)W;W=X BY 9X !X KY +YNVNZ *X (X ;Z4Z 2Z @Z !Z 6YJZ ?Y Z /Z DY2Z JZ1Y ,T T MZ N[ NZ HZJ" + "Y >Z0Z K[ &Z(\\ MZ MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;" + "[ ![ 8X !X >Y 9[:[ 'Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" + "5Y %\\ =] %Y BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[ @\\LXK[ %X &\\KXL\\ " + " DU -Z +S$T EY:Y /X -Z %V?fBU Eo +VEg=V =VZ !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$" + "Z(Z$Y'Y IZ;Z Ay BY9^ G[ %U HY 0]<^ G^=^ F" + "^<] E]<^ G^=^ 1X 9Z)Z @Z \"U .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[ &[ 7Z +XCX /W?X D[GTC" + "V)W;W=W AZ :X \"Y KY *j (X (X ZY .Y3Y 3Z '\\ MZ )Z ;Z 2^ +Y ;Y " + "X Y 6Y /Y5Y $[ =` G^ !Z IZ M\\ #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[ B\\KXJ[" + " &X '\\JXK\\ H\\ 1Z ,U&V EY;Y /X ,Z 'V@jDV Gp +UDj?V >VZ !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)" + "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[ \"[ &Z &[ ![" + " #\\ #[ ![ G[@W IYBZ J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z @Z \"U .k 5q N~o Mm 4l =X" + ".X 7^ 7Z3Z NZ %Z 6Z +XCX /W?W D[FT@S)W;W>X AZ :X \"Y JX (f &X )X ;Z3Z 2Z @Z !Z 7" + "XHZ ?Y !Z /Z CY1Y JZ1Z 2Y Y $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ MZ -[ Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'[" + "$[ NZ2Z%[%[#Z2[ JZ :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X X ?Y ;[6[ (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X NX " + "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y X Y 9_>W KY5Y #[ =c h >XD` " + "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[ C\\JXI[ 'X (\\IXJ\\ " + " (Y d 5Z -W(X FYV=W +X HX )^ ,Y1Y HnEW KV 0X7W BW-W HW.X M^/X )" + "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#" + "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y NX NX X E[ >XD` -c )c *b *c )c '\\ &bDX L" + "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y 8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z AZ !U /m 6q N~o No 6o ?X.X 8_ " + "6Y3Z Z $Z 6Z +XCX 0X@X DZET>Q)W;W>W ?Y :X \"X IY 'b $X )X ;Z2Y 2Z @Z !Z 8YHZ ?Y " + "!Z 0[ CY1Y JZ1Z 5\\ \\ 'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ" + "2Z&[#Z#Z2[ JZ :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z C[IXH[ (X ([HXI[ (" + "Z $k 9Z .Y*Z FY=Y .X ,\\ *UAnCU J^CW -VCmAV ?W>V *X IX (a /Y1Y HnEW KV 0X7W BW.X HW.W La3X " + "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z(" + "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y Y NX Y E[ ?XFd 1g .h /h /h /h )\\ )hHX " + "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y 9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y A[ !T /n 6q N~o q 8q @X.X 8` 7" + "Y3Y Z $Z 5Z +XCX 0X@X DYDT EW;W?X ?Y :X #Y IY %^ \"X )X k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[ CYHXGY 'X 'YGXHY 'Z &o" + " ;Z /[,[ FZ?Y -X +\\ +UBoBU LZ>W -UBnAU >W@W *X JX 'c 1Y1Y HnEW KV /W7W BW.W GW/X Lc5W 'Y ," + "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#" + "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y Y NX Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0" + "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z AZ !U /o 7p M~n s :s AX.X 8` 7Z4Y Y" + " #Z 5Z +XCX 0W?X EYCT EW;W@X >Z ;X #Y HX #Z X *X ;Z1Z 3Z @Z !Z 9XFZ ?Y \"Z /Z " + "BY2Z KZ0[ [/Z 4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y NX 0X5\\ 6Y FY" + "JiBi$YJk 8o ?YJj 9kJX ;YJc Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\" + "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t t TAU NX;W )P9P =UAWAYAU >XDX )X LX HY 3Y1Y HnEW KV /W7W " + "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-" + "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u oLX ;YLe ?u VAW?XAU ?ZHY (X MX EX 4Y1Y HnE" + "W KV /W7W AQ:Q :W0W EW1X Z !Z !Z \"Z :Z%['YHZ" + "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y Y N" + "X Y *r BXKn qMY GYMp 0Y NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u U@W?XAU >j (X " + " NX CX 5Y1Y HnEW KV /W7W AR;R ;W1X EW1W :XZ " + "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ w ?x >x ?w >w#wKv Nu ?v" + " =v =v =v 0Y Y NX Y +s BXLp >u \\ DX.X :c 7Z7Z!Y \"Z 4Z +XCX C~d&XBT DW=XB" + "X :[ >X $Y FY +f &X +X ;Z/Z 4Z AZ !Z ;YDZ ?YFP -Z?Q BZ ?Z5Z JZ/Z 5Z \"[ Gj Ii ;[\"X1Q,W\"YCZ BZ1" + "Z MZ \"Z$[!Z MZ /Z LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[ ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\" + " 1X MX AY BZ&Z 8^Ga AYN[H_ " + "YDY *X )b 6UDY%U V9W ,SU@W>W@T =h 'X X AW 5Y1Y HnEW KV /X9X ASZ !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z(" + "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y Y NX Y ,u CXM^Nb" + " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0" + "Z CZ ;P4U 1w 9l J~m#z B[;[ EX.X :d 7Y7Y X )~Q #Z +XCX C~d&XBT DW=XCX 9\\ ?X $Y FY " + "-j (X +X ;Z/Z 4Z AZ \"Z :XCZ ?YM_ 5ZE^ IZ >Y6Z IZ0[ 5Z \"[ Jj Ci ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z " + "MZ 0[ LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[ ^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\" + ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y X Y M];\\ F]E[JX IY9[ LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^" + " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[ W@U =f &X !X @W 5Y1Y HnEW KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC" + "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z" + "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y Y NX Y -w DXNY" + "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1" + "Z DZ =S4U 2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y *~R #Z +XCX C~d'YBT DX?XBW 7\\ @X $Y FY " + "/ZNVNZ *X ,X :Z/Z 4Z AZ #Z :XBZ ?o 9ZGc MZ =Z8[ HY0\\ 6Z \"[ Li >j C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!" + "Z MZ 0[ LZ(Z :Z \"Z 4ZJ] .Z 2YHXY 9Z(Z NZ2Z,Z\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6" + "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y X Y `>` I\\B[KX IY:\\ LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L" + "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\ XAU V ?W3X CW3X 8X>W #Y /Z" + "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*" + "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y Y " + "NX Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z ~d W5T 2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X )~R #Z 0~d&XBT DX?XCX 6\\ " + " =Y EY 0ZMVMZ +X ,X :Z/Z 4Z B[ %\\ :XBZ ?q ;YHg Z \\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;ZW>X@T ;a #X #X =W 6Y1Y GmEW KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :" + "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z" + "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y Y NX Y /_B] E_<" + "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y ;X~d#Z6U 3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X *~R \"Z 0~d&YCT CXAXBW 5] " + " >Y EY 2ZKVKZ -X ,X :Z/Z 4Z BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki J\\!X:hKVAW%Y@Y CZ5\\ L" + "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F` BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X" + " LX BY JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y EY .Y1Y " + "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y X Y \"eCd L[>YLX HY>^ IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G" + "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.ZUDX!T\"XW>X@U :] !X $X Z !Z !Z \"Z :Z#Z(YEZ~d&^7U 4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X )~R \"Z NW?W BYCT CYBXCX 6_ ?Y EZ 5ZI" + "VIZ /X ,X :Z.Y 4Z C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z " + " MZ 0Z KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X KX CY " + " )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y EY .Y1Y GY3Y#Y=WNX=Y M" + "ZAZ AY3Y %[ /Y X Y #gEf N[W>W?U 7W <~d BX ;W 6Y1Y GmEW KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y" + "?Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZY D~P JZ !Z#[\"~Q Dy Z K~] :Z \"Z 4ZN] *Z 2YFX?XF" + "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X KX CY (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] " + "G]7Z 4Y NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X NX Y $iGh Z:XNX" + " GYHg HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[W>W?U K~d CX ;X " + " 6Y1Y FlEW KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y Y Y ;Y?Z JZ>~Q3[ I~Q G~Q F~Q G~Q 5Z !Z !Z " + "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY @Y Y !Z Y Y Y 4_4Y)[ %Z3" + "Y GZ3Y FZ4Y FZ4Y 4Y Y NX Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[ Y D~ IZ !Z#[\"~Q Dy![ K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck Y 9Z(Z LZ6Z(" + "Z?XDX?Z G` *d #[ +X KX CY 'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I" + "Z5\\ ;] -X DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y NX Y $XIZHZIY!Z:XNX GYHf GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#" + "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[Z EiKh 6X /XC^ BTDX U\"YA\\ 4ZCZ N~d &U>W?X>T K~d EY :W 5Y1Y EkEW KV ,YAY =ZCZ DW6X @W6" + "X 5W@W 'Z>Y Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L" + "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY @Y Y Y NY Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y Y NX Y 2[6Z G" + "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z Z6Y &[ .Y NX Y %WEYJYEX#Z8a GYHe FY DX4[ DY $\\5Y CY8Z 5d Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$" + "Y'Y >c 6Z2Z KYDY ?Y X NX NY Y Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y Y NX Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ" + "4[ >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z <~d'RKY:U 5~U J" + "~T$~g'~X KY1X GX.X Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)YZ=Z =YZ=Z =YZ=Z LZ=~Q3Z H~Q G~Q F~Q G~Q" + " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY ?Y Y X MX Y Y" + " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y Y NX Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z >Z:a IY0X HY0X GX0X GX0Y FZ7Y E[" + "3Z GY6Y ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z <~d NX:U 5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W " + " NZ 3Y X@X ?]IT ?hCW 7h2X ;Y CY 7TAVAT 1X .X 8Z.Y 4Z G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5" + "^ 4i ;~d :i 1[ LWr *Y " + "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X JX DY &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y NX 0XE\\ &Y FZ4[5Y*[4Z IZ" + "2Z H[2Y KY2[ ;[ +X DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y NX NY *Q NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y" + " 5YMY [/[IuI[.\\ 4X 4\\ =X =\\$\\" + " =X MZAU -Z &X8Y G~W 6X 0W<\\ FUEX MT iNW 8[D[ K~d &T=WE\\QZZeBX] ,Z 1j <[7_ 7i 8~d 7i 5[ KW=Z=" + "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z[/[IuI[/\\ 3X 3\\ >X >\\\"\\ >X MZAU -Z 'X6X 5c " + "%X 1X;\\ GUEX MT NgMW 9[D[ J~d &T=m;T K~d In 4TA[ 4Y1Y BhEW 3Z DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z KY" + ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8" + "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[ @X NX Y NY X NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y Y NX Y 4Z2Z HZ3Y IZ1Z I" + "Z1Z JY1Z JZ1Z IZ1Z @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z I" + "[ 7YY ~m 4Z 3Y W?X >g =cAW?]'[K\\5Y ;Y CZ %V M" + "X /X 7Y-Z 5Z H[ 4l ;XZ>Z.[IuI[0\\ 2X 2\\ ?X ?\\ \\ ?X MY@U 8y ;X6X 4a $X 1X9[ HUEX MT MeLW :[D[ I~d &T=l:T " + "K~d Io 5m 3Y1Y AgEW 3Z Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[ LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z H~Q G~Q F~Q G" + "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[ AX NX Y NY Y X" + " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y Y NX Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D" + "Z0Y GY9Z ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y HZ 5X_@XAa*[I\\6Y ;Y CZ %V MX /X 7Y-Z 5Z I[ 3n >X;Z ] G`9\\ .Z 4s @[9` " + " =i /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z" + "$ZAW@WAZ F_ %\\ $[ &X IX EY &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0" + "Z ;Z *Z FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z MX N[ 7g1U U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BYY1Y%Z" + " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\ 1X 1\\ @X @\\ M\\ @X NZ" + "@U 8y ;W4X 5` #X 1X8Z HUEX MT LbJW ;ZC[ H~d &T=j8U L~d Io 5l 2Y1Y @fEW 3Z Nl 0c 0[CZ&lDW5[>mEXE\\N^" + "AlAX6\\ LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K" + "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\ BY X NX NY Y X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y Y NX Y 5Z0Y HY" + "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /" + "Y:Z IZ 4Y=T 6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m 4Z 3Y !X@X ;l @[>WBe,ZG\\7Y ;Y" + " CZ %V ;~c LX 7Y-Z 5Z J\\ 2n @Y;Z N\\ G`8\\ /Z 5u A\\V+Y8Y G~R LZ !Z\"Z\"~Q" + " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZY1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z " + "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX NZ@U 8y dEW 3Z Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6] LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z H~Q " + "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\ CY X NX" + " NY Y Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y Y NX Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$ZX /Z;Z JZ 2X>U 6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y " + "N~m 4Z 3Y !X@X :n 'WBg.ZE\\8X :Y CZ %V <~e NX 6Y-Y 4Z K\\ #a AX:Z M\\ H_6[ 0Z" + " 6aI` A]?c ?f $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z " + "K['v +o 2Y 9Z(Z IZq:X !U:[9U&Y5] DY?d =jLX FY/Z C[ " + ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX" + " B~o BX NZ@U 8y mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ" + "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y Y NX Y 5Y/" + "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CYX .Y;Y JZ 1Y?U 6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m 4Z 3Y !W?X 9p +XCi0ZC\\9X " + " :Y CZ %V <~e NX 6Z.Y 4Z L\\ M^ CY:Z L[ H^4Z 0Z 7^A^ C_Ce ?c Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\"" + "Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X HX FY " + " >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^ KY .Y1Y CY;Y KYCXIXCY " + "Bc 4Y\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ" + "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX NY?U 8y mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7" + "Y CY7Z#Z:Z Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ " + "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y Y NX Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X" + " HY0X GX0X GX0Y CYX .YW-Y6Y HZ2\\ Z !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z" + "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X HX FY At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y NX" + " 0XM\\ MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e JX Ke KzF^ !U9Y7T'Z4[ CY7] @[E" + "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L[" + "4~p BX B~o BX C~q CX NY?U 8y Z !Z !Z \"Z :Z#[)Y>ZCY*Z K" + "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z 9WK~KW/WJ}JW.W:\\:W.W" + "9[:W/W9[9W >X .Z=Y JZ /X@U 6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l 4Z 3Y \"X@X 3n /X" + "CZIZ2Z@\\W.Z6" + "Z IZ1[ Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ " + "\"X GX GY Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0XN\\ LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z" + " KYDXGWDY @a 3Z>Y +[ 5d IX Ic L~d !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y " + "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX NY?U 8y Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?" + "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0" + "X GX0Y BY>Z BY.Y EY>Y 8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z KZ .YAU 6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z" + "CZ L~k :y KY \"X@X 0m 1WCYEY3Y>\\=X 9Y BY %V <~e =l X 5Z.Y 4Z \\ E[ GY8Z JZ I]" + "2Z 2Z 8[7[ BqMZ ?^ C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s" + " I[ LZ&[Cc Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X GX GY Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0e KY" + " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b GX Ga L~c T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f " + "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[WEY9T -X EY1Y 1WEW 3Z 6ZCZ 7X7" + "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z " + "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` Y 8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z " + " LZ -YBU 5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW 'y JY \"W?X ,j 3WCYCY4Y=\\>X 9Y CZ" + " %V <~e =l X 5Z.Y 4Z !\\ C[ IY7Z JZ I]2Z 3[ 9[5[ BoLZ ?a Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z MZ 0" + "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^ M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X GX GY " + "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ " + "3` EX E_ L\\Cx NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ" + "4Y 4\\ 1Z 1[ NZ.[" + "WDX:U -X EY1Y 1WEW 3Z 5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[ IZ !Z " + " Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ " + "B{'~x/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y " + "DY@Z 8WK~KW/WJ}JW.W=aX ,Y?Y LZ +XBU 6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX " + " (y JY \"W?W (h 5XCXAX5Z<\\@Y 9Y CZ $T ;~e =l X 5Z/Z 4Z \"\\ AZ IX6Z JZ I\\1[ 4Z 8Z3Z AmKZ" + " ?d d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(YWCX;U -X EY1Y 1WEW 3Z Is 0YAX 8W6UJV IW)W" + " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Yc=W.W=[6W/X:[:X >X ,Y@Z M[ " + "+YCT 5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX )y HX #X@X (TNc 6WCX@X5Y:\\AX 8Y CZ :~e" + " =l !X 4Z/Z 4Z #\\ @[ KY6Z IZ I[0Z 4Z 9Z2[ @jJZ ?f %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z MZ 0Z Z'Z(Z" + " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(YWBXZ !Z !Z \"Z :Z#Z(YW.W>[5W.W:[:W =W +ZAY LZ *YDU 5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX " + ")y HX 6~e 9TJ_ 7XCX?X6Y9\\BX 8Y CZ KX Nl !X 4Z/Z 4Z $\\ >Z LY5Z IZ I[0Z 5Z 8Z1Z >fHY =h " + " +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[ K\\ 6Y 9Z(Z FZ" + "BZ MYFXY FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0e KY FY3Y2Y+Y1Y KZ-Y JY.Y" + " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e JX Ke LU4k IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&" + "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1] 1X 1\\ @X @\\ L\\ AX DX 4" + "Z?U -Z (X4X H~W ;\\;W GTDX\"U s A[D[ 6X %T>WBXZ !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC" + "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY" + "-Y LZ-Y MY-Z MY-Y LZ-Y CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY 6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ " + " MZ (XDU 5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX +y GX 6~e 9TG] 8WBW>X6Y8\\DY 8Y CZ " + " KX Nl !X 4Z/Z 4Z %\\ =Z LX4Z IZ I[0Z 5Z 9Z0Z X3W?W/~S KZ-Z\"Z \"Z#Z!Z MZ 0[!Z" + "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[ K\\ 7Y 9Z(Z FZCZ LZGX^ .YCZ ." + "[ )_ KX L_ ES/e FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J" + "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\ 1X 2\\ ?X ?[ M\\ @X DX 4Z?U -Z 'W4W G~W :]>X GTDY#U s @[D[ 7" + "X %U?WAX>U ,X EY1Y 1WEW \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z IZ !Z Z" + " !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8" + "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y BYDXAY KY0X HY0X GX0X GX0Y" + " @ZCY ?Y.Y CYBY 5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ 6~d IYET 4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX " + " Z 3X 6~e 9TF\\ 9WBX=W7Z7\\EX 7Y CZ KX Nl \"X 3Z/Z 4Z &\\ ;Z M~Z %Z I[0Z 6[ 9Z/" + "Y 8ZCZ 8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6[" + " J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\ LX EX IY L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y NX 0XM\\ MY " + "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\ LX M\\ AR+` CT9[:U'Z3X AY0Y FY9Z FY-Z " + "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\ 2X 3" + "\\ >X >[ \\ ?X DX 4Z?U -Z 'X6X G~W 9^@X GUDY$T Ns ?[CZ 8X %U?WAY?U ,X EY1Y 1WEW \"s 4" + "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N" + "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2ZX )YCY 5~d IYFU 4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX Z 3X 6~e 9TD[ ;XBX=X8" + "Z6\\GY 7Y CY JX Nl \"X 2Y/Z 4Z '\\ :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z 5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ " + "#Z$[!Z MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[ K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\ KX EX" + " IY LZ4Y FY.Y KZ %Z.Y KZ X DX 4Z?U -Z 'X6X G~W " + "8^BX FUDY%U Ns =ZCZ 9X $U@W@X?T +X EY1Y 1WEW \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~" + "T J~U K~T*~ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ " + "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y Y Y Y 9Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYEW?Y" + " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY 4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ 5~d HXFU 4~_+~i z @w DX.Y" + " IX.X BZ@Y 6YGZ IY Y @~e 9TCZ ;WAX=X8Y4\\HX 6Y CY JX Mj !X 2Y/Y 3Z (\\ 9Z" + " M~Z %Z I[0Z 6Z 8Z/Z \"Z 2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ " + "Z Z .Z [%Z4Z JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\ JX EX IY MZ3Y FY.Y JY %Z/Z JY Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z" + "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y Y Y Y 9Y Y " + "NX Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ 4X:X9W,W JW+WE\\H[EX,X" + "E[1W,W JW =X )ZEY 4~d HYHU 2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX Z A~e 9TCZ ~d >i 2Z GV>X3W@W0~V LZ-[\"Z " + "#Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[ KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[ IX DX J" + "Y MY2Y FY.Y JY %Z/Z JY Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N" + "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\ 5X 6\\ JTEXET J[&\\ KSDXES $Y 3Y?U -Z &Y:Y F~W 5_GX DU" + "CZ9QAU DZCZ ;X $VAW?YBU +X EY1Y 1WEW DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V" + ",~P :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y " + "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y Y Y Y 9Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z BYGX?Z KY1Y HY0X" + " GX0X GX0Y >YFZ >Y.Y AYFY 2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY 4~d GXHU 2kNRMk*tNq Mv Y 7ZIZ GY !Z A~e 9TBY `=Y(Y8ZJZ([\"[ Z " + ".[!Z$Z3Z KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\ IX DX JY MY2Y FY.Y JY $Y/Z JY YEY CYIWBXIX @f 0YGZ 0[ LZ NX NY 'U>WMW?V&Z4Y AY/Y HY8Y" + " EZ.Y FZ %Y1Y Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\ 6" + "X 7\\ JVFXFV J[(\\ KUEXFU %Y 3Y?U -Z %YXCU *X EY1Y 1WEW" + " F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+[" + "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y Y Y Z :Y Y" + " NX Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY 2X;X:W+X LX*WH\\D[HX" + "*WG[0W+X LX =X (YFZ 4~d GYIU 2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX !Z A~e 9T" + "BZ >W?W;W8Z2\\MY 4Y DY JX 4X 1Z1Z 3Z +\\ 6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z )i D~d Ci -Z GV=W4XAW/~W M" + "Z-[\"[ $Z&[ NZ MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[ L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\" + " HX DX JY NY1Y FZ0Z JY $Y/Z JY YEY BXJXAWJY A[N[ 1YGY 0[ JY NX NY 'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y Z " + "/Z K_MZ BUC]BVBU A[D[ >X #VBW=XDU *X EY1Y 1WEW G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M" + "~W N~X M~W N~X.~Q :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z" + " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z :Y Y NX Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z " + " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY 1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW Z 7ZJY EY !Z 1X@X &TAY ?X?W;W8Z1\\NX 3Y DY JX 5Y 0" + "Y1Z 3Z ,\\ 5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2" + "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[ M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\ GX CX KY NY1Y FZ0Z JZ %Y/Z JZ =Y 4" + "Y0Z GY1Y 5Y NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[ IY NX Z &VB" + "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y Z !Z !Z \"Z :Z&[&" + "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z" + " !Z :Y Y NX Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y AYIWW;W8Z0e 3Y EZ JX 5X /Z2Y 2Z -\\ 4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z #j J~d Ii CW>X6Y" + "BX0~Y NZ-[![ %Z'\\ NZ MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<][ 0" + "Z 3\\ FX CX KY NY2Z FZ0Y IZ %Y/Z JZ =Y 4Y0Z GY1Y 5Y NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 7Z 8Y" + " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[ HY NX Y %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ " + "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\ 9X :\\ J\\IXI[ I\\/\\ K[HXI[ (Y 3Z@U -Z " + "%^F^ /Z X \"f >VBnCU >[D[ @X \"VCWZ !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v " + "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y Y Y Y :Y Y NX Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW" + ";Y IX1Y GX1Y GY2Y GY2Z YJX(XJY/X)X Y W;W7Y/c 2Y EY IX 5X /Z3Z 2Z .\\" + " 3Z M~Z &Z FY1Z 8[ 6Z/Z $Z i L~d Li @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ MZ -[$Z'Z(Z :Z \"Z 4Z<] Z !Z !Z \"Z :Z(\\%" + "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s YJY .X=X=Y(" + "X!X'YJWX.Y HY2Y CZW=X8ZC" + "W/Z*Z Z-Z N[ &Z(\\ MZ MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]Y 4Z2[ GY1Y 5Y NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y 6Y" + " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[ EX NX Y $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB" + "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\ ;X <\\ F\\KXK\\ F\\3\\ H\\JXK\\ 'Y " + "2ZAU -Z 'z 1Z X Na ;V@jDV :ZCZ BX UDW;XIU 'X EY2Z 1WEW KZCZ 3X9U@V\"W*X LX9VAW H[ " + "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!" + "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z ;Y Y NX Y 5Z0Y HX0X IZ1Z IY0Z KZ0" + "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ #Z JXLU" + " -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ #Z 3X@X %TAX @WZ 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[ Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\ DX BX LY NY3[ F[2Z HZ %Y1" + "[ IZ >Y 3Y2[ GY1Y 5Y NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y 6Z 9Y .Y2Z Z !Z" + " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2" + "[.Y2\\ 2Z $Z !Z !Z Y ;Y Y NX Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z =" + "YLY ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY #Z IYNU ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ " + " $[,P )W?X %TBY AXXMY DZDZ 2YLY 3[ DY X Y \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y " + "FY2Z%[<\\a@V 7YBY CX NV LV BZ3Z 1WEW LYBY 2W8U?V#W+X KX9U" + "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z Z !Z >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8" + "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z ZMZ DZMW4WMZ![7Z 3Z 7\\ BX AX MY NY3" + "[ F\\4Z FZ &Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y 5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ " + "3YMY 3[ CY X Y !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3" + "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z i i 2WZ4" + "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW :X :" + "V >r >V/V @s #Z 2[CU -Z +[MeL[ 5Z X G\\ :W!V 3W@W 7V!W AZ4[ 1WEW LW@W 1W7s,X-" + "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z Z !Z >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\," + "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z =Y Y NX Y " + "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8" + "X $YMY %[ F^ '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[ %[1S -Y 'X@X ;Q:TCZ CX:X=X" + "5[.] /Y HY HX NZ GZ 'X +[8Z 0Z 4\\ 0[ 'Z M\\ CZ6[ 9Z 2[3[ '[ 0Y Y ?f f BX DW=\\C_J[.Z&Z\"Z0\\ " + "J\\(T'Z._ JZ MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[ @X" + " AX MY NY5] F]6Z DZ &Z5] G[ AY 2[8^ GY1Y 5Y NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y 6Y 8Y -Z6\\ ;Z" + "MZ =b=b EZ@Z 3d 5[ AY X Y L[:\\ IZ;` D[4Z FXZ5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z " + "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU 9X 9T

q \"Z 1ZCU -Z ,[JaI[ 6Z X F\\ :W#V 1" + "V?V 7W#W @[5[ 1WEW LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z Z " + "!Z >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY" + "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[ >Y Y NX Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ " + "FY6] 9c 9]6Z :d )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ %Z D] $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\ " + " '\\3T -Z (W?X ;Sd c @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-" + "Z NS*\\ 6Y 6[1[ Z 4c 5[ @Y X Y HS3V FZZ%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS 8X 8R :n :R+R U 6W%W ?[6\\ 1WEW LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z" + "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z Z !Z >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\" + "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[ ?Y Y NX Y 3[7[ FX0X G[7[ E[7[ FZ7[ F" + "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d %Z C\\ ?S 2\\ETD" + "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\ '\\5U -Z (W?W :U>TE[ CX8X?X3\\3b 1Y IY GX NZ GZ (" + "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[ ;a ` =Z EX[ 4b 6[ ?Y X Y " + "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ" + "(Z :Z 8]3] FQ 7X 7P 8l 8P)P :m Z 0[EU -Z .[?P?[ 8Z X D[ 9W(W -T\\8] 1WEW " + " LSZ !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3" + "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q " + "G\\-Q G\\-Q 5Y Y NX Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b &[2[" + " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c &Z B\\ ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :] (\\7V " + "-Z )X@X :W@TF[ BW7X?X3]6e 1X IY GX NZ GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[ 8^ " + " ^ ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a" + "\"Z0[ 7Z ;\\ >X @X NY MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ " + "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[ >Y X Y F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#[" + "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\ Z #^A] :n DZ(Z :Z 7]5] +X Mj (k NZ 0\\FUBP ;Z /[,[ " + "9Z X CZ 8X+W *R;R 4X+X =]:^ 1WEW LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z" + "8Z/Z 3_2Y NZ !Z Z !Z >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #" + "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y Y NX Y 1[:[ EX0X F\\;\\ C\\;[ C[:" + "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c 'Z " + "@[ @T JT _ %] 7X.Y D^D^ BZ6Y 6b 9_ *];X -Z )X@X :ZCTH] CX7YAX1^:h 2Y JY GX NZ" + " GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[ 5[ [ 8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ MZ &`4^" + "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[ =X @X NY M[\\ @]7R" + " D\\=a E]4U 7Y /]Bc GY1Y 5Y NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[ >Y " + "X Y E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J" + "Z$Z N[ NZ \"^C^ 7g @Z(Z :Z 7_9_ +X Lh &i MZ /]HUDR ;Z .Y*Y 8Z X BZ 8Y/X (Q:Q 2X/Y " + " <^<` 2WEW LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z Z !Z >Z !Z !Z \"Z :" + "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[=" + "` E[=a 8a 8`=\\ 6` #]:] H]9] G]:] G]:] H]9] 4W !a 'Z ?Z ?U KT N] $] 7X.Y Cv AZ6Z 7a 7a " + " -_?Z -Z )W?X :^GTK_ CX5XAX0_>k 3Y JX FX NZ GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX" + "@_ ,Z \"[ 3Y X 5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^_ 8b @a2a$[.[ 8Z <~` AX ?X Y L\\@c Fb@] ?^` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>" + "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[ 0V U 2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H" + "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX ?X Y L^DZNY FYNZF_ =`CY B^EZNY CaB] 7" + "Y .qMY GY1Y 5Y NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y X Y " + " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :" + "Z 5dGd )X Jd \"e KZ -`MUKY H~U IU&U 6Z X AY 5Z7Z LZ7Z ;~d 3cFk 8WEW " + " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd" + " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]" + " DaA] DaA] DaA] 5Y Y NX Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_ 7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^" + " dLd CdMd BdLd CdLd DeMd 2X !` %X =Y ?U LV MZ !Y 5X.Y As AZ4Y 6` 5~] )x -Z " + "*X@X 9} BX3YFZ-{L] 4Y LY FX NZ GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[ -S S 0Z BW8aG[%[\"Z$~R" + " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX ?X Y KsN" + "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y X " + " Y CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~ 'X " + " Ib c JZ ,u H~U HS$S 5Z X AY 4\\>\\ I]>\\ :~d 3~Y 8WEW CW KV)W0X FX LW" + "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} " + "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y Y NX Y .v BX0X Cw =w >v >w =w 8{ ?qMX CqMX C" + "qMX CqMY 6] 6YNr 3^ My Ay @y @z Ay 1X _ $V X !" + "Y JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6" + "] 5~P 2Y X Y CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z" + " :Z 2{ &X H` Ma IZ +t H~U GQ\"Q 4Z X AY 2aLb FaKa 8~d 3YNlN_ 8WEW " + "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM" + "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y Y NX Y -t AX0X Bu ;u pLX CpLX CpLX BoLY 6] 6YMp 1] Lv >w =v =v >w 0X _ #T ;X ?W MV LW LV 4X.Y ?n >Y3Z 7_ 1~Z " + " 't +Z *W?X 8y @X1j)vG] 5X MY EX NZ GZ *X !p <} I~S Iz Z By 6r 5Z )w As (Z \"[ " + " 5Z AX HZ Z%~ 9|$~P >~S FZ ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX " + ">X !Y JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y Y NX Y ,r @X0X As 9s :r :s 9s 7z <" + "nKX BnKX BnKX BnKY 6] 6YLn 0\\ Jt ;s :t ;t ;s .X N] !R 9V >W NX LU KU 3X.Y >l =Y2Y 7_ /~X " + " %p )Z *W?W 4u @X/i(tE] 6Y NX DX NZ GZ *X m :} I~S Iy NZ Bw 2o 5Z 'u @r 'Z \"Z " + " 4Z AY J[ Z%} 6x\"} <~S FZ N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX >X" + " !Y InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2" + "[ 7\\ 5~P 2Y X Y AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z " + ":Z(Z :Z /u #X F\\ I] GZ )r H~U *Z X AY /p >o 4~d 3YMiK^ 8WEW EX " + "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv p %Z \"Z " + " 4Z @X JZ MZ&{ 3u z 9~S FZ Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z Lr BZ(Z!y -Y -s /] <^.]&[&[ m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7" + "\\ K[0Z 6Z 4~P 2Y X Y ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o" + "Do @Z :Z(Z :Z .s \"X EZ G[ FZ 'p H~U *Z X AY ,k :k 2~d 3YLgJ^ 8WEW " + " EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r " + " AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y Y NX Y *m =X0X >m 3m 5n 5m" + " 3m 6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z El 3k 2l 3l 4l *X N\\ 5U ?Y Y KR HQ 1X.Y 9b 9Y1Z 7" + "] )~S \"j &Z +X@X -h ;X+c!l?\\ 6X Y DX Z FZ +X Kh 8} I~S Fr JZ As ,i 3[ $n ;m " + "#Z \"Y 3Z ?X KZ MZ&x -p Mu 4~S FZ Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i L^ 4e " + "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n NX DX EY EZ %m G~U *Z X BZ )e 4e /~d 3YKeH] 8" + "WEW FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4" + "n 5n 6o 6n @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y Y NX Y (i ;X0X " + "fGX >fGX >fGY 4Y 4YHf +Z Bg /g .g -g /g (X M[ 5T ?Z !Z JP 'X.Y 5" + "[ 6Y0Y 7] &~P Ne $Z +W?X '] 6W)a Mh<\\ 7Y !X CX Y EZ +X Id 6} I~S Cm HZ =l 'e " + "1Z i 6h !Z #Z 3Z ?Y M[ M['s &k Jo .~S FZ Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z Gl AZ'Z Jm (Y (i )\\ " + ";].]'[#Z =Z =~a AX =X \"Y DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y" + " $k ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y X Y ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e " + "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i LX CV CW DZ #h D~U *Z X -R9Z #[ *[ *~d 3" + "YIaE\\ 8WEW GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o " + "5Y,^ Ai /h 0i 0i 0i >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y Y NX Y" + " &e 9X0X :e ,f -f -e ,f 4XFe 0cEX a NU CZ N` 9X -T<[ " + " LYG]BX 5WEW %U HW NX MX GZ (d +b (b )b )a )b 9V;a " + ")c *c *c *c =a 4_ &^ %^ $^ &_ &_ :_/c RM] !R Z 5\\ " + " 9X ;X $Y HY NY 0Y 'X NY BY X !Y " + ":Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X -p " + " IY 8WEW #V &Z MV " + " 0U 'P ;Y 2Y >Z 8X " + " MT *X &X 9X DX " + " 5X ?\\%W ?Z 4\\ :X ;X $Y " + " IZ NY 0Y 'X NY BZ !X !Y :Y 8Y 4Y *Y 1Y EX 3Y " + " CZ IU 3X -o HY 8WEW \"V " + " 'Z LU 0V " + " CZ 2Y >Y 7X " + " MT )X 'X 9X DX 5W <\\(X ?" + "Z 3\\ ;Y e GX 2f KZ LY 0Y 'X !Y >" + "\\ %X &] 9Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3" + "X $^ @Y 8WEW !V '\\:V ;V " + " 1W GZ 0Y @Z " + " FWHX LT 'X +W 7W " + " V 5b?c A[ -\\ ?e !f " + " f /X 0g 9Y 8Y 4Y *Y " + " 1Y EX 3Y CZ IU 3X 5Y " + " NV &\\=X ;V " + "1W GY /Y AZ EWHX " + " LT &W ,X 7V V 3~T " + " A] ,\\ @e !f d " + " %e -Y Nd @c " + " (m @c " + " +u $b -Y 'X 0d 2^ /X 0_ 1Y 8Y 4Y *Y " + " 1Y EX 3Y CZ IT 2X 5Y " + "-c !q Hd >c " + " $d ,Y Nd ?b " + " %g =" + "b *t #a ,Y 'X 0d " + " ,X /X 0Y +Y 8Y 4Y *Y 1Y EX 3Y CZ '" + "X 5Y -c Nm Fc " + " =c $c +Y Nc " + " >a " + " M\\ 8a \"~Y 1" + "r !` +Y 'X 0c 1X 1Y 8Y 4Y *Y 1Y EX 3Y " + " CZ &W 5Y -b Lj " + " Db std::printf(). + \note If configuration macro \c cimg_strict_warnings is set, this function throws a + \c CImgWarningException instead. + \warning As the first argument is a format string, it is highly recommended to write + \code + cimg::warn("%s",warning_message); + \endcode + instead of + \code + cimg::warn(warning_message); + \endcode + if \c warning_message can be arbitrary, to prevent nasty memory access. + **/ + inline void warn(const char *const format, ...) { + if (cimg::exception_mode()>=1) { + char *const message = new char[16384]; + std::va_list ap; + va_start(ap,format); + cimg_vsnprintf(message,16384,format,ap); + va_end(ap); +#ifdef cimg_strict_warnings + throw CImgWarningException(message); +#else + std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message); +#endif + delete[] message; + } + } + + // Execute an external system command. + /** + \param command C-string containing the command line to execute. + \param module_name Module name. + \return Status value of the executed command, whose meaning is OS-dependent. + \note This function is similar to std::system() + but it does not open an extra console windows + on Windows-based systems. + **/ + inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) { + cimg::unused(module_name); +#ifdef cimg_no_system_calls + return -1; +#else + if (is_verbose) return std::system(command); +#if cimg_OS==1 + const unsigned int l = (unsigned int)std::strlen(command); + if (l) { + char *const ncommand = new char[l + 24]; + std::memcpy(ncommand,command,l); + std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent + const int out_val = std::system(ncommand); + delete[] ncommand; + return out_val; + } else return -1; +#elif cimg_OS==2 + PROCESS_INFORMATION pi; + STARTUPINFOA si; + std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); + std::memset(&si,0,sizeof(STARTUPINFO)); + GetStartupInfoA(&si); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW; + const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi); + if (res) { + WaitForSingleObject(pi.hProcess,INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return 0; + } else { + char* lpMsgBuf; + + // Get the error message. + DWORD errorCode = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0); + cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s", + module_name==0?"(null)":module_name, + command==0?"(null)":command, + errorCode,lpMsgBuf); + return -1; + } +#else + return std::system(command); +#endif +#endif + } + + //! Return a reference to a temporary variable of type T. + template + inline T& temporary(const T&) { + static T temp; + return temp; + } + + //! Exchange values of variables \c a and \c b. + template + inline void swap(T& a, T& b) { T t = a; a = b; b = t; } + + //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { + cimg::swap(a1,b1); cimg::swap(a2,b2); + } + + //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { + cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { + cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7, T8& a8, T8& b8) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); + } + + //! Return the endianness of the current architecture. + /** + \return \c false for Little Endian or \c true for Big Endian. + **/ + inline bool endianness() { + const int x = 1; + return ((unsigned char*)&x)[0]?false:true; + } + + //! Reverse endianness of all elements in a memory buffer. + /** + \param[in,out] buffer Memory buffer whose endianness must be reversed. + \param size Number of buffer elements to reverse. + **/ + template + inline void invert_endianness(T* const buffer, const cimg_ulong size) { + if (size) switch (sizeof(T)) { + case 1 : break; + case 2 : { + for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) { + const unsigned short val = *(--ptr); + *ptr = (unsigned short)((val>>8) | ((val<<8))); + } + } break; + case 4 : { + for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) { + const unsigned int val = *(--ptr); + *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24); + } + } break; + case 8 : { + const cimg_uint64 + m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24, + m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56; + for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) { + const cimg_uint64 val = *(--ptr); + *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) | + ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56)); + } + } break; + default : { + for (T* ptr = buffer + size; ptr>buffer; ) { + unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); + for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); + } + } + } + } + inline void invert_endianness(bool* const, const cimg_ulong) {} + inline void invert_endianness(unsigned char* const, const cimg_ulong) {} + inline void invert_endianness(char* const, const cimg_ulong) {} + + //! Reverse endianness of a single variable. + /** + \param[in,out] a Variable to reverse. + \return Reference to reversed variable. + **/ + template + inline T& invert_endianness(T& a) { + invert_endianness(&a,1); + return a; + } + + // Conversion functions to get more precision when trying to store unsigned ints values as floats. + inline unsigned int float2uint(const float f) { + int tmp = 0; + std::memcpy(&tmp,&f,sizeof(float)); + if (tmp>=0) return (unsigned int)f; + unsigned int u; + // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&u,&f,sizeof(float)); + return ((u)<<2)>>2; // set sign & exponent bit to 0 + } + + inline float uint2float(const unsigned int u) { + if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287) + float f; + const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1 + // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&f,&v,sizeof(float)); + return f; + } + + //! Return the value of a system timer, with a millisecond precision. + /** + \note The timer does not necessarily starts from \c 0. + **/ + inline cimg_uint64 time() { +#if cimg_OS==1 + struct timeval st_time; + gettimeofday(&st_time,0); + return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000; +#elif cimg_OS==2 + ULARGE_INTEGER ul; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + return (cimg_uint64)ul.QuadPart/10000; +#else + return 0; +#endif + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline cimg_uint64 tictoc(const bool is_tic); + + //! Start tic/toc timer for time measurement between code instructions. + /** + \return Current value of the timer (same value as time()). + **/ + inline cimg_uint64 tic() { + return cimg::tictoc(true); + } + + //! End tic/toc timer and displays elapsed time from last call to tic(). + /** + \return Time elapsed (in ms) since last call to tic(). + **/ + inline cimg_uint64 toc() { + return cimg::tictoc(false); + } + + //! Sleep for a given numbers of milliseconds. + /** + \param milliseconds Number of milliseconds to wait for. + \note This function frees the CPU resources during the sleeping time. + It can be used to temporize your program properly, without wasting CPU time. + **/ + inline void sleep(const unsigned int milliseconds) { +#if cimg_OS==1 + struct timespec tv; + tv.tv_sec = milliseconds/1000; + tv.tv_nsec = (milliseconds%1000)*1000000; + nanosleep(&tv,0); +#elif cimg_OS==2 + Sleep(milliseconds); +#else + cimg::unused(milliseconds); +#endif + } + + inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) { + if (!*p_timer) *p_timer = cimg::time(); + const cimg_uint64 current_time = cimg::time(); + if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; } + const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time); + *p_timer = current_time + time_diff; + cimg::sleep(time_diff); + return time_diff; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \return Number of milliseconds elapsed since the last call to wait(). + \note Same as sleep() with a waiting time computed with regard to the last call + of wait(). It may be used to temporize your program properly, without wasting CPU time. + **/ + inline unsigned int wait(const unsigned int milliseconds) { + cimg::mutex(3); + static cimg_uint64 timer = cimg::time(); + cimg::mutex(3,0); + return cimg::wait(milliseconds,&timer); + } + + // Custom random number generator (allow re-entrance). + inline cimg_uint64& rng() { // Used as a shared global number for rng + static cimg_uint64 rng = 0xB16B00B5U; + return rng; + } + + inline unsigned int _rand(cimg_uint64 *const p_rng) { + *p_rng = *p_rng*1103515245 + 12345U; + return (unsigned int)*p_rng; + } + + inline unsigned int _rand() { + cimg::mutex(4); + const unsigned int res = cimg::_rand(&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline void srand(cimg_uint64 *const p_rng) { +#if cimg_OS==1 + *p_rng = cimg::time() + (cimg_uint64)getpid(); +#elif cimg_OS==2 + *p_rng = cimg::time() + (cimg_uint64)_getpid(); +#endif + } + + inline void srand() { + cimg::mutex(4); + cimg::srand(&cimg::rng()); + cimg::mutex(4,0); + } + + inline void srand(const cimg_uint64 seed) { + cimg::mutex(4); + cimg::rng() = seed; + cimg::mutex(4,0); + } + + inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) { + const double val = cimg::_rand(p_rng)/(double)~0U; + return val_min + (val_max - val_min)*val; + } + + inline double rand(const double val_min, const double val_max) { + cimg::mutex(4); + const double res = cimg::rand(val_min,val_max,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline double rand(const double val_max, cimg_uint64 *const p_rng) { + const double val = cimg::_rand(p_rng)/(double)~0U; + return val_max*val; + } + + inline double rand(const double val_max=1) { + cimg::mutex(4); + const double res = cimg::rand(val_max,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline double grand(cimg_uint64 *const p_rng) { + double x1, w; + do { + const double x2 = cimg::rand(-1,1,p_rng); + x1 = cimg::rand(-1,1,p_rng); + w = x1*x1 + x2*x2; + } while (w<=0 || w>=1.); + return x1*std::sqrt((-2*std::log(w))/w); + } + + inline double grand() { + cimg::mutex(4); + const double res = cimg::grand(&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline unsigned int prand(const double z, cimg_uint64 *const p_rng) { + if (z<=1.e-10) return 0; + if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z); + unsigned int k = 0; + const double y = std::exp(-z); + for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng); + return k - 1; + } + + inline unsigned int prand(const double z) { + cimg::mutex(4); + const unsigned int res = cimg::prand(z,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + //! Cut (i.e. clamp) value in specified interval. + template + inline T cut(const T& val, const t& val_min, const t& val_max) { + return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val; + } + + //! Bitwise-rotate value on the left. + template + inline T rol(const T& a, const unsigned int n=1) { + return n?(T)((a<>((sizeof(T)<<3) - n))):a; + } + + inline float rol(const float a, const unsigned int n=1) { + return (float)rol((int)a,n); + } + + inline double rol(const double a, const unsigned int n=1) { + return (double)rol((cimg_long)a,n); + } + + inline double rol(const long double a, const unsigned int n=1) { + return (double)rol((cimg_long)a,n); + } + +#ifdef cimg_use_half + inline half rol(const half a, const unsigned int n=1) { + return (half)rol((int)a,n); + } +#endif + + //! Bitwise-rotate value on the right. + template + inline T ror(const T& a, const unsigned int n=1) { + return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a; + } + + inline float ror(const float a, const unsigned int n=1) { + return (float)ror((int)a,n); + } + + inline double ror(const double a, const unsigned int n=1) { + return (double)ror((cimg_long)a,n); + } + + inline double ror(const long double a, const unsigned int n=1) { + return (double)ror((cimg_long)a,n); + } + +#ifdef cimg_use_half + inline half ror(const half a, const unsigned int n=1) { + return (half)ror((int)a,n); + } +#endif + + //! Return absolute value of a value. + template + inline T abs(const T& a) { + return a>=0?a:-a; + } + inline bool abs(const bool a) { + return a; + } + inline int abs(const unsigned char a) { + return (int)a; + } + inline int abs(const unsigned short a) { + return (int)a; + } + inline int abs(const unsigned int a) { + return (int)a; + } + inline int abs(const int a) { + return std::abs(a); + } + inline cimg_int64 abs(const cimg_uint64 a) { + return (cimg_int64)a; + } + inline double abs(const double a) { + return std::fabs(a); + } + inline float abs(const float a) { + return (float)std::fabs((double)a); + } + + //! Return hyperbolic arcosine of a value. + inline double acosh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::acosh(x); +#else + return std::log(x + std::sqrt(x*x - 1)); +#endif + } + + //! Return hyperbolic arcsine of a value. + inline double asinh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::asinh(x); +#else + return std::log(x + std::sqrt(x*x + 1)); +#endif + } + + //! Return hyperbolic arctangent of a value. + inline double atanh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::atanh(x); +#else + return 0.5*std::log((1. + x)/(1. - x)); +#endif + } + + //! Return the sinc of a given value. + inline double sinc(const double x) { + return x?std::sin(x)/x:1; + } + + //! Return base-2 logarithm of a value. + inline double log2(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::log2(x); +#else + const double base2 = std::log(2.); + return std::log(x)/base2; +#endif + } + + //! Return square of a value. + template + inline T sqr(const T& val) { + return val*val; + } + + // Return inverse of error function. + template + inline T erfinv(const T& val) { + const T + sgn = val<0?-1:1, + x = (1 - val)*(1 + val), + lnx = std::log(x), + tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx), + tt2 = lnx/(T)0.147; + return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2)); + } + + //! Return cubic root of a value. + template + inline double cbrt(const T& x) { +#if cimg_use_cpp11==1 + return std::cbrt(x); +#else + return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3); +#endif + } + + template + inline T pow3(const T& val) { + return val*val*val; + } + template + inline T pow4(const T& val) { + return val*val*val*val; + } + + //! Return the minimum between three values. + template + inline t min(const t& a, const t& b, const t& c) { + return std::min(std::min(a,b),c); + } + + //! Return the minimum between four values. + template + inline t min(const t& a, const t& b, const t& c, const t& d) { + return std::min(std::min(a,b),std::min(c,d)); + } + + //! Return the minabs between two values. + template + inline t minabs(const t& a, const t& b) { + return cimg::abs(b) + inline t minabs(const t& a, const t& b, const t& abs_b) { + return abs_b + inline t max(const t& a, const t& b, const t& c) { + return std::max(std::max(a,b),c); + } + + //! Return the maximum between four values. + template + inline t max(const t& a, const t& b, const t& c, const t& d) { + return std::max(std::max(a,b),std::max(c,d)); + } + + //! Return the maxabs between two values. + template + inline t maxabs(const t& a, const t& b) { + return cimg::abs(b)>cimg::abs(a)?b:a; + } + + template + inline t maxabs(const t& a, const t& b, const t& abs_b) { + return abs_b>cimg::abs(a)?b:a; + } + + //! Return the sign of a value. + template + inline T sign(const T& x) { + return (T)(cimg::type::is_nan(x)?0:x<0?-1:x>0); + } + + //! Return the nearest power of 2 higher than given value. + template + inline cimg_uint64 nearest_pow2(const T& x) { + cimg_uint64 i = 1; + while (x>i) i<<=1; + return i; + } + + //! Return the modulo of a value. + /** + \param x Input value. + \param m Modulo value. + \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type. + **/ + template + inline T mod(const T& x, const T& m) { + const double dx = (double)x, dm = (double)m; + if (!cimg::type::is_finite(dm)) return x; + if (cimg::type::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm)); + return (T)0; + } + inline int mod(const bool x, const bool m) { + return m?(x?1:0):0; + } + inline int mod(const unsigned char x, const unsigned char m) { + return x%m; + } + inline int mod(const char x, const char m) { +#if defined(CHAR_MAX) && CHAR_MAX==255 + return x%m; +#else + return x>=0?x%m:(x%m?m + x%m:0); +#endif + } + inline int mod(const unsigned short x, const unsigned short m) { + return (int)(x%m); + } + inline int mod(const short x, const short m) { + return (int)(x>=0?x%m:(x%m?m + x%m:0)); + } + inline int mod(const unsigned int x, const unsigned int m) { + return (int)(x%m); + } + inline int mod(const int x, const int m) { + return (int)(x>=0?x%m:(x%m?m + x%m:0)); + } + inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) { + return (cimg_int64)(x%m); + } + inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) { + return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0)); + } + + //! Return the min-mod of two values. + /** + \note minmod(\p a,\p b) is defined to be: + - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. + - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. + **/ + template + inline T minmod(const T& a, const T& b) { + return a*b<=0?0:(a>0?(a + inline T round(const T& x) { + return (T)std::floor((_cimg_Tfloat)x + 0.5f); + } + + template + inline int uiround(const T x) { + return cimg::type::is_float()?(int)(x + 0.5f):(int)x; + } + + //! Return rounded value. + /** + \param x Value to be rounded. + \param y Rounding precision. + \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward). + \return Rounded value, having the same type as input value \c x. + **/ + template + inline T round(const T& x, const double y, const int rounding_type=0) { + if (y<=0) return x; + if (y==1) switch (rounding_type) { + case 0 : return cimg::round(x); + case 1 : return (T)std::ceil((_cimg_Tfloat)x); + default : return (T)std::floor((_cimg_Tfloat)x); + } + const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor; + return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx))); + } + + // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values. + // (contribution by RawTherapee: http://rawtherapee.com/). + template + inline T median(T val0, T val1) { + return (val0 + val1)/2; + } + + template + inline T median(T val0, T val1, T val2) { + return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1))); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4) { + T tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); + val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2); + val1 = tmp; tmp = std::min(val2,val3); + return std::max(val1,tmp); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) { + T tmp = std::min(val0,val5); + val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp; + tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4); + val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5); + val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); + val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp); + tmp = std::min(val1,tmp); val3 = std::max(tmp,val3); + return std::min(val3,val4); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) { + T tmp = std::min(val1,val2); + val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); + val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); + val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); + val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7); + val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2); + val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); + val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); + val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8); + val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6); + val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7); + tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp); + return std::min(val4,val2); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8, T val9, T val10, T val11, + T val12) { + T tmp = std::min(val1,val7); + val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; + tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8); + val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12); + val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp; + tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11); + val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp; + tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2); + val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; + tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4); + val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; + tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12); + tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10); + val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; + tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9); + val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp; + tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3); + tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10); + val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8); + val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp); + val5 = std::max(tmp,val5); val6 = std::min(val6,val7); + return std::max(val5,val6); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, + T val5, T val6, T val7, T val8, T val9, + T val10, T val11, T val12, T val13, T val14, + T val15, T val16, T val17, T val18, T val19, + T val20, T val21, T val22, T val23, T val24) { + T tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); + val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3); + val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; + tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6); + tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10); + val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9); + tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13); + val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12); + tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16); + val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15); + tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19); + val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18); + tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22); + val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21); + tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5); + val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp; + tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3); + tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7); + val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14); + val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14); + val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15); + val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15); + val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16); + val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16); + val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23); + val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23); + val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24); + val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24); + val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22); + val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18); + val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18); + val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp; + tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10); + val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp; + tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11); + tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21); + val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12); + tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22); + val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23); + val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23); + val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24); + val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15); + val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19); + tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp); + val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11); + val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17); + tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12); + val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14); + val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7); + tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14); + tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12); + val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp); + val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp; + val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp; + tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp); + val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp; + tmp = std::min(val10,val20); + return std::max(tmp,val12); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, + T val7, T val8, T val9, T val10, T val11, T val12, T val13, + T val14, T val15, T val16, T val17, T val18, T val19, T val20, + T val21, T val22, T val23, T val24, T val25, T val26, T val27, + T val28, T val29, T val30, T val31, T val32, T val33, T val34, + T val35, T val36, T val37, T val38, T val39, T val40, T val41, + T val42, T val43, T val44, T val45, T val46, T val47, T val48) { + T tmp = std::min(val0,val32); + val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp; + tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35); + val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp; + tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38); + val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp; + tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41); + val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42); + val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp; + tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45); + val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46); + val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp; + tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16); + val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17); + val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19); + val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp; + tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22); + val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp; + tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25); + val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26); + val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp; + tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29); + val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30); + val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp; + tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32); + val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33); + val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp; + tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36); + val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37); + val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp; + tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40); + val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41); + val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp; + tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44); + val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45); + val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp; + tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8); + val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp; + tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11); + val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp; + tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14); + val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp; + tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25); + val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26); + val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp; + tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29); + val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30); + val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp; + tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41); + val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42); + val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp; + tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45); + val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46); + val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp; + tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33); + val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34); + val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp; + tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37); + val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38); + val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp; + tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16); + val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17); + val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp; + tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20); + val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21); + val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp; + tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32); + val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33); + val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp; + tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36); + val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37); + val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp; + tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48); + val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4); + val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6); + val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; + tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13); + val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14); + val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp; + tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21); + val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22); + val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp; + tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29); + val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30); + val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp; + tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37); + val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38); + val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp; + tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45); + val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46); + val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp; + tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33); + val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34); + val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp; + tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41); + val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42); + val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp; + tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16); + val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17); + val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp; + tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24); + val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25); + val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp; + tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32); + val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33); + val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp; + tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40); + val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41); + val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp; + tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48); + val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8); + val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10); + val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp; + tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17); + val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18); + val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp; + tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25); + val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26); + val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp; + tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33); + val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34); + val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp; + tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41); + val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42); + val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp; + tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2); + val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp; + tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7); + val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp; + tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14); + val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15); + val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp; + tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22); + val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23); + val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp; + tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30); + val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31); + val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp; + tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38); + val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39); + val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp; + tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46); + val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47); + val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33); + val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp; + tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40); + val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41); + val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp; + tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48); + val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16); + val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp; + tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21); + val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24); + val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp; + tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29); + val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32); + val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp; + tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37); + val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40); + val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp; + tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45); + val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48); + val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9); + val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; + tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16); + val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17); + val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp; + tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24); + val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25); + val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp; + tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32); + val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33); + val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp; + tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40); + val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41); + val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp; + tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48); + val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); + val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8); + val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp; + tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13); + val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16); + val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp; + tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21); + val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24); + val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp; + tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29); + val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32); + val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp; + tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37); + val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40); + val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp; + tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45); + val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48); + val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5); + val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11); + val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17); + val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23); + val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29); + val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35); + val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41); + val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47); + val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36); + val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42); + val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48); + val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28); + val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34); + val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24); + val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30); + val24 = std::max(val21,val24); val23 = std::min(val23,val26); + return std::max(val23,val24); + } + + //! Return sqrt(x^2 + y^2). + template + inline T hypot(const T x, const T y) { + return std::sqrt(x*x + y*y); + } + + template + inline T hypot(const T x, const T y, const T z) { + return std::sqrt(x*x + y*y + z*z); + } + + template + inline T _hypot(const T x, const T y) { // Slower but more precise version + T nx = cimg::abs(x), ny = cimg::abs(y), t; + if (nx0) { t/=nx; return nx*std::sqrt(1 + t*t); } + return 0; + } + + //! Return the factorial of n + inline double factorial(const int n) { + if (n<0) return cimg::type::nan(); + if (n<2) return 1; + double res = 2; + for (int i = 3; i<=n; ++i) res*=i; + return res; + } + + //! Return the number of permutations of k objects in a set of n objects. + inline double permutations(const int k, const int n, const bool with_order) { + if (n<0 || k<0) return cimg::type::nan(); + if (k>n) return 0; + double res = 1; + for (int i = n; i>=n - k + 1; --i) res*=i; + return with_order?res:res/cimg::factorial(k); + } + + inline double _fibonacci(int exp) { + double + base = (1 + std::sqrt(5.))/2, + result = 1/std::sqrt(5.); + while (exp) { + if (exp&1) result*=base; + exp>>=1; + base*=base; + } + return result; + } + + //! Calculate fibonacci number. + // (Precise up to n = 78, less precise for n>78). + inline double fibonacci(const int n) { + if (n<0) return cimg::type::nan(); + if (n<3) return 1; + if (n<11) { + cimg_uint64 fn1 = 1, fn2 = 1, fn = 0; + for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } + return (double)fn; + } + if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10 + return (double)((cimg_uint64)(_fibonacci(n) + 0.5)); + + if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93 + cimg_uint64 + fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL) + fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL + fn = 0; + for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } + return (double)fn; + } + return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation + } + + //! Calculate greatest common divisor. + inline long gcd(long a, long b) { + while (a) { const long c = a; a = b%a; b = c; } + return b; + } + + //! Convert character to lower case. + inline char lowercase(const char x) { + return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a'); + } + inline double lowercase(const double x) { + return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a'); + } + + //! Convert C-string to lower case. + inline void lowercase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr); + } + + //! Convert character to upper case. + inline char uppercase(const char x) { + return (char)((x<'a'||x>'z')?x:x - 'a' + 'A'); + } + + inline double uppercase(const double x) { + return (double)((x<'a'||x>'z')?x:x - 'a' + 'A'); + } + + //! Convert C-string to upper case. + inline void uppercase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr); + } + + //! Return \c true if input character is blank (space, tab, or non-printable character). + inline bool is_blank(const char c) { + return c>=0 && (unsigned char)c<=' '; + } + + //! Read value in a C-string. + /** + \param str C-string containing the float value to read. + \return Read value. + \note Same as std::atof() extended to manage the retrieval of fractions from C-strings, + as in "1/2". + **/ + inline double atof(const char *const str) { + double x = 0, y = 1; + return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; + } + + //! Compare the first \p l characters of two C-strings, ignoring the case. + /** + \param str1 C-string. + \param str2 C-string. + \param l Number of characters to compare. + \return \c 0 if the two strings are equal, something else otherwise. + \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). + **/ + inline int strncasecmp(const char *const str1, const char *const str2, const int l) { + if (!l) return 0; + if (!str1) return str2?-1:0; + const char *nstr1 = str1, *nstr2 = str2; + int k, diff = 0; for (k = 0; kp && str[q]==delimiter; ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } + return false; + } + + //! Remove white spaces on the start and/or end of a C-string. + inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) { + if (!str) return false; + const int l = (int)std::strlen(str); + int p, q; + if (is_symmetric) for (p = 0, q = l - 1; pp && is_blank(str[q]); ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } + return false; + } + + //! Replace reserved characters (for Windows filename) by another character. + /** + \param[in,out] str C-string to work with (modified at output). + \param[in] c Replacement character. + **/ + inline void strwindows_reserved(char *const str, const char c='_') { + for (char *s = str; *s; ++s) { + const char i = *s; + if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c; + } + } + + //! Replace escape sequences in C-strings by character values. + /** + \param[in,out] str C-string to work with (modified at output). + **/ + inline void strunescape(char *const str) { +#define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break; + + unsigned char val = 0; + for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) { + cimg_strunescape('a','\a'); + cimg_strunescape('b','\b'); + cimg_strunescape('e',0x1B); + cimg_strunescape('f','\f'); + cimg_strunescape('n','\n'); + cimg_strunescape('r','\r'); + cimg_strunescape('t','\t'); + cimg_strunescape('v','\v'); + cimg_strunescape('\\','\\'); + cimg_strunescape('\'','\''); + cimg_strunescape('\"','\"'); + cimg_strunescape('\?','\?'); + case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : + val = (unsigned char)(*(ns++) - '0'); + if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; + if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; + *nd = (char)val; + break; + case 'x' : { + char c = lowercase(*(++ns)); + if ((c>='0' && c<='9') || (c>='a' && c<='f')) { + val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10); + c = lowercase(*(++ns)); + if ((c>='0' && c<='9') || (c>='a' && c<='f')) { + (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10); + ++ns; + } + *nd = (char)val; + } else *nd = c; + } break; + case 'u' : { // UTF-8 BMP + char c1, c2, c3, c4; + if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) && + (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) && + (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) && + (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) { + c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10); + c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10); + c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10); + c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10); + const unsigned int ival = + ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4; + if (ival<=0x007f) *nd = (char)ival; + else if (ival<=0x07ff) { + *(nd++) = (char)((ival>>6)|0xc0); + *nd = (char)((ival&0x3f)|0x80); + } else { + *(nd++) = (char)((ival>>12)|0xe0); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } + ns+=5; + } else *nd = *(ns++); + } break; + case 'U' : { // UTF-8 astral planes + char c1, c2, c3, c4, c5, c6, c7, c8; + if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) && + (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) && + (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) && + (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) && + (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) && + (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) && + (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) && + (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) { + c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10); + c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10); + c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10); + c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10); + c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10); + c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10); + c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10); + c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10); + const unsigned int ival = + ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) | + ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8; + if (ival<=0x007f) *nd = (char)ival; + else if (ival<=0x07ff) { + *(nd++) = (char)((ival>>6)|0xc0); + *nd = (char)((ival&0x3f)|0x80); + } else if (ival<=0xffff) { + *(nd++) = (char)((ival>>12)|0xe0); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } else { + *(nd++) = (char)((ival>>18)|0xf0); + *(nd++) = (char)(((ival>>12)&0x3f)|0x80); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } + ns+=9; + } else *nd = *(ns++); + } break; + default : if (*ns) *nd = *(ns++); + } + else *nd = *(ns++); + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const cimg_ulong size); + + // Return string that identifies the running OS. + inline const char *stros() { +#if defined(linux) || defined(__linux) || defined(__linux__) + static const char *const str = "Linux"; +#elif defined(sun) || defined(__sun) + static const char *const str = "Sun OS"; +#elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) + static const char *const str = "BSD"; +#elif defined(sgi) || defined(__sgi) + static const char *const str = "Irix"; +#elif defined(__MACOSX__) || defined(__APPLE__) + static const char *const str = "Mac OS"; +#elif defined(unix) || defined(__unix) || defined(__unix__) + static const char *const str = "Generic Unix"; +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ + defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + static const char *const str = "Windows"; +#else + const char + *const _str1 = std::getenv("OSTYPE"), + *const _str2 = _str1?_str1:std::getenv("OS"), + *const str = _str2?_str2:"Unknown OS"; +#endif + return str; + } + + //! Return the basename of a filename. + inline const char* basename(const char *const s, const char separator=cimg_file_separator) { + const char *p = 0, *np = s; + while (np>=s && (p=np)) np = std::strchr(np,separator) + 1; + return p; + } + + // Return a random filename. + inline const char* filenamerand() { + cimg::mutex(6); + static char randomid[9]; + for (unsigned int k = 0; k<8; ++k) { + const int v = (int)cimg::rand(65535)%3; + randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)): + (v==1?('a' + ((int)cimg::rand(65535)%26)): + ('A' + ((int)cimg::rand(65535)%26)))); + } + cimg::mutex(6,0); + return randomid; + } + + // Convert filename as a Windows-style filename (short path name). + inline void winformat_string(char *const str) { + if (str && *str) { +#if cimg_OS==2 + char *const nstr = new char[MAX_PATH]; + if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr); + delete[] nstr; +#endif + } + } + + // Open a file (similar to std:: fopen(), but with wide character support on Windows). + inline std::FILE *std_fopen(const char *const path, const char *const mode); + + + //! Open a file. + /** + \param path Path of the filename to open. + \param mode C-string describing the opening mode. + \return Opened file. + \note Same as std::fopen() but throw a \c CImgIOException when + the specified file cannot be opened, instead of returning \c 0. + **/ + inline std::FILE *fopen(const char *const path, const char *const mode) { + if (!path) + throw CImgArgumentException("cimg::fopen(): Specified file path is (null)."); + if (!mode) + throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).", + path); + std::FILE *res = 0; + if (*path=='-' && (!path[1] || path[1]=='.')) { + res = (*mode=='r')?cimg::_stdin():cimg::_stdout(); +#if cimg_OS==2 + if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode +#ifdef __BORLANDC__ + if (setmode(_fileno(res),0x8000)==-1) res = 0; +#else + if (_setmode(_fileno(res),0x8000)==-1) res = 0; +#endif + } +#endif + } else res = cimg::std_fopen(path,mode); + if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.", + path,mode); + return res; + } + + //! Close a file. + /** + \param file File to close. + \return \c 0 if file has been closed properly, something else otherwise. + \note Same as std::fclose() but display a warning message if + the file has not been closed properly. + **/ + inline int fclose(std::FILE *file) { + if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; } + if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0; + const int errn = std::fclose(file); + if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.", + errn); + return errn; + } + + //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows). + inline int fseek(FILE *stream, cimg_long offset, int origin) { +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + return _fseeki64(stream,(__int64)offset,origin); +#else + return std::fseek(stream,offset,origin); +#endif + } + + //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows). + inline cimg_long ftell(FILE *stream) { +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + return (cimg_long)_ftelli64(stream); +#else + return (cimg_long)std::ftell(stream); +#endif + } + + // Get the file or directory attributes with support for UTF-8 paths (Windows only). +#if cimg_OS==2 + inline DWORD win_getfileattributes(const char *const path); +#endif + + //! Check if a path is a directory. + /** + \param path Specified path to test. + **/ + inline bool is_directory(const char *const path) { + if (!path || !*path) return false; +#if cimg_OS==1 + struct stat st_buf; + return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)); +#elif cimg_OS==2 + const DWORD res = win_getfileattributes(path); + return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY); +#else + return false; +#endif + } + + //! Check if a path is a file. + /** + \param path Specified path to test. + **/ + inline bool is_file(const char *const path) { + if (!path || !*path) return false; +#if cimg_OS==2 + const DWORD res = cimg::win_getfileattributes(path); + return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY); +#else + std::FILE *const file = cimg::std_fopen(path,"rb"); + if (!file) return false; + cimg::fclose(file); + return !is_directory(path); +#endif + } + + //! Get file size. + /** + \param filename Specified filename to get size from. + \return File size or '-1' if file does not exist. + **/ + inline cimg_int64 fsize(const char *const filename) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + if (!file) return (cimg_int64)-1; + std::fseek(file,0,SEEK_END); + const cimg_int64 siz = (cimg_int64)std::ftell(file); + cimg::fclose(file); + return siz; + } + + //! Get last write time of a given file or directory (multiple-attributes version). + /** + \param path Specified path to get attributes from. + \param[in,out] attr Type of requested time attributes. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } + Replaced by read attributes after return (or -1 if an error occurred). + \param nb_attr Number of attributes to read/write. + \return Latest read attribute. + **/ + template + inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) { +#define _cimg_fdate_err() for (unsigned int i = 0; i + inline int date(T *attr, const unsigned int nb_attr) { + int res = -1; + cimg::mutex(6); +#if cimg_OS==2 + SYSTEMTIME st; + GetLocalTime(&st); + for (unsigned int i = 0; itm_year + 1900: + attr[i]==1?st->tm_mon + 1: + attr[i]==2?st->tm_mday: + attr[i]==3?st->tm_wday: + attr[i]==4?st->tm_hour: + attr[i]==5?st->tm_min: + attr[i]==6?st->tm_sec: + attr[i]==7?_st.tv_usec/1000:-1); + attr[i] = (T)res; + } +#endif + cimg::mutex(6,0); + return res; + } + + //! Get current local time (single-attribute version). + /** + \param attr Type of requested time attribute. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second | + 7=millisecond } + \return Specified attribute or -1 if an error occurred. + **/ + inline int date(unsigned int attr) { + int out = (int)attr; + return date(&out,1); + } + + // Get/set path to the \c curl binary. + inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c dcraw binary. + inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the custom's \c custom binary. + inline const char *custom_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the GraphicsMagick's \c gm binary. + inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c gunzip binary. + inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c gzip binary. + inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the ImageMagick's \c convert binary. + inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the Medcon's \c medcon binary. + inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to store temporary files. + inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c wget binary. + inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false); + + //! Split filename into two C-strings \c body and \c extension. + /** + filename and body must not overlap! + **/ + inline const char *split_filename(const char *const filename, char *const body=0) { + if (!filename) { if (body) *body = 0; return ""; } + const char * p = std::strrchr(filename,'.'); + if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension. + if (body) std::strcpy(body,filename); + return filename + std::strlen(filename); + } + const unsigned int l = (unsigned int)(p - filename); + if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; } + return p + 1; + } + + // Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, + const unsigned int digits, char *const str); + + //! Read data from file. + /** + \param[out] ptr Pointer to memory buffer that will contain the binary data read from file. + \param nmemb Number of elements to read. + \param stream File to read data from. + \return Number of read elements. + \note Same as std::fread() but may display warning message if all elements could not be read. + **/ + template + inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",stream,ptr); + if (!nmemb) return 0; + const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0; + do { + l_to_read = (to_read*sizeof(T))0); + if (to_read>0) + warn("cimg::fread(): Only %lu/%lu elements could be read from file.", + (unsigned long)al_read,(unsigned long)nmemb); + return al_read; + } + + //! Write data to file. + /** + \param ptr Pointer to memory buffer containing the binary data to write on file. + \param nmemb Number of elements to write. + \param[out] stream File to write data on. + \return Number of written elements. + \note Similar to std::fwrite but may display warning messages if all elements could not be written. + **/ + template + inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",ptr,stream); + if (!nmemb) return 0; + const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0; + do { + l_to_write = (to_write*sizeof(T))0); + if (to_write>0) + warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.", + (unsigned long)al_write,(unsigned long)nmemb); + return al_write; + } + + //! Create an empty file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + **/ + inline void fempty(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::fempty(): Specified filename is (null)."); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!file) cimg::fclose(nfile); + } + + // Try to guess format from an image file. + inline const char *ftype(std::FILE *const file, const char *const filename); + + // Get or set load from network mode (can be { 0=disabled | 1=enabled }). + inline bool& network_mode(const bool value, const bool is_set) { + static bool mode = true; + if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); } + return mode; + } + + inline bool& network_mode() { + return network_mode(false,false); + } + + // Load file from network as a local temporary file. + inline char *load_network(const char *const url, char *const filename_local, + const unsigned int timeout=0, const bool try_fallback=false, + const char *const referer=0); + + //! Return options specified on the command line. + inline const char* option(const char *const name, const int argc, const char *const *const argv, + const char *const _default, const char *const usage, const bool reset_static) { + static bool first = true, visu = false; + if (reset_static) { first = true; return 0; } + const char *res = 0; + if (first) { + first = false; + visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0; + } + if (!name && visu) { + if (usage) { + std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); + std::fprintf(cimg::output(),": %s",usage); + std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time); + } + if (_default) std::fprintf(cimg::output(),"%s\n",_default); + } + if (name) { + if (argc>0) { + int k = 0; + while (k Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n", + cimg::t_bold, + cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"), + cimg::t_normal,cimg::t_green, + cimg_OS, + cimg::t_normal); + + std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n", + cimg::t_bold, + cimg::endianness()?"Big":"Little", + cimg::t_normal); + + std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n", + cimg::t_bold, + cimg_verbosity==0?"Quiet": + cimg_verbosity==1?"Console": + cimg_verbosity==2?"Dialog": + cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings", + cimg::t_normal,cimg::t_green, + cimg_verbosity, + cimg::t_normal); + + std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n", + cimg::t_bold, +#ifdef cimg_strict_warnings + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n", + cimg::t_bold, + cimg_use_cpp11?"Yes":"No", + cimg::t_normal,cimg::t_green, + (int)cimg_use_cpp11, + cimg::t_normal); + + std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_vt100 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n", + cimg::t_bold, + cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown", + cimg::t_normal,cimg::t_green, + (int)cimg_display, + cimg::t_normal); + +#if cimg_display==1 + std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xshm + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xrandr + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); +#endif + std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n", + cimg::t_bold, +#if cimg_use_openmp!=0 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_png + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_jpeg + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_tiff + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_magick + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_fftw3 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_lapack + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + char *const tmp = new char[1024]; + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path()); + std::fprintf(cimg::output()," > Path of 'curl': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path()); + std::fprintf(cimg::output()," > Path of 'dcraw': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::custom_path()); + std::fprintf(cimg::output()," > Path of 'custom': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path()); + std::fprintf(cimg::output()," > Path of 'graphicsmagick': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path()); + std::fprintf(cimg::output()," > Path of 'gunzip': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path()); + std::fprintf(cimg::output()," > Path of 'gzip': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path()); + std::fprintf(cimg::output()," > Path of 'imagemagick': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path()); + std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path()); + std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path()); + std::fprintf(cimg::output()," > Path of 'wget': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + std::fprintf(cimg::output(),"\n"); + delete[] tmp; + } + + // Declare LAPACK function signatures if LAPACK support is enabled. +#ifdef cimg_use_lapack + template + inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { + dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { + sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + template + inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { + dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { + sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + template + inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, + T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { + dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, + float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { + sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + template + inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { + int one = 1; + dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { + int one = 1; + sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + template + inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { + dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { + ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + template + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA, + T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) { + dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA, + float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) { + sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + +#endif + + } // namespace cimg { ... + + /*------------------------------------------------ + # + # + # Definition of mathematical operators and + # external functions. + # + # + -------------------------------------------------*/ + +#define _cimg_create_operator(typ) \ + template \ + inline CImg::type> operator+(const typ val, const CImg& img) { \ + return img + val; \ + } \ + template \ + inline CImg::type> operator-(const typ val, const CImg& img) { \ + typedef typename cimg::superset::type Tt; \ + return CImg(img._width,img._height,img._depth,img._spectrum,val)-=img; \ + } \ + template \ + inline CImg::type> operator*(const typ val, const CImg& img) { \ + return img*val; \ + } \ + template \ + inline CImg::type> operator/(const typ val, const CImg& img) { \ + return val*img.get_invert(); \ + } \ + template \ + inline CImg::type> operator&(const typ val, const CImg& img) { \ + return img & val; \ + } \ + template \ + inline CImg::type> operator|(const typ val, const CImg& img) { \ + return img | val; \ + } \ + template \ + inline CImg::type> operator^(const typ val, const CImg& img) { \ + return img ^ val; \ + } \ + template \ + inline bool operator==(const typ val, const CImg& img) { \ + return img == val; \ + } \ + template \ + inline bool operator!=(const typ val, const CImg& img) { \ + return img != val; \ + } + + _cimg_create_operator(bool) + _cimg_create_operator(unsigned char) + _cimg_create_operator(char) + _cimg_create_operator(signed char) + _cimg_create_operator(unsigned short) + _cimg_create_operator(short) + _cimg_create_operator(unsigned int) + _cimg_create_operator(int) + _cimg_create_operator(cimg_uint64) + _cimg_create_operator(cimg_int64) + _cimg_create_operator(float) + _cimg_create_operator(double) + _cimg_create_operator(long double) + + template + inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg& img) { + return img + expression; + } + + template + inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg& img) { + return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img; + } + + template + inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg& img) { + return img*expression; + } + + template + inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg& img) { + return expression*img.get_invert(); + } + + template + inline CImg operator&(const char *const expression, const CImg& img) { + return img & expression; + } + + template + inline CImg operator|(const char *const expression, const CImg& img) { + return img | expression; + } + + template + inline CImg operator^(const char *const expression, const CImg& img) { + return img ^ expression; + } + + template + inline bool operator==(const char *const expression, const CImg& img) { + return img==expression; + } + + template + inline bool operator!=(const char *const expression, const CImg& img) { + return img!=expression; + } + + template + inline CImg transpose(const CImg& instance) { + return instance.get_transpose(); + } + + template + inline CImg<_cimg_Tfloat> invert(const CImg& instance, const bool use_LU=true) { + return instance.get_invert(use_LU); + } + + template + inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance, const bool use_LU=false) { + return instance.get_pseudoinvert(use_LU); + } + +#define _cimg_create_pointwise_function(name) \ + template \ + inline CImg<_cimg_Tfloat> name(const CImg& instance) { \ + return instance.get_##name(); \ + } + + _cimg_create_pointwise_function(sqr) + _cimg_create_pointwise_function(sqrt) + _cimg_create_pointwise_function(exp) + _cimg_create_pointwise_function(log) + _cimg_create_pointwise_function(log2) + _cimg_create_pointwise_function(log10) + _cimg_create_pointwise_function(abs) + _cimg_create_pointwise_function(sign) + _cimg_create_pointwise_function(cos) + _cimg_create_pointwise_function(sin) + _cimg_create_pointwise_function(sinc) + _cimg_create_pointwise_function(tan) + _cimg_create_pointwise_function(acos) + _cimg_create_pointwise_function(asin) + _cimg_create_pointwise_function(atan) + _cimg_create_pointwise_function(cosh) + _cimg_create_pointwise_function(sinh) + _cimg_create_pointwise_function(tanh) + _cimg_create_pointwise_function(acosh) + _cimg_create_pointwise_function(asinh) + _cimg_create_pointwise_function(atanh) + + /*----------------------------------- + # + # Define the CImgDisplay structure + # + ----------------------------------*/ + //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events). + /** + CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window + (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems). + If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter + a minimal mode where warning messages will be outputted each time the program is trying to call one of the + CImgDisplay method. + + The configuration variable \c cimg_display tells about the graphic library used. + It is set automatically by \CImg when one of these graphic libraries has been detected. + But, you can override its value if necessary. Valid choices are: + - 0: Disable display capabilities. + - 1: Use \b X-Window (X11) library. + - 2: Use \b GDI32 library. + + Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay. + **/ + struct CImgDisplay { + cimg_uint64 _timer, _fps_frames, _fps_timer; + unsigned int _width, _height, _normalization; + float _fps_fps, _min, _max; + bool _is_fullscreen; + char *_title; + unsigned int _window_width, _window_height, _button, *_keys, *_released_keys; + int _window_x, _window_y, _mouse_x, _mouse_y, _wheel; + bool _is_closed, _is_resized, _is_moved, _is_event, + _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7, + _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2, + _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0, + _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE, + _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE, + _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG, + _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX, + _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT, + _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT, + _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3, + _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB, + _is_keyPADMUL, _is_keyPADDIV; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- + +#ifdef cimgdisplay_plugin +#include cimgdisplay_plugin +#endif +#ifdef cimgdisplay_plugin1 +#include cimgdisplay_plugin1 +#endif +#ifdef cimgdisplay_plugin2 +#include cimgdisplay_plugin2 +#endif +#ifdef cimgdisplay_plugin3 +#include cimgdisplay_plugin3 +#endif +#ifdef cimgdisplay_plugin4 +#include cimgdisplay_plugin4 +#endif +#ifdef cimgdisplay_plugin5 +#include cimgdisplay_plugin5 +#endif +#ifdef cimgdisplay_plugin6 +#include cimgdisplay_plugin6 +#endif +#ifdef cimgdisplay_plugin7 +#include cimgdisplay_plugin7 +#endif +#ifdef cimgdisplay_plugin8 +#include cimgdisplay_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + \note If the associated window is visible on the screen, it is closed by the call to the destructor. + **/ + ~CImgDisplay() { + assign(); + delete[] _keys; + delete[] _released_keys; + } + + //! Construct an empty display. + /** + \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until + display of valid data is performed. + \par Example + \code + CImgDisplay disp; // Does actually nothing + ... + disp.display(img); // Construct new window and display image in it + \endcode + **/ + CImgDisplay(): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(); + } + + //! Construct a display with specified dimensions. + /** \param width Window width. + \param height Window height. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note A black background is initially displayed on the associated window. + **/ + CImgDisplay(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(width,height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image. + /** \param img Image used as a model to create the window. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note The pixels of the input image are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(img,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list. + /** \param list The images list to display. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note All images of the list, appended along the X-axis, are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(list,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of an existing one. + /** + \param disp Display instance to copy. + \note The pixel buffer of the input window is initially displayed on the associated window. + **/ + CImgDisplay(const CImgDisplay& disp): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(disp); + } + + //! Take a screenshot. + /** + \param[out] img Output screenshot. Can be empty on input + **/ + template + static void screenshot(CImg& img) { + return screenshot(0,0,cimg::type::max(),cimg::type::max(),img); + } + +#if cimg_display==0 + + static void _no_display_exception() { + throw CImgDisplayException("CImgDisplay(): No display available."); + } + + //! Destructor - Empty constructor \inplace. + /** + \note Replace the current instance by an empty display. + **/ + CImgDisplay& assign() { + return flush(); + } + + //! Construct a display with specified dimensions \inplace. + /** + **/ + CImgDisplay& assign(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + cimg::unused(width,height,title,normalization,is_fullscreen,is_closed); + _no_display_exception(); + return assign(); + } + + //! Construct a display from an image \inplace. + /** + **/ + template + CImgDisplay& assign(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list \inplace. + /** + **/ + template + CImgDisplay& assign(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of another one \inplace. + /** + **/ + CImgDisplay& assign(const CImgDisplay &disp) { + _no_display_exception(); + return assign(disp._width,disp._height); + } + +#endif + + //! Return a reference to an empty display. + /** + \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&) + must have a default value. + \par Example + \code + void foo(CImgDisplay& disp=CImgDisplay::empty()); + \endcode + **/ + static CImgDisplay& empty() { + static CImgDisplay _empty; + return _empty.assign(); + } + + //! Return a reference to an empty display \const. + static const CImgDisplay& const_empty() { + static const CImgDisplay _empty; + return _empty; + } + +#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \ + CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true) + static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz, + const int dmin, const int dmax, const bool return_y) { + const int + u = CImgDisplay::screen_width(), + v = CImgDisplay::screen_height(); + const float + mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin, + mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin, + Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax, + Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax; + float + w = (float)std::max(1U,dx), + h = (float)std::max(1U,dy); + if (dz>1) { w+=dz; h+=dz; } + if (wMw) { h = h*Mw/w; w = Mw; } + if (h>Mh) { w = w*Mh/h; h = Mh; } + if (wdisp = img is equivalent to disp.display(img). + **/ + template + CImgDisplay& operator=(const CImg& img) { + return display(img); + } + + //! Display list of images on associated window. + /** + \note disp = list is equivalent to disp.display(list). + **/ + template + CImgDisplay& operator=(const CImgList& list) { + return display(list); + } + + //! Construct a display as a copy of another one \inplace. + /** + \note Equivalent to assign(const CImgDisplay&). + **/ + CImgDisplay& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return \c false if display is empty, \c true otherwise. + /** + \note if (disp) { ... } is equivalent to if (!disp.is_empty()) { ... }. + **/ + operator bool() const { + return !is_empty(); + } + + //@} + //------------------------------------------ + // + //! \name Instance Checking + //@{ + //------------------------------------------ + + //! Return \c true if display is empty, \c false otherwise. + /** + **/ + bool is_empty() const { + return !(_width && _height); + } + + //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise. + /** + \note + - When a user physically closes the associated window, the display is set to closed. + - A closed display is not destroyed. Its associated window can be show again on the screen using show(). + **/ + bool is_closed() const { + return _is_closed; + } + + //! Return \c true if associated window has been resized on the screen, \c false otherwise. + /** + **/ + bool is_resized() const { + return _is_resized; + } + + //! Return \c true if associated window has been moved on the screen, \c false otherwise. + /** + **/ + bool is_moved() const { + return _is_moved; + } + + //! Return \c true if any event has occurred on the associated window, \c false otherwise. + /** + **/ + bool is_event() const { + return _is_event; + } + + //! Return \c true if current display is in fullscreen mode, \c false otherwise. + /** + **/ + bool is_fullscreen() const { + return _is_fullscreen; + } + + //! Return \c true if any key is being pressed on the associated window, \c false otherwise. + /** + \note The methods below do the same only for specific keys. + **/ + bool is_key() const { + return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 || + _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 || + _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 || + _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 || + _is_key3 || _is_key4 || _is_key5 || _is_key6 || + _is_key7 || _is_key8 || _is_key9 || _is_key0 || + _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME || + _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW || + _is_keyE || _is_keyR || _is_keyT || _is_keyY || + _is_keyU || _is_keyI || _is_keyO || _is_keyP || + _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN || + _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD || + _is_keyF || _is_keyG || _is_keyH || _is_keyJ || + _is_keyK || _is_keyL || _is_keyENTER || + _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC || + _is_keyV || _is_keyB || _is_keyN || _is_keyM || + _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT || + _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR || + _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT || + _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT || + _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 || + _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 || + _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 || + _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB || + _is_keyPADMUL || _is_keyPADDIV; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode Keycode to test. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())' + disp.wait(); + } + \endcode + **/ + bool is_key(const unsigned int keycode) const { +#define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k; + _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3); + _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7); + _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11); + _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2); + _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6); + _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0); + _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME); + _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W); + _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y); + _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P); + _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN); + _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D); + _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J); + _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER); + _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C); + _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M); + _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT); + _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR); + _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT); + _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT); + _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2); + _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5); + _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8); + _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB); + _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV); + return false; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode C-string containing the keycode label of the key to test. + \note Use it when the key you want to test can be dynamically set by the user. + \par Example + \code + CImgDisplay disp(400,400); + const char *const keycode = "TAB"; + while (!disp.is_closed()) { + if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())' + disp.wait(); + } + \endcode + **/ + bool& is_key(const char *const keycode) { + static bool f = false; + f = false; +#define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k; + _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3); + _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7); + _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11); + _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2); + _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6); + _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0); + _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME); + _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W); + _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y); + _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P); + _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN); + _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D); + _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J); + _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER); + _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C); + _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M); + _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT); + _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR); + _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT); + _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT); + _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2); + _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5); + _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8); + _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB); + _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV); + return f; + } + + //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise. + /** + \param keycodes_sequence Buffer of keycodes to test. + \param length Number of keys in the \c keycodes_sequence buffer. + \param remove_sequence Tells if the key sequence must be removed from the key history, if found. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD }; + while (!disp.is_closed()) { + if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event + disp.wait(); + } + \endcode + **/ + bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, + const bool remove_sequence=false) { + if (keycodes_sequence && length) { + const unsigned int + *const ps_end = keycodes_sequence + length - 1, + *const pk_end = (unsigned int*)_keys + 1 + 128 - length, + k = *ps_end; + for (unsigned int *pk = (unsigned int*)_keys; pk[0,255]. + If the range of values of the data to display is different, a normalization may be required for displaying + the data in a correct way. The normalization type can be one of: + - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the + CImgDisplay instance have values in range [0,255]. + - \c 1: Value normalization is always performed (this is the default behavior). + Before displaying an input image, its values will be (virtually) stretched + in range [0,255], so that the contrast of the displayed pixels will be maximum. + Use this mode for images whose minimum and maximum values are not prescribed to known values + (e.g. float-valued images). + Note that when normalized versions of images are computed for display purposes, the actual values of these + images are not modified. + - \c 2: Value normalization is performed once (on the first image display), then the same normalization + coefficients are kept for next displayed frames. + - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types, + the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then + for unsigned char). + For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image + data instead. + **/ + unsigned int normalization() const { + return _normalization; + } + + //! Return title of the associated window as a C-string. + /** + \note Window title may be not visible, depending on the used window manager or if the current display is + in fullscreen mode. + **/ + const char *title() const { + return _title?_title:""; + } + + //! Return width of the associated window. + /** + \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual width of the associated window. + **/ + int window_width() const { + return (int)_window_width; + } + + //! Return height of the associated window. + /** + \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual height of the associated window. + **/ + int window_height() const { + return (int)_window_height; + } + + //! Return X-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_x() const { + return _window_x; + } + + //! Return Y-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_y() const { + return _window_y; + } + + //! Return X-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,width()-1]. + **/ + int mouse_x() const { + return _mouse_x; + } + + //! Return Y-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,height()-1]. + **/ + int mouse_y() const { + return _mouse_y; + } + + //! Return current state of the mouse buttons. + /** + \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned + value is set: + - bit \c 0 (value \c 0x1): State of the left mouse button. + - bit \c 1 (value \c 0x2): State of the right mouse button. + - bit \c 2 (value \c 0x4): State of the middle mouse button. + + Several bits can be activated if more than one button are pressed at the same time. + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.button()&1) { // Left button clicked + ... + } + if (disp.button()&2) { // Right button clicked + ... + } + if (disp.button()&4) { // Middle button clicked + ... + } + disp.wait(); + } + \endcode + **/ + unsigned int button() const { + return _button; + } + + //! Return current state of the mouse wheel. + /** + \note + - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled + forward or backward. + - Scrolling the wheel forward add \c 1 to the wheel value. + - Scrolling the wheel backward subtract \c 1 to the wheel value. + - The returned value cumulates the number of forward of backward scrolls since the creation of the display, + or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset + the wheel counter when an action has been performed regarding the current wheel value. + Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done + (as many in forward as in backward directions). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.wheel()) { + int counter = disp.wheel(); // Read the state of the mouse wheel + ... // Do what you want with 'counter' + disp.set_wheel(); // Reset the wheel value to 0 + } + disp.wait(); + } + \endcode + **/ + int wheel() const { + return _wheel; + } + + //! Return one entry from the pressed keys history. + /** + \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry). + \return Keycode of a pressed key or \c 0 for a released key. + \note + - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed, + its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead. + This means that up to the 64 last pressed keys may be read from the pressed keys history. + When a new value is stored, the pressed keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int& key(const unsigned int pos=0) const { + static unsigned int key0; + return pos<128?_keys[pos]:(key0 = 0); + + } + + //! Return one entry from the released keys history. + /** + \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry). + \return Keycode of a released key or \c 0 for a pressed key. + \note + - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released, + its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead. + This means that up to the 64 last released keys may be read from the released keys history. + When a new value is stored, the released keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int& released_key(const unsigned int pos=0) const { + static unsigned int key0; + return pos<128?_released_keys[pos]:(key0 = 0); + } + + //! Return keycode corresponding to the specified string. + /** + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB + \endcode + **/ + static unsigned int keycode(const char *const keycode) { +#define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k; + _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3); + _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7); + _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11); + _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2); + _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6); + _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0); + _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME); + _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W); + _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y); + _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P); + _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN); + _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D); + _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J); + _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER); + _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C); + _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M); + _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT); + _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR); + _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT); + _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT); + _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2); + _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5); + _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8); + _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB); + _cimg_keycode(PADMUL); _cimg_keycode(PADDIV); + return 0; + } + + //! Return the current refresh rate, in frames per second. + /** + \note Returns a significant value when the current instance is used to display successive frames. + It measures the delay between successive calls to frames_per_second(). + **/ + float frames_per_second() { + if (!_fps_timer) _fps_timer = cimg::time(); + const float delta = (float)((cimg::time() - _fps_timer)/1000.f); + ++_fps_frames; + if (delta>=1) { + _fps_fps = _fps_frames/delta; + _fps_frames = 0; + _fps_timer = cimg::time(); + } + return _fps_fps; + } + + // Move current display window so that its content stays inside the current screen. + CImgDisplay& move_inside_screen() { + if (is_empty()) return *this; + const int + x0 = window_x(), + y0 = window_y(), + x1 = x0 + window_width() - 1, + y1 = y0 + window_height() - 1, + sw = CImgDisplay::screen_width(), + sh = CImgDisplay::screen_height(); + if (x0<0 || y0<0 || x1>=sw || y1>=sh) + move(std::max(0,std::min(x0,sw - x1 + x0)), + std::max(0,std::min(y0,sh - y1 + y0))); + return *this; + } + + //@} + //--------------------------------------- + // + //! \name Window Manipulation + //@{ + //--------------------------------------- + +#if cimg_display==0 + + //! Display image on associated window. + /** + \param img Input image to display. + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImg& img) { + return assign(img); + } + +#endif + + //! Display list of images on associated window. + /** + \param list List of images to display. + \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c). + \param align Relative position of aligned images when displaying lists with images of different sizes + (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right). + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImgList& list, const char axis='x', const float align=0) { + if (list._width==1) { + const CImg& img = list[0]; + if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img); + } + CImgList::ucharT> visu(list._width); + unsigned int dims = 0; + cimglist_for(list,l) { + const CImg& img = list._data[l]; + img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2).move_to(visu[l]); + dims = std::max(dims,visu[l]._spectrum); + } + cimglist_for(list,l) if (visu[l]._spectrumimg.width() become equal, as well as height() and + img.height(). + - The associated window is also resized to specified dimensions. + **/ + template + CImgDisplay& resize(const CImg& img, const bool force_redraw=true) { + return resize(img._width,img._height,force_redraw); + } + + //! Resize display to the size of another CImgDisplay instance. + /** + \param disp Input display to take size from. + \param force_redraw Tells if the previous window content must be resized and updated as well. + \note + - Calling this method ensures that width() and disp.width() become equal, as well as height() and + disp.height(). + - The associated window is also resized to specified dimensions. + **/ + CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) { + return resize(disp.width(),disp.height(),force_redraw); + } + + // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs). + template + static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, + t *ptrd, const unsigned int wd, const unsigned int hd) { + typedef typename cimg::last::type ulongT; + const ulongT one = (ulongT)1; + CImg off_x(wd), off_y(hd + 1); + if (wd==ws) off_x.fill(1); + else { + ulongT *poff_x = off_x._data, curr = 0; + for (unsigned int x = 0; xstd::printf(). + \warning As the first argument is a format string, it is highly recommended to write + \code + disp.set_title("%s",window_title); + \endcode + instead of + \code + disp.set_title(window_title); + \endcode + if \c window_title can be arbitrary, to prevent nasty memory access. + **/ + CImgDisplay& set_title(const char *const format, ...) { + return assign(0,0,format); + } + +#endif + + //! Enable or disable fullscreen mode. + /** + \param is_fullscreen Tells is the fullscreen mode must be activated or not. + \param force_redraw Tells if the previous window content must be displayed as well. + \note + - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the + current display is not modified. + - The screen resolution may be switched to fit the associated window size and ensure it appears the largest + as possible. + For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen + resolution change (requires the X11 extensions to be enabled). + **/ + CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) { + if (is_empty() || _is_fullscreen==is_fullscreen) return *this; + return toggle_fullscreen(force_redraw); + } + +#if cimg_display==0 + + //! Toggle fullscreen mode. + /** + \param force_redraw Tells if the previous window content must be displayed as well. + \note Enable fullscreen mode if it was not enabled, and disable it otherwise. + **/ + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + return assign(_width,_height,0,3,force_redraw); + } + + //! Show mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& show_mouse() { + return assign(); + } + + //! Hide mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& hide_mouse() { + return assign(); + } + + //! Move mouse pointer to a specified location. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& set_mouse(const int pos_x, const int pos_y) { + return assign(pos_x,pos_y); + } + +#endif + + //! Simulate a mouse button release event. + /** + \note All mouse buttons are considered released at the same time. + **/ + CImgDisplay& set_button() { + _button = 0; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a mouse button press or release event. + /** + \param button Buttons event code, where each button is associated to a single bit. + \param is_pressed Tells if the mouse button is considered as pressed or released. + **/ + CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) { + const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U; + if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode; + _is_event = buttoncode?true:false; + if (buttoncode) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all mouse wheel events. + /** + \note Make wheel() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_wheel() { + _wheel = 0; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a wheel event. + /** + \param amplitude Amplitude of the wheel scrolling to simulate. + \note Make wheel() to return \c amplitude, if called afterwards. + **/ + CImgDisplay& set_wheel(const int amplitude) { + _wheel+=amplitude; + _is_event = amplitude?true:false; + if (amplitude) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all key events. + /** + \note Make key() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_key() { + std::memset((void*)_keys,0,128*sizeof(unsigned int)); + std::memset((void*)_released_keys,0,128*sizeof(unsigned int)); + _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = + _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = + _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = + _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = + _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = + _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL = + _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN = + _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = + _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = + _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = + _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL = + _is_keyPADDIV = false; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a keyboard press/release event. + /** + \param keycode Keycode of the associated key. + \param is_pressed Tells if the key is considered as pressed or released. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) { +#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed; + _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3); + _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7); + _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11); + _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2); + _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6); + _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0); + _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME); + _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W); + _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y); + _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P); + _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN); + _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D); + _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J); + _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER); + _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C); + _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M); + _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT); + _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR); + _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT); + _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT); + _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2); + _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5); + _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8); + _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB); + _cimg_set_key(PADMUL); _cimg_set_key(PADDIV); + if (is_pressed) { + if (*_keys) + std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); + *_keys = keycode; + if (*_released_keys) { + std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); + *_released_keys = 0; + } + } else { + if (*_keys) { + std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); + *_keys = 0; + } + if (*_released_keys) + std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); + *_released_keys = keycode; + } + _is_event = keycode?true:false; + if (keycode) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all display events. + /** + \note Remove all passed events from the current display. + **/ + CImgDisplay& flush() { + set_key().set_button().set_wheel(); + _is_resized = _is_moved = _is_event = false; + _fps_timer = _fps_frames = _timer = 0; + _fps_fps = 0; + return *this; + } + + //! Wait for any user event occurring on the current display. + CImgDisplay& wait() { + wait(*this); + return *this; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \note Similar to cimg::wait(). + **/ + CImgDisplay& wait(const unsigned int milliseconds) { + cimg::wait(milliseconds,&_timer); + return *this; + } + + //! Wait for any event occurring on the display \c disp1. + static void wait(CImgDisplay& disp1) { + disp1._is_event = false; + while (!disp1._is_closed && !disp1._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1 or \c disp2. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { + disp1._is_event = disp2._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed) && + !disp1._is_event && !disp2._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { + disp1._is_event = disp2._is_event = disp3._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, + CImgDisplay& disp5) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) + wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, + CImgDisplay& disp10) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) + wait_all(); + } + +#if cimg_display==0 + + //! Wait for any window event occurring in any opened CImgDisplay. + static void wait_all() { + return _no_display_exception(); + } + + //! Render image into internal display buffer. + /** + \param img Input image data to render. + \note + - Convert image data representation into the internal display buffer (architecture-dependent structure). + - The content of the associated window is not modified, until paint() is called. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + template + CImgDisplay& render(const CImg& img) { + return assign(img); + } + + //! Paint internal display buffer on associated window. + /** + \note + - Update the content of the associated window with the internal display buffer, e.g. after a render() call. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + CImgDisplay& paint() { + return assign(); + } + + + //! Take a snapshot of the current screen content. + /** + \param x0 X-coordinate of the upper left corner. + \param y0 Y-coordinate of the upper left corner. + \param x1 X-coordinate of the lower right corner. + \param y1 Y-coordinate of the lower right corner. + \param[out] img Output screenshot. Can be empty on input + **/ + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + cimg::unused(x0,y0,x1,y1,&img); + _no_display_exception(); + } + + //! Take a snapshot of the associated window content. + /** + \param[out] img Output snapshot. Can be empty on input. + **/ + template + const CImgDisplay& snapshot(CImg& img) const { + cimg::unused(img); + _no_display_exception(); + return *this; + } +#endif + + // X11-based implementation + //-------------------------- +#if cimg_display==1 + + Atom _wm_window_atom, _wm_protocol_atom; + Window _window, _background_window; + Colormap _colormap; + XImage *_image; + void *_data; + +#ifdef cimg_use_xshm + XShmSegmentInfo *_shminfo; +#endif + + static int screen_width() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display."); + res = DisplayWidth(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width; + else res = DisplayWidth(dpy,DefaultScreen(dpy)); +#else + res = DisplayWidth(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static int screen_height() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display."); + res = DisplayHeight(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height; + else res = DisplayHeight(dpy,DefaultScreen(dpy)); +#else + res = DisplayHeight(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static void wait_all() { + if (!cimg::X11_attr().display) return; + pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex); + pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex); + pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex); + } + + void _handle_events(const XEvent *const pevent) { + Display *const dpy = cimg::X11_attr().display; + XEvent event = *pevent; + switch (event.type) { + case ClientMessage : { + if ((int)event.xclient.message_type==(int)_wm_protocol_atom && + (int)event.xclient.data.l[0]==(int)_wm_window_atom) { + XUnmapWindow(cimg::X11_attr().display,_window); + _is_closed = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + } break; + case ConfigureNotify : { + while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {} + const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height; + const int nx = event.xconfigure.x, ny = event.xconfigure.y; + if (nw && nh && (nw!=_window_width || nh!=_window_height)) { + _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1; + XResizeWindow(dpy,_window,_window_width,_window_height); + _is_resized = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + if (nx!=_window_x || ny!=_window_y) { + _window_x = nx; + _window_y = ny; + _is_moved = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + } break; + case Expose : { + while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {} + _paint(false); + if (_is_fullscreen) { + XWindowAttributes attr; + do { + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + XSetInputFocus(dpy,_window,RevertToParent,CurrentTime); + } + } break; + case ButtonPress : { + do { + _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1); break; + case 3 : set_button(2); break; + case 2 : set_button(3); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event)); + } break; + case ButtonRelease : { + do { + _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1,false); break; + case 3 : set_button(2,false); break; + case 2 : set_button(3,false); break; + case 4 : set_wheel(1); break; + case 5 : set_wheel(-1); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event)); + } break; + case KeyPress : { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,true); + } break; + case KeyRelease : { + char keys_return[32]; // Check that the key has been physically unpressed + XQueryKeymap(dpy,keys_return); + const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8; + const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1; + if (!is_key_pressed) { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,false); + } + } break; + case EnterNotify: { + while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + } break; + case LeaveNotify : { + while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {} + _mouse_x = _mouse_y = -1; _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } break; + case MotionNotify : { + while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } break; + } + } + + static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows + Display *const dpy = cimg::X11_attr().display; + XEvent event; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + if (!arg) for ( ; ; ) { + cimg_lock_display(); + bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); + if (!event_flag) event_flag = XCheckMaskEvent(dpy, + ExposureMask | StructureNotifyMask | ButtonPressMask | + KeyPressMask | PointerMotionMask | EnterWindowMask | + LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event); + if (event_flag) + for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) + cimg::X11_attr().wins[i]->_handle_events(&event); + cimg_unlock_display(); + pthread_testcancel(); + cimg::sleep(8); + } + return 0; + } + + void _set_colormap(Colormap& cmap, const unsigned int dim) { + XColor *const colormap = new XColor[256]; + switch (dim) { + case 1 : { // colormap for greyscale images + for (unsigned int index = 0; index<256; ++index) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8); + colormap[index].flags = DoRed | DoGreen | DoBlue; + } + } break; + case 2 : { // colormap for RG images + for (unsigned int index = 0, r = 8; r<256; r+=16) + for (unsigned int g = 8; g<256; g+=16) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].blue = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } break; + default : { // colormap for RGB images + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + colormap[index].pixel = index; + colormap[index].red = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index].blue = (unsigned short)(b<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } + } + XStoreColors(cimg::X11_attr().display,cmap,colormap,256); + delete[] colormap; + } + + void _map_window() { + Display *const dpy = cimg::X11_attr().display; + bool is_exposed = false, is_mapped = false; + XWindowAttributes attr; + XEvent event; + XMapRaised(dpy,_window); + do { // Wait for the window to be mapped + XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event); + switch (event.type) { + case MapNotify : is_mapped = true; break; + case Expose : is_exposed = true; break; + } + } while (!is_exposed || !is_mapped); + do { // Wait for the window to be visible + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + _window_x = attr.x; + _window_y = attr.y; + } + + void _paint(const bool wait_expose=true) { + if (_is_closed || !_image) return; + Display *const dpy = cimg::X11_attr().display; + if (wait_expose) { // Send an expose event sticked to display window to force repaint + XEvent event; + event.xexpose.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = 1; + event.xexpose.display = dpy; + event.xexpose.window = _window; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = width(); + event.xexpose.height = height(); + event.xexpose.count = 0; + XSendEvent(dpy,_window,0,0,&event); + } else { // Repaint directly (may be called from the expose event) + GC gc = DefaultGC(dpy,DefaultScreen(dpy)); + +#ifdef cimg_use_xshm + if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1); + else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#else + XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#endif + } + } + + template + void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) { + Display *const dpy = cimg::X11_attr().display; + cimg::unused(pixel_type); + +#ifdef cimg_use_xshm + if (_shminfo) { + XShmSegmentInfo *const nshminfo = new XShmSegmentInfo; + XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); + if (!nimage) { delete nshminfo; return; } + else { + nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777); + if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; } + else { + nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); + if (nshminfo->shmaddr==(char*)-1) { + shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; + } else { + nshminfo->readOnly = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,nshminfo); + XFlush(dpy); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(nshminfo->shmaddr); + shmctl(nshminfo->shmid,IPC_RMID,0); + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + T *const ndata = (T*)nimage->data; + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + XShmDetach(dpy,_shminfo); + XDestroyImage(_image); + shmdt(_shminfo->shmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = nshminfo; + _image = nimage; + _data = (void*)ndata; + } + } + } + } + } else +#endif + { + T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T)); + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + _data = (void*)ndata; + XDestroyImage(_image); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0); + } + } + + void _init_fullscreen() { + if (!_is_fullscreen || _is_closed) return; + Display *const dpy = cimg::X11_attr().display; + _background_window = 0; + +#ifdef cimg_use_xrandr + int foo; + if (XRRQueryExtension(dpy,&foo,&foo)) { + XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation); + if (!cimg::X11_attr().resolutions) { + cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo); + cimg::X11_attr().nb_resolutions = (unsigned int)foo; + } + if (cimg::X11_attr().resolutions) { + cimg::X11_attr().curr_resolution = 0; + for (unsigned int i = 0; i=_width && nh>=_height && + nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) && + nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height)) + cimg::X11_attr().curr_resolution = i; + } + if (cimg::X11_attr().curr_resolution>0) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy), + cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + } + } + } + if (!cimg::X11_attr().resolutions) + cimg::warn(_cimgdisplay_instance + "init_fullscreen(): Xrandr extension not supported by the X server.", + cimgdisplay_instance); +#endif + + const unsigned int sx = screen_width(), sy = screen_height(); + if (sx==_width && sy==_height) return; + XSetWindowAttributes attr_set; + + attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy)); + attr_set.override_redirect = 1; + _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0, + InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set); + XEvent event; + XSelectInput(dpy,_background_window,StructureNotifyMask); + XMapRaised(dpy,_background_window); + do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event); + while (event.type!=MapNotify); + + XWindowAttributes attr; + do { + XGetWindowAttributes(dpy,_background_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + Display *const dpy = cimg::X11_attr().display; + XUngrabKeyboard(dpy,CurrentTime); + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + cimg::X11_attr().curr_resolution = 0; + } +#endif + if (_background_window) XDestroyWindow(dpy,_background_window); + _background_window = 0; + _is_fullscreen = false; + } + + static int _assign_xshm(Display *dpy, XErrorEvent *error) { + cimg::unused(dpy,error); + cimg::X11_attr().is_shm_enabled = false; + return 0; + } + + void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + cimg::mutex(14); + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous display window if existing + if (!is_empty()) assign(); + + // Open X11 display and retrieve graphical properties. + Display* &dpy = cimg::X11_attr().display; + if (!dpy) { + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Failed to open X11 display.", + cimgdisplay_instance); + + cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy)); + if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && + cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Invalid %u bits screen mode detected " + "(only 8, 16, 24 and 32 bits modes are managed).", + cimgdisplay_instance, + cimg::X11_attr().nb_bits); + XVisualInfo vtemplate; + vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy))); + int nb_visuals; + XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals); + if (vinfo && vinfo->red_maskblue_mask) cimg::X11_attr().is_blue_first = true; + cimg::X11_attr().byte_order = ImageByteOrder(dpy); + XFree(vinfo); + + cimg_lock_display(); + cimg::X11_attr().events_thread = new pthread_t; + pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0); + } else cimg_lock_display(); + + // Set display variables. + _width = std::min(dimw,(unsigned int)screen_width()); + _height = std::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = cimg::type::min(); + _is_closed = closed_flag; + _title = tmp_title; + flush(); + + // Create X11 window (and LUT, if 8bits display) + if (_is_fullscreen) { + if (!_is_closed) _init_fullscreen(); + const unsigned int sx = screen_width(), sy = screen_height(); + XSetWindowAttributes attr_set; + attr_set.override_redirect = 1; + _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0, + InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set); + } else + _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L); + + XSelectInput(dpy,_window, + ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); + + XStoreName(dpy,_window,_title?_title:" "); + if (cimg::X11_attr().nb_bits==8) { + _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll); + _set_colormap(_colormap,3); + XSetWindowColormap(dpy,_window,_colormap); + } + + static const char *const _window_class = cimg_appname; + XClassHint *const window_class = XAllocClassHint(); + window_class->res_name = (char*)_window_class; + window_class->res_class = (char*)_window_class; + XSetClassHint(dpy,_window,window_class); + XFree(window_class); + + _window_width = _width; + _window_height = _height; + + // Create XImage +#ifdef cimg_use_xshm + _shminfo = 0; + if (XShmQueryExtension(dpy)) { + _shminfo = new XShmSegmentInfo; + _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,_shminfo,_width,_height); + if (!_image) { delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777); + if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0)); + if (_shminfo->shmaddr==(char*)-1) { + shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; + } else { + _shminfo->readOnly = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,_shminfo); + XSync(dpy,0); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); + delete _shminfo; _shminfo = 0; + } + } + } + } + } + if (!_shminfo) +#endif + { + const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1: + (cimg::X11_attr().nb_bits==16?2:4)); + _data = std::malloc(buf_size); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,(char*)_data,_width,_height,8,0); + } + + _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0); + _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0); + XSetWMProtocols(dpy,_window,&_wm_window_atom,1); + + if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime); + cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this; + if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type::min(); + cimg_unlock_display(); + cimg::mutex(14,0); + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + + // Remove display window from event thread list. + unsigned int i; + for (i = 0; ishmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = 0; + } +#endif + + XDestroyImage(_image); + if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap); + XDestroyWindow(dpy,_window); + XSync(dpy,0); + _window = 0; _colormap = 0; _data = 0; _image = 0; + + // Reset display variables. + delete[] _title; + _width = _height = _normalization = _window_width = _window_height = 0; + _window_x = _window_y = cimg::type::min(); + _is_fullscreen = false; + _is_closed = true; + _min = _max = 0; + _title = 0; + flush(); + + cimg_unlock_display(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + _min = _max = 0; + std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))* + (size_t)_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + cimg::X11_attr().nb_bits==16?sizeof(unsigned short): + sizeof(unsigned int))*(size_t)_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + Display *const dpy = cimg::X11_attr().display; + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100), + tmpdimy = (nheight>0)?nheight:(-nheight*height()/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + show(); + cimg_lock_display(); + if (_window_width!=dimx || _window_height!=dimy) { + XWindowAttributes attr; + for (unsigned int i = 0; i<10; ++i) { + XResizeWindow(dpy,_window,dimx,dimy); + XGetWindowAttributes(dpy,_window,&attr); + if (attr.width==(int)dimx && attr.height==(int)dimy) break; + cimg::wait(5,&_timer); + } + } + if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { + case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } + } + _window_width = _width = dimx; _window_height = _height = dimy; + cimg_unlock_display(); + } + _is_resized = false; + if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const cimg_ulong buf_size = (cimg_ulong)_width*_height* + (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + void *image_data = std::malloc(buf_size); + std::memcpy(image_data,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,image_data,buf_size); + std::free(image_data); + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + cimg_lock_display(); + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + _map_window(); + cimg_unlock_display(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + if (_is_fullscreen) _desinit_fullscreen(); + XUnmapWindow(dpy,_window); + _window_x = _window_y = cimg::type::min(); + _is_closed = true; + cimg_unlock_display(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + show(); + if (_window_x!=posx || _window_y!=posy) { + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XMoveWindow(dpy,_window,posx,posy); + _window_x = posx; + _window_y = posy; + cimg_unlock_display(); + } + _is_moved = false; + return paint(); + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XUndefineCursor(dpy,_window); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + static const char pix_data[8] = { 0 }; + XColor col; + col.red = col.green = col.blue = 0; + Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); + Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0); + XFreePixmap(dpy,pix); + XDefineCursor(dpy,_window,cur); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); + _mouse_x = posx; _mouse_y = posy; + _is_moved = false; + XSync(dpy,0); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char *const tmp = new char[1024]; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,1024,format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XStoreName(dpy,_window,tmp); + cimg_unlock_display(); + delete[] tmp; + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(false); + } + + CImgDisplay& paint(const bool wait_expose=true) { + if (is_empty()) return *this; + cimg_lock_display(); + _paint(wait_expose); + cimg_unlock_display(); + return *this; + } + + template + CImgDisplay& render(const CImg& img, const bool flag8=false) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2)); + if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) + return render(img.get_resize(_width,_height,1,-100,1)); + if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) { + static const CImg::ucharT> default_colormap = CImg::ucharT>::default_LUT256(); + return render(img.get_index(default_colormap,1,false)); + } + + const T + *data1 = img._data, + *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1; + + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + cimg_lock_display(); + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, no normalization + _set_colormap(_colormap,img._spectrum); + unsigned char + *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: + new unsigned char[(size_t)img._width*img._height], + *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + (*ptrd++) = (unsigned char)*(data1++); + break; + case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++), + B = (unsigned char)*(data3++); + (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); + delete[] ndata; + } + } break; + case 16 : { // 16 bits colors, no normalization + unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: + new unsigned short[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + ptrd[0] = (val&M) | (G>>3); + ptrd[1] = (G<<5) | (G>>1); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + ptrd[0] = (G<<5) | (G>>1); + ptrd[1] = (val&M) | (G>>3); + ptrd+=2; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd[1] = (G<<5); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = (G<<5); + ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd+=2; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3); + ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd+=2; + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); + delete[] ndata; + } + } break; + default : { // 24 bits colors, no normalization + unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: + new unsigned int[(size_t)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | + (unsigned char)*(data3++); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | + ((unsigned char)*(data1++)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data1++); + ptrd[2] = 0; + ptrd[3] = 0; + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = 0; + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data2++); + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data1++); + ptrd[2] = (unsigned char)*(data2++); + ptrd[3] = (unsigned char)*(data3++); + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = (unsigned char)*(data3++); + ptrd[1] = (unsigned char)*(data2++); + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { _min = (float)cimg::type::min(); _max = (float)cimg::type::max(); } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.f); + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, with normalization + _set_colormap(_colormap,img._spectrum); + unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: + new unsigned char[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char R = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = R; + } break; + case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm), + B = (unsigned char)((*(data3++) - _min)*mm); + *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); + delete[] ndata; + } + } break; + case 16 : { // 16 bits colors, with normalization + unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: + new unsigned short[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; + ptrd[0] = (val&M) | (G>>3); + ptrd[1] = (G<<5) | (val>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; + ptrd[0] = (G<<5) | (val>>3); + ptrd[1] = (val&M) | (G>>3); + ptrd+=2; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd[1] = (G<<5); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = (G<<5); + ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd+=2; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); + ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd+=2; + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); + delete[] ndata; + } + } break; + default : { // 24 bits colors, with normalization + unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: + new unsigned int[(size_t)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (val<<24) | (val<<16) | (val<<8); + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8) | + (unsigned char)((*(data3++) - _min)*mm); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data3++) - _min)*mm)<<24) | + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + ptrd[0] = 0; + ptrd[1] = val; + ptrd[2] = val; + ptrd[3] = val; + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + ptrd[0] = val; + ptrd[1] = val; + ptrd[2] = val; + ptrd[3] = 0; + ptrd+=4; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[3] = 0; + ptrd+=4; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[3] = (unsigned char)((*(data3++) - _min)*mm); + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = (unsigned char)((*(data3++) - _min)*mm); + ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[3] = 0; + ptrd+=4; + } + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } + } + } + } + cimg_unlock_display(); + return *this; + } + + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + img.assign(); + Display *dpy = cimg::X11_attr().display; + cimg_lock_display(); + if (!dpy) { + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display."); + } + Window root = DefaultRootWindow(dpy); + XWindowAttributes gwa; + XGetWindowAttributes(dpy,root,&gwa); + const int width = gwa.width, height = gwa.height; + int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; + if (_x0>_x1) cimg::swap(_x0,_x1); + if (_y0>_y1) cimg::swap(_y0,_y1); + + XImage *image = 0; + if (_x1>=0 && _x0=0 && _y0red_mask, + green_mask = image->green_mask, + blue_mask = image->blue_mask; + img.assign(image->width,image->height,1,3); + T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2); + cimg_forXY(img,x,y) { + const unsigned long pixel = XGetPixel(image,x,y); + *(pR++) = (T)((pixel & red_mask)>>16); + *(pG++) = (T)((pixel & green_mask)>>8); + *(pB++) = (T)(pixel & blue_mask); + } + XDestroyImage(image); + } + } + if (!cimg::X11_attr().display) XCloseDisplay(dpy); + cimg_unlock_display(); + if (img.is_empty()) + throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot " + "with coordinates (%d,%d)-(%d,%d).", + x0,y0,x1,y1); + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned char *ptrs = (unsigned char*)_data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + switch (cimg::X11_attr().nb_bits) { + case 8 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = *(ptrs++); + *(data1++) = (T)(val&0xe0); + *(data2++) = (T)((val&0x1c)<<3); + *(data3++) = (T)(val<<6); + } + } break; + case 16 : { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + val0 = ptrs[0], + val1 = ptrs[1]; + ptrs+=2; + *(data1++) = (T)(val0&0xf8); + *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5)); + *(data3++) = (T)(val1<<3); + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned short + val0 = ptrs[0], + val1 = ptrs[1]; + ptrs+=2; + *(data1++) = (T)(val1&0xf8); + *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5)); + *(data3++) = (T)(val0<<3); + } + } break; + default : { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ++ptrs; + *(data1++) = (T)ptrs[0]; + *(data2++) = (T)ptrs[1]; + *(data3++) = (T)ptrs[2]; + ptrs+=3; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + *(data3++) = (T)ptrs[0]; + *(data2++) = (T)ptrs[1]; + *(data1++) = (T)ptrs[2]; + ptrs+=3; + ++ptrs; + } + } + } + return *this; + } + + // Windows-based implementation. + //------------------------------- +#elif cimg_display==2 + + bool _is_mouse_tracked, _is_cursor_visible; + HANDLE _thread, _is_created, _mutex; + HWND _window, _background_window; + CLIENTCREATESTRUCT _ccs; + unsigned int *_data; + DEVMODE _curr_mode; + BITMAPINFO _bmi; + HDC _hdc; + + static int screen_width() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return (int)mode.dmPelsWidth; + } + + static int screen_height() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return (int)mode.dmPelsHeight; + } + + static void wait_all() { + WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE); + } + + static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { +#ifdef _WIN64 + CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); +#else + CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); +#endif + MSG st_msg; + switch (msg) { + case WM_CLOSE : + disp->_mouse_x = disp->_mouse_y = -1; + disp->_window_x = disp->_window_y = cimg::type::min(); + disp->set_button().set_key(0).set_key(0,false)._is_closed = true; + ReleaseMutex(disp->_mutex); + ShowWindow(disp->_window,SW_HIDE); + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + return 0; + case WM_SIZE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->_mutex,INFINITE); + const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); + if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) { + disp->_window_width = nw; + disp->_window_height = nh; + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_resized = disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_MOVE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->_mutex,INFINITE); + const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); + if (nx!=disp->_window_x || ny!=disp->_window_y) { + disp->_window_x = nx; + disp->_window_y = ny; + disp->_is_moved = disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_PAINT : + disp->paint(); + cimg_lock_display(); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0); + cimg_unlock_display(); + break; + case WM_ERASEBKGND : + // return 0; + break; + case WM_KEYDOWN : + disp->set_key((unsigned int)wParam); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_KEYUP : + disp->set_key((unsigned int)wParam,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MOUSEMOVE : { + while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {} + disp->_mouse_x = LOWORD(lParam); + disp->_mouse_y = HIWORD(lParam); +#if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT) + if (!disp->_is_mouse_tracked) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = disp->_window; + if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true; + } +#endif + if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height()) + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + cimg_lock_display(); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0); + cimg_unlock_display(); + } break; + case WM_MOUSELEAVE : { + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_mouse_tracked = false; + cimg_lock_display(); + while (ShowCursor(TRUE)<0) {} + cimg_unlock_display(); + } break; + case WM_LBUTTONDOWN : + disp->set_button(1); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONDOWN : + disp->set_button(2); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONDOWN : + disp->set_button(3); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_LBUTTONUP : + disp->set_button(1,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONUP : + disp->set_button(2,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONUP : + disp->set_button(3,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case 0x020A : // WM_MOUSEWHEEL: + disp->set_wheel((int)((short)HIWORD(wParam))/120); + SetEvent(cimg::Win32_attr().wait_event); + } + return DefWindowProc(window,msg,wParam,lParam); + } + + static DWORD WINAPI _events_thread(void* arg) { + CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]); + const char *const title = (const char*)(((void**)arg)[1]); + MSG msg; + delete[] (void**)arg; + disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + disp->_bmi.bmiHeader.biWidth = disp->width(); + disp->_bmi.bmiHeader.biHeight = -disp->height(); + disp->_bmi.bmiHeader.biPlanes = 1; + disp->_bmi.bmiHeader.biBitCount = 32; + disp->_bmi.bmiHeader.biCompression = BI_RGB; + disp->_bmi.bmiHeader.biSizeImage = 0; + disp->_bmi.bmiHeader.biXPelsPerMeter = 1; + disp->_bmi.bmiHeader.biYPelsPerMeter = 1; + disp->_bmi.bmiHeader.biClrUsed = 0; + disp->_bmi.bmiHeader.biClrImportant = 0; + disp->_data = new unsigned int[(size_t)disp->_width*disp->_height]; + if (!disp->_is_fullscreen) { // Normal window + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int + border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2), + border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1), + ww = disp->width() + 2*border1, + wh = disp->height() + border1 + border2, + sw = CImgDisplay::screen_width(), + sh = CImgDisplay::screen_height(); + int + wx = (int)cimg::round(cimg::rand(0,sw - ww -1)), + wy = (int)cimg::round(cimg::rand(64,sh - wh - 65)); + if (wx + ww>=sw) wx = sw - ww; + if (wy + wh>=sh) wy = sh - wh; + if (wx<0) wx = 0; + if (wy<0) wy = 0; + disp->_window = CreateWindowA("MDICLIENT",title?title:" ", + (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)), + wx,wy,ww,wh,0,0,0,&(disp->_ccs)); + if (!disp->_is_closed) { + GetWindowRect(disp->_window,&rect); + disp->_window_x = rect.left; + disp->_window_y = rect.top; + } else disp->_window_x = disp->_window_y = cimg::type::min(); + } else { // Fullscreen window + const unsigned int + sx = (unsigned int)screen_width(), + sy = (unsigned int)screen_height(); + disp->_window = CreateWindowA("MDICLIENT",title?title:" ", + (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)), + (int)(sx - disp->_width)/2, + (int)(sy - disp->_height)/2, + disp->width(),disp->height(),0,0,0,&(disp->_ccs)); + disp->_window_x = disp->_window_y = 0; + } + SetForegroundWindow(disp->_window); + disp->_hdc = GetDC(disp->_window); + disp->_window_width = disp->_width; + disp->_window_height = disp->_height; + disp->flush(); +#ifdef _WIN64 + SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp); + SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events); +#else + SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp); + SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events); +#endif + SetEvent(disp->_is_created); + while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); + return 0; + } + + CImgDisplay& _update_window_pos() { + if (_is_closed) _window_x = _window_y = cimg::type::min(); + else { + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + GetWindowRect(_window,&rect); + _window_x = rect.left; + _window_y = rect.top; + } + return *this; + } + + void _init_fullscreen() { + _background_window = 0; + if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0; + else { + DEVMODE mode; + unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; + for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { + const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; + if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { + bestbpp = mode.dmBitsPerPel; + ibest = imode; + bw = nw; bh = nh; + } + } + if (bestbpp) { + _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode); + EnumDisplaySettings(0,ibest,&mode); + ChangeDisplaySettings(&mode,0); + } else _curr_mode.dmSize = 0; + + const unsigned int + sx = (unsigned int)screen_width(), + sy = (unsigned int)screen_height(); + if (sx!=_width || sy!=_height) { + CLIENTCREATESTRUCT background_ccs = { 0,0 }; + _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, + 0,0,(int)sx,(int)sy,0,0,0,&background_ccs); + SetForegroundWindow(_background_window); + } + } + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + if (_background_window) DestroyWindow(_background_window); + _background_window = 0; + if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0); + _is_fullscreen = false; + } + + CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (!is_empty()) assign(); + + // Set display variables + _width = std::min(dimw,(unsigned int)screen_width()); + _height = std::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = cimg::type::min(); + _is_closed = closed_flag; + _is_cursor_visible = true; + _is_mouse_tracked = false; + _title = tmp_title; + flush(); + if (_is_fullscreen) _init_fullscreen(); + + // Create event thread + void *const arg = (void*)(new void*[2]); + ((void**)arg)[0] = (void*)this; + ((void**)arg)[1] = (void*)_title; + _mutex = CreateMutex(0,FALSE_WIN,0); + _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); + _thread = CreateThread(0,0,_events_thread,arg,0,0); + WaitForSingleObject(_is_created,INFINITE); + return *this; + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + DestroyWindow(_window); + TerminateThread(_thread,0); + delete[] _data; + delete[] _title; + _data = 0; + _title = 0; + if (_is_fullscreen) _desinit_fullscreen(); + _width = _height = _normalization = _window_width = _window_height = 0; + _window_x = _window_y = cimg::type::min(); + _is_fullscreen = false; + _is_closed = true; + _min = _max = 0; + _title = 0; + flush(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + _min = _max = 0; + std::memset(_data,0,sizeof(unsigned int)*_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight); + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100), + tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + if (_window_width!=dimx || _window_height!=dimy) { + RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1; + SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); + } + if (_width!=dimx || _height!=dimy) { + unsigned int *const ndata = new unsigned int[dimx*dimy]; + if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); + else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + delete[] _data; + _data = ndata; + _bmi.bmiHeader.biWidth = (LONG)dimx; + _bmi.bmiHeader.biHeight = -(int)dimy; + _width = dimx; + _height = dimy; + } + _window_width = dimx; _window_height = dimy; + show(); + } + _is_resized = false; + if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const cimg_ulong buf_size = (cimg_ulong)_width*_height*4; + void *odata = std::malloc(buf_size); + if (odata) { + std::memcpy(odata,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,odata,buf_size); + std::free(odata); + } + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + ShowWindow(_window,SW_SHOW); + _update_window_pos(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + _is_closed = true; + if (_is_fullscreen) _desinit_fullscreen(); + ShowWindow(_window,SW_HIDE); + _window_x = _window_y = cimg::type::min(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + if (_window_x!=posx || _window_y!=posy) { + SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); + _window_x = posx; + _window_y = posy; + } + show(); + _is_moved = false; + return *this; + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = true; + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = false; + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed || posx<0 || posy<0) return *this; + if (!_is_closed) { + _update_window_pos(); + const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy); + if (res) { _mouse_x = posx; _mouse_y = posy; } + } + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char *const tmp = new char[1024]; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,1024,format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + SetWindowTextA(_window, tmp); + delete[] tmp; + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(); + } + + CImgDisplay& paint() { + if (_is_closed) return *this; + WaitForSingleObject(_mutex,INFINITE); + SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS); + ReleaseMutex(_mutex); + return *this; + } + + template + CImgDisplay& render(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2)); + + const T + *data1 = img._data, + *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1; + + WaitForSingleObject(_mutex,INFINITE); + unsigned int + *const ndata = (img._width==_width && img._height==_height)?_data: + new unsigned int[(size_t)img._width*img._height], + *ptrd = ndata; + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (img._spectrum) { + case 1 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); + } + } break; + case 2 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); + } + } break; + default : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++), + B = (unsigned char)*(data3++); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); + } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { + _min = (float)cimg::type::min(); + _max = (float)cimg::type::max(); + } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.f); + switch (img._spectrum) { + case 1 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); + } + } break; + case 2 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); + } + } break; + default : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm), + B = (unsigned char)((*(data3++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); + } + } + } + } + if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; } + ReleaseMutex(_mutex); + return *this; + } + + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + img.assign(); + HDC hScreen = GetDC(GetDesktopWindow()); + if (hScreen) { + const int + width = GetDeviceCaps(hScreen,HORZRES), + height = GetDeviceCaps(hScreen,VERTRES); + int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; + if (_x0>_x1) cimg::swap(_x0,_x1); + if (_y0>_y1) cimg::swap(_y0,_y1); + if (_x1>=0 && _x0=0 && _y0 + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned int *ptrs = _data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned int val = *(ptrs++); + *(data1++) = (T)(unsigned char)(val>>16); + *(data2++) = (T)(unsigned char)((val>>8)&0xFF); + *(data3++) = (T)(unsigned char)(val&0xFF); + } + return *this; + } +#endif + + //@} + }; // struct CImgDisplay { ... + + /* + #-------------------------------------- + # + # + # + # Definition of the CImg structure + # + # + # + #-------------------------------------- + */ + + //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. + /** + This is the main class of the %CImg Library. It declares and constructs + an image, allows access to its pixel values, and is able to perform various image operations. + + \par Image representation + + A %CImg image is defined as an instance of the container \c CImg, which contains a regular grid of pixels, + each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth + and number of channels. + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), + while the number of channels is rather used as a vector-valued dimension + (it may describe the R,G,B color channels for instance). + If you need a fifth dimension, you can use image lists \c CImgList rather than simple images \c CImg. + + Thus, the \c CImg class is able to represent volumetric images of vector-valued pixels, + as well as images with less dimensions (1D scalar signal, 2D color images, ...). + Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. + + Concerning the pixel value type \c T: + fully supported template types are the basic C++ types: unsigned char, char, short, unsigned int, int, + unsigned long, long, float, double, ... . + Typically, fast image display can be done using CImg images, + while complex image processing algorithms may be rather coded using CImg or CImg + images that have floating-point pixel values. The default value for the template T is \c float. + Using your own template types may be possible. However, you will certainly have to define the complete set + of arithmetic and logical operators for your class. + + \par Image structure + + The \c CImg structure contains \e six fields: + - \c _width defines the number of \a columns of the image (size along the X-axis). + - \c _height defines the number of \a rows of the image (size along the Y-axis). + - \c _depth defines the number of \a slices of the image (size along the Z-axis). + - \c _spectrum defines the number of \a channels of the image (size along the C-axis). + - \c _data defines a \a pointer to the \a pixel \a data (of type \c T). + - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with + another image. + + You can access these fields publicly although it is recommended to use the dedicated functions + width(), height(), depth(), spectrum() and ptr() to do so. + Image dimensions are not limited to a specific range (as long as you got enough available memory). + A value of \e 1 usually means that the corresponding dimension is \a flat. + If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. + Empty images should not contain any pixel data and thus, will not be processed by CImg member functions + (a CImgInstanceException will be thrown instead). + Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). + + \par Image declaration and construction + + Declaring an image can be done by using one of the several available constructors. + Here is a list of the most used: + + - Construct images from arbitrary dimensions: + - CImg img; declares an empty image. + - CImg img(128,128); declares a 128x128 greyscale image with + \c unsigned \c char pixel values. + - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. + - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image + (colors are stored as an image with three channels). + - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image + (with \c double pixel values). + - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image + (with \c float pixels, which is the default value of the template parameter \c T). + - \b Note: images pixels are not automatically initialized to 0. You may use the function \c fill() to + do it, or use the specific constructor taking 5 parameters like this: + CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. + + - Construct images from filenames: + - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the + file "analyze.hdr". + - \b Note: You need to install ImageMagick + to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). + + - Construct images from C-style arrays: + - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer + \c data_buffer (of size 256x256=65536). + - CImg img(data_buffer,256,256,1,3); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). + + The complete list of constructors can be found here. + + \par Most useful functions + + The \c CImg class contains a lot of functions that operates on images. + Some of the most useful are: + + - operator()(): Read or write pixel values. + - display(): displays the image in a new window. + **/ + template + struct CImg { + + unsigned int _width, _height, _depth, _spectrum; + bool _is_shared; + T *_data; + + //! Simple iterator type, to loop through each pixel value of an image instance. + /** + \note + - The \c CImg::iterator type is defined to be a T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + CImg img("reference.jpg"); // Load image from file + // Set all pixels to '0', with a CImg iterator. + for (CImg::iterator it = img.begin(), it::const_iterator type is defined to be a \c const \c T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + const CImg img("reference.jpg"); // Load image from file + float sum = 0; + // Compute sum of all pixel values, with a CImg iterator. + for (CImg::iterator it = img.begin(), it::value_type type of a \c CImg is defined to be a \c T. + - \c CImg::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs. + static size_t safe_size(const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) { + if (!(dx && dy && dz && dc)) return 0; + size_t siz = (size_t)dx, osiz = siz; + if ((dy==1 || (siz*=dy)>osiz) && + ((osiz = siz), dz==1 || (siz*=dz)>osiz) && + ((osiz = siz), dc==1 || (siz*=dc)>osiz) && + ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) return siz; + throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.", + pixel_type(),dx,dy,dz,dc); + } + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimg_plugin +#include cimg_plugin +#endif +#ifdef cimg_plugin1 +#include cimg_plugin1 +#endif +#ifdef cimg_plugin2 +#include cimg_plugin2 +#endif +#ifdef cimg_plugin3 +#include cimg_plugin3 +#endif +#ifdef cimg_plugin4 +#include cimg_plugin4 +#endif +#ifdef cimg_plugin5 +#include cimg_plugin5 +#endif +#ifdef cimg_plugin6 +#include cimg_plugin6 +#endif +#ifdef cimg_plugin7 +#include cimg_plugin7 +#endif +#ifdef cimg_plugin8 +#include cimg_plugin8 +#endif + + //@} + //--------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //--------------------------------------------------------- + + //! Destroy image. + /** + \note + - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances. + - Destroying an empty or shared image does nothing actually. + \warning + - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image + that shares its buffer with the destroyed instance, in order to avoid further invalid memory access + (to a deallocated buffer). + **/ + ~CImg() { + if (!_is_shared) delete[] _data; + } + + //! Construct empty image. + /** + \note + - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum() + are set to \c 0, as well as its pixel buffer pointer data(). + - An empty image may be re-assigned afterwards, e.g. with the family of + assign(unsigned int,unsigned int,unsigned int,unsigned int) methods, + or by operator=(const CImg&). In all cases, the type of pixels stays \c T. + - An empty image is never shared. + \par Example + \code + CImg img1, img2; // Construct two empty images + img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image + img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1' + img2.assign(); // Re-assign 'img2' to be an empty image again + \endcode + **/ + CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} + + //! Construct image with specified size. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \note + - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() + for each constructed image instance. + - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of + an \e empty image. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. when requested size is too big for available memory). + \warning + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values during construction (e.g. with \c 0), use constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead. + \par Example + \code + CImg img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values + CImg img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0' + \endcode + **/ + explicit CImg(const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1): + _is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value Initialization value. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), + but it also fills the pixel buffer with the specified \c value. + \warning + - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels + (e.g. RGB vector, for color images). + For this task, you may use fillC() after construction. + **/ + CImg(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const T& value): + _is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + fill(value); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, + with pixels of type \c T, and initialize pixel + values from the specified sequence of integers \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be an \e integer). + \param value1 Second value of the initialization sequence (must be an \e integer). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \warning + - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence. + Otherwise, the constructor may crash or fill your image pixels with garbage. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image + 0,255,0,255, // Set the 4 values for the red component + 0,0,255,255, // Set the 4 values for the green component + 64,64,64,64); // Set the 4 values for the blue component + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _CImg_stdarg(img,a0,a1,N,t) { \ + size_t _siz = (size_t)N; \ + if (_siz--) { \ + va_list ap; \ + va_start(ap,a1); \ + T *ptrd = (img)._data; \ + *(ptrd++) = (T)a0; \ + if (_siz--) { \ + *(ptrd++) = (T)a1; \ + for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + } \ + va_end(ap); \ + } \ + } + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int); + } + +#if cimg_use_cpp11==1 + //! Construct image with specified size and initialize pixel values from an initializer list of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, + with pixels of type \c T, and initialize pixel + values from the specified initializer list of integers { \c value0,\c value1,\c ... } + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param { value0, value1, ... } Initialization list + \param repeat_values Tells if the value filling process is repeated over the image. + + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image + { 0,255,0,255, // Set the 4 values for the red component + 0,0,255,255, // Set the 4 values for the green component + 64,64,64,64 }); // Set the 4 values for the blue component + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + template + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _cimg_constructor_cpp11(repeat_values) \ + auto it = values.begin(); \ + size_t siz = size(); \ + if (repeat_values) for (T *ptrd = _data; siz--; ) { \ + *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \ + else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); } + assign(size_x,size_y,size_z,size_c); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, + std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, const unsigned int size_y, + std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, + std::initializer_list values, + const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x); + _cimg_constructor_cpp11(repeat_values); + } + + //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers. + /** + Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1, + with pixels of type \c T, and initialize pixel + values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is + given by the size of the initializer list. + \param { value0, value1, ... } Initialization list + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1, + but it also fills the pixel buffer with a sequence of specified integer values. + \par Example + \code + const CImg img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + template + CImg(const std::initializer_list values): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(values.size(),1,1,1); + auto it = values.begin(); + unsigned int siz = _width; + for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); + } + + template + CImg& operator=(std::initializer_list values) { + _cimg_constructor_cpp11(siz>values.size()); + return *this; + } +#endif + + //! Construct image with specified size and initialize pixel values from a sequence of doubles. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be a \e double). + \param value1 Second value of the initialization sequence (must be a \e double). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but + takes a sequence of double values instead of integers. + \warning + - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence. + Otherwise, the constructor may crash or fill your image with garbage. + For instance, the code below will probably crash on most platforms: + \code + const CImg img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'! + \endcode + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double); + } + + //! Construct image with specified size and initialize pixel values from a value string. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initializes pixel values from the specified string \c values. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param values Value string describing the way pixel values are set. + \param repeat_values Tells if the value filling process is repeated over the image. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with values described in the value string \c values. + - Value string \c values may describe two different filling processes: + - Either \c values is a sequences of values assigned to the image pixels, as in "1,2,3,7,8,2". + In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence. + - Either, \c values is a formula, as in "cos(x/10)*sin(y/20)". + In this case, parameter \c repeat_values is pointless. + - For both cases, specifying \c repeat_values is mandatory. + It disambiguates the possible overloading of constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a const char*. + - A \c CImgArgumentException is thrown when an invalid value string \c values is specified. + \par Example + \code + const CImg img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence + img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula + (img1,img2).display(); + \endcode + \image html ref_constructor2.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values):_is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + fill(values,repeat_values); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initializes pixel values from the specified \c t* memory buffer. + \param values Pointer to the input memory buffer. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param is_shared Tells if input memory buffer must be shared by the current instance. + \note + - If \c is_shared is \c false, the image instance allocates its own pixel buffer, + and values from the specified input buffer are copied to the instance buffer. + If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy. + - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its + own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared + image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. when requested size is too big for available memory). + \warning + - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() + (e.g. already deallocated). + \par Example + \code + unsigned char tab[256*256] = { 0 }; + CImg img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab' + img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab' + tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1' + \endcode + **/ + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance " + "from a (%s*) buffer (pixel types are different).", + cimg_instance, + size_x,size_y,size_z,size_c,CImg::pixel_type()); + } + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + + } + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared; + if (_is_shared) _data = const_cast(values); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + std::memcpy(_data,values,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from memory buffer with specified size and pixel ordering scheme. + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const axes_order):_data(0),_is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } + else { ++n_code[c%=4]; s_code[l] = c; } + } + if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { + const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); + int s0 = 0, s1 = 0, s2 = 0, s3 = 0; + const char *inv_order = 0; + switch (code) { + case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc + case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz + case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc + case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy + case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz + case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy + case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc + case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz + case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc + case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx + case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz + case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx + case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc + case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy + case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc + case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx + case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy + case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx + case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz + case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy + case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz + case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx + case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy + case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx + } + CImg(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this); + } else { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from reading an image file. + /** + Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from + an image file. + \param filename Filename, as a C-string. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image + dimensions and pixel values from the specified image file. + - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system + and on the external libraries you used to link your code against. + - Considered pixel type \c T should better fit the file format specification, or data loss may occur during + file load (e.g. constructing a \c CImg from a float-valued image file). + - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not + recognized. + \par Example + \code + const CImg img("reference.jpg"); + img.display(); + \endcode + \image html ref_image.jpg + **/ + explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(filename); + } + + //! Construct image copy. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance. + \param img Input image to copy. + \note + - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the + input image \c img. + - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also + \e shared, and shares its pixel buffer with \c img. + Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img. + This behavior is needful to allow functions to return shared images. + - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input + image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and + \c t are different. + - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than + with different types. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. not enough available memory). + **/ + template + CImg(const CImg& img):_is_shared(false) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image copy \specialization. + CImg(const CImg& img) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = img._is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Advanced copy constructor. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance, + while forcing the shared state of the constructed copy. + \param img Input image to copy. + \param is_shared Tells about the shared state of the constructed copy. + \note + - Similar to CImg(const CImg&), except that it allows to decide the shared state of + the constructed image, which does not depend anymore on the shared state of the input image \c img: + - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img. + For that case, the pixel types \c T and \c t \e must be the same. + - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input + image \c img is shared or not. + - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t. + **/ + template + CImg(const CImg& img, const bool is_shared):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a shared instance from a " + "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).", + cimg_instance, + CImg::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data); + } + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Advanced copy constructor \specialization. + CImg(const CImg& img, const bool is_shared) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image with dimensions borrowed from another image. + /** + Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing + \c CImg instance. + \param img Input image from which dimensions are borrowed. + \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions + (\e not its pixel values) from an existing \c CImg instance. + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg&,const char*,T) + instead. + \par Example + \code + const CImg img1(256,128,1,3), // 'img1' is a 256x128x1x3 image + img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image + img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image + img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0') + \endcode + **/ + template + CImg(const CImg& img, const char *const dimensions): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values. + /** + Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing + \c CImg instance, and set all pixel values to specified \c value. + \param img Input image from which dimensions are borrowed. + \param dimensions String describing the image size along the X,Y,Z and V-dimensions. + \param value Value used for initialization. + \note + - Similar to CImg(const CImg&,const char*), but it also fills the pixel buffer with the specified \c value. + **/ + template + CImg(const CImg& img, const char *const dimensions, const T& value): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions).fill(value); + } + + //! Construct image from a display window. + /** + Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance. + \param disp Input display window. + \note + - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay. + - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 + (i.e. a 2D color image). + - The image pixels are read as 8-bits RGB values. + **/ + explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + disp.snapshot(*this); + } + + // Constructor and assignment operator for rvalue references (c++11). + // This avoids an additional image copy for methods returning new images. Can save RAM for big images ! +#if cimg_use_cpp11==1 + CImg(CImg&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + swap(img); + } + + CImg& operator=(CImg&& img) { + if (_is_shared) return assign(img); + return img.swap(*this); + } +#endif + + //! Construct empty image \inplace. + /** + In-place version of the default constructor CImg(). It simply resets the instance to an empty image. + **/ + CImg& assign() { + if (!_is_shared) delete[] _data; + _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; + return *this; + } + + //! Construct image with specified size \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!siz) return assign(); + const size_t curr_siz = (size_t)size(); + if (siz!=curr_siz) { + if (_is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from specified " + "image (%u,%u,%u,%u).", + cimg_instance, + size_x,size_y,size_z,size_c); + else { + delete[] _data; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + } + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + return *this; + } + + //! Construct image with specified size and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const T& value) { + return assign(size_x,size_y,size_z,size_c).fill(value); + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a value string \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values) { + return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \inplace. + /** + In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int). + **/ + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + assign(size_x,size_y,size_z,size_c); + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + const size_t curr_siz = (size_t)size(); + if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); + if (_is_shared || values + siz<_data || values>=_data + size()) { + assign(size_x,size_y,size_z,size_c); + if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T)); + else std::memcpy((void*)_data,(void*)values,siz*sizeof(T)); + } else { + T *new_data = 0; + try { new_data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + std::memcpy((void*)new_data,(void*)values,siz*sizeof(T)); + delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + } + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + if (is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from (%s*) buffer" + "(pixel types are different).", + cimg_instance, + CImg::pixel_type()); + return assign(values,size_x,size_y,size_z,size_c); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } + else { + if (!_is_shared) { + if (values + siz<_data || values>=_data + size()) assign(); + else cimg::warn(_cimg_instance + "assign(): Shared image instance has overlapping memory.", + cimg_instance); + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; + _data = const_cast(values); + } + return *this; + } + + //! Construct image from memory buffer with specified size and pixel ordering scheme. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const axes_order) { + CImg(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this); + } + + //! Construct image from reading an image file \inplace. + /** + In-place version of the constructor CImg(const char*). + **/ + CImg& assign(const char *const filename) { + return load(filename); + } + + //! Construct image copy \inplace. + /** + In-place version of the constructor CImg(const CImg&). + **/ + template + CImg& assign(const CImg& img) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum); + } + + //! In-place version of the advanced copy constructor. + /** + In-place version of the constructor CImg(const CImg&,bool). + **/ + template + CImg& assign(const CImg& img, const bool is_shared) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared); + } + + //! Construct image with dimensions borrowed from another image \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions) { + if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum); + unsigned int siz[4] = { 0,1,1,1 }, k = 0; + CImg item(256); + for (const char *s = dimensions; *s && k<4; ++k) { + if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item); + if (*s) { + unsigned int val = 0; char sep = 0; + if (cimg_sscanf(s,"%u%c",&val,&sep)>0) { + if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100; + else siz[k] = val; + while (*s>='0' && *s<='9') ++s; + if (sep=='%') ++s; + } else switch (cimg::lowercase(*s)) { + case 'x' : case 'w' : siz[k] = img._width; ++s; break; + case 'y' : case 'h' : siz[k] = img._height; ++s; break; + case 'z' : case 'd' : siz[k] = img._depth; ++s; break; + case 'c' : case 's' : siz[k] = img._spectrum; ++s; break; + default : + throw CImgArgumentException(_cimg_instance + "assign(): Invalid character '%c' detected in specified dimension string '%s'.", + cimg_instance, + *s,dimensions); + } + } + } + return assign(siz[0],siz[1],siz[2],siz[3]); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*,T). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions, const T& value) { + return assign(img,dimensions).fill(value); + } + + //! Construct image from a display window \inplace. + /** + In-place version of the constructor CImg(const CImgDisplay&). + **/ + CImg& assign(const CImgDisplay &disp) { + disp.snapshot(*this); + return *this; + } + + //! Construct empty image \inplace. + /** + Equivalent to assign(). + \note + - It has been defined for compatibility with STL naming conventions. + **/ + CImg& clear() { + return assign(); + } + + //! Transfer content of an image instance into another one. + /** + Transfer the dimensions and the pixel buffer content of an image instance into another one, + and replace instance by an empty image. It avoids the copy of the pixel buffer + when possible. + \param img Destination image. + \note + - Pixel types \c T and \c t of source and destination images can be different, though the process is + designed to be instantaneous when \c T and \c t are the same. + \par Example + \code + CImg src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0' + dest(16,16); // Construct a 16x16x1x1 (scalar) image + src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image + \endcode + **/ + template + CImg& move_to(CImg& img) { + img.assign(*this); + assign(); + return img; + } + + //! Transfer content of an image instance into another one \specialization. + CImg& move_to(CImg& img) { + if (_is_shared || img._is_shared) img.assign(*this); + else swap(img); + assign(); + return img; + } + + //! Transfer content of an image instance into a new image in an image list. + /** + Transfer the dimensions and the pixel buffer content of an image instance + into a newly inserted image at position \c pos in specified \c CImgList instance. + \param list Destination list. + \param pos Position of the newly inserted image in the list. + \note + - When optional parameter \c pos is omitted, the image instance is transferred as a new + image at the end of the specified \c list. + - It is convenient to sequentially insert new images into image lists, with no + additional copies of memory buffer. + \par Example + \code + CImgList list; // Construct an empty image list + CImg img("reference.jpg"); // Read image from filename + img.move_to(list); // Transfer image content as a new item in the list (no buffer copy) + \endcode + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos=~0U) { + const unsigned int npos = pos>list._width?list._width:pos; + move_to(list.insert(1,npos)[npos]); + return list; + } + + //! Swap fields of two image instances. + /** + \param img Image to swap fields with. + \note + - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing + with algorithms requiring two swapping buffers. + \par Example + \code + CImg img1("lena.jpg"), + img2("milla.jpg"); + img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena' + \endcode + **/ + CImg& swap(CImg& img) { + cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum); + cimg::swap(_data,img._data); + cimg::swap(_is_shared,img._is_shared); + return img; + } + + //! Return a reference to an empty image. + /** + \note + This function is useful mainly to declare optional parameters having type \c CImg in functions prototypes, + e.g. + \code + void f(const int x=0, const int y=0, const CImg& img=CImg::empty()); + \endcode + **/ + static CImg& empty() { + static CImg _empty; + return _empty.assign(); + } + + //! Return a reference to an empty image \const. + static const CImg& const_empty() { + static const CImg _empty; + return _empty; + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Access to a pixel value. + /** + Return a reference to a located pixel value of the image instance, + being possibly \e const, whether the image instance is \e const or not. + This is the standard method to get/set pixel values in \c CImg images. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Range of pixel coordinates start from (0,0,0,0) to + (width() - 1,height() - 1,depth() - 1,spectrum() - 1). + - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the + corresponding dimension is equal to \c 1. + For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by img(x,y,c) instead of + img(x,y,0,c). + \warning + - There is \e no boundary checking done in this operator, to make it as fast as possible. + You \e must take care of out-of-bounds access by yourself, if necessary. + For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary + checking operations in this operator. In that case, warning messages will be printed on the error output + when accessing out-of-bounds pixels. + \par Example + \code + CImg img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0' + const float + valR = img(10,10,0,0), // Read red value at coordinates (10,10) + valG = img(10,10,0,1), // Read green value at coordinates (10,10) + valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted) + avg = (valR + valG + valB)/3; // Compute average pixel value + img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value + \endcode + **/ +#if cimg_verbosity>=3 + T& operator()(const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + const ulongT off = (ulongT)offset(x,y,z,c); + if (!_data || off>=size()) { + cimg::warn(_cimg_instance + "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].", + cimg_instance, + (int)x,(int)y,(int)z,(int)c,off); + return *_data; + } + else return _data[off]; + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->operator()(x,y,z,c); + } + + //! Access to a pixel value. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param wh Precomputed offset, must be equal to width()*\ref height(). + \param whd Precomputed offset, must be equal to width()*\ref height()*\ref depth(). + \note + - Similar to (but faster than) operator()(). + It uses precomputed offsets to optimize memory access. You may use it to optimize + the reading/writing of several pixel values in the same image (e.g. in a loop). + **/ + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd=0) { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd=0) const { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } +#else + T& operator()(const unsigned int x) { + return _data[x]; + } + + const T& operator()(const unsigned int x) const { + return _data[x]; + } + + T& operator()(const unsigned int x, const unsigned int y) { + return _data[x + y*_width]; + } + + const T& operator()(const unsigned int x, const unsigned int y) const { + return _data[x + y*_width]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const ulongT wh) { + return _data[x + y*_width + z*wh]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const ulongT wh) const { + return _data[x + y*_width + z*wh]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd) { + return _data[x + y*_width + z*wh + c*whd]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd) const { + return _data[x + y*_width + z*wh + c*whd]; + } +#endif + + //! Implicitly cast an image into a \c T*. + /** + Implicitly cast a \c CImg instance into a \c T* or \c const \c T* pointer, whether the image instance + is \e const or not. The returned pointer points on the first value of the image pixel buffer. + \note + - It simply returns the pointer data() to the pixel buffer. + - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g. + \code + CImg img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image + if (img1) { // Test succeeds, 'img1' is not an empty image + if (!img2) { // Test succeeds, 'img2' is an empty image + std::printf("'img1' is not empty, 'img2' is empty."); + } + } + \endcode + - It also allows to use brackets to access pixel values, without need for a \c CImg::operator[](), e.g. + \code + CImg img(100,100); + const float value = img[99]; // Access to value of the last pixel on the first row + img[510] = 255; // Set pixel value at (10,5) + \endcode + **/ + operator T*() { + return _data; + } + + //! Implicitly cast an image into a \c T* \const. + operator const T*() const { + return _data; + } + + //! Assign a value to all image pixels. + /** + Assign specified \c value to each pixel value of the image instance. + \param value Value that will be assigned to image pixels. + \note + - The image size is never modified. + - The \c value may be casted to pixel type \c T if necessary. + \par Example + \code + CImg img(100,100); // Declare image (with garbage values) + img = 0; // Set all pixel values to '0' + img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char') + \endcode + **/ + CImg& operator=(const T& value) { + return fill(value); + } + + //! Assign pixels values from a specified expression. + /** + Initialize all pixel values from the specified string \c expression. + \param expression Value string describing the way pixel values are set. + \note + - String parameter \c expression may describe different things: + - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"), + the pixel values are set from specified \c expression and the image size is not modified. + - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and + replace the image instance. The image size is modified if necessary. + \par Example + \code + CImg img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values + img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence + img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula + img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified) + (img1,img2,img3).display(); + \endcode + \image html ref_operator_eq.jpg + **/ + CImg& operator=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + _fill(expression,true,1,0,0,"operator=",0); + } catch (CImgException&) { + cimg::exception_mode(omode); + load(expression); + } + cimg::exception_mode(omode); + return *this; + } + + //! Copy an image into the current image instance. + /** + Similar to the in-place copy constructor assign(const CImg&). + **/ + template + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy an image into the current image instance \specialization. + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy the content of a display window to the current image instance. + /** + Similar to assign(const CImgDisplay&). + **/ + CImg& operator=(const CImgDisplay& disp) { + disp.snapshot(*this); + return *this; + } + + //! In-place addition operator. + /** + Add specified \c value to all pixels of an image instance. + \param value Value to add. + \note + - Resulting pixel values are casted to fit the pixel type \c T. + For instance, adding \c 0.2 to a \c CImg is possible but does nothing indeed. + - Overflow values are treated as with standard C++ numeric types. For instance, + \code + CImg img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255' + img+=1; // Add '1' to each pixels -> Overflow + // here all pixels of image 'img' are equal to '0'. + \endcode + - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, + and use cut() after addition. + \par Example + \code + CImg img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]) + CImg img2(img1); // Construct a float-valued copy of 'img1' + img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats + img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint + img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1' + const CImg img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way + (img1,img2,img3).display(); + \endcode + \image html ref_operator_plus.jpg + **/ + template + CImg& operator+=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr + value,524288); + return *this; + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the specified string \c expression. + \param expression Value string describing the way pixel values are added. + \note + - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance, + instead of assigning them. + **/ + CImg& operator+=(const char *const expression) { + return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this); + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the values of the input image \c img. + \param img Input image to add. + \note + - The size of the image instance is never modified. + - It is not mandatory that input image \c img has the same size as the image instance. + If less values are available in \c img, then the values are added periodically. For instance, adding one + WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3) + means each color channel will be incremented with the same values at the same locations. + \par Example + \code + CImg img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) + // Construct a scalar shading (img2.spectrum()==1). + const CImg img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); + img1+=img2; // Add shading to each channel of 'img1' + img1.cut(0,255); // Prevent [0,255] overflow + (img2,img1).display(); + \endcode + \image html ref_operator_plus1.jpg + **/ + template + CImg& operator+=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this+=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator++() { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr + 1,524288); + return *this; + } + + //! In-place increment operator (postfix). + /** + Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance. + \note + - Use the prefixed version operator++() if you don't need a copy of the initial + (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage. + **/ + CImg operator++(int) { + const CImg copy(*this,false); + ++*this; + return copy; + } + + //! Return a non-shared copy of the image instance. + /** + \note + - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T. + Indeed, the usual copy constructor CImg(const CImg&) returns a shared copy of a shared input image, + and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no + information about the shared state of the input image. + - Writing \c (+img) is equivalent to \c CImg(img,false). + **/ + CImg operator+() const { + return CImg(*this,false); + } + + //! Addition operator. + /** + Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const t value) const { + return CImg<_cimg_Tt>(*this,false)+=value; + } + + //! Addition operator. + /** + Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator+(const char *const expression) const { + return CImg(*this,false)+=expression; + } + + //! Addition operator. + /** + Similar to operator+=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)+=img; + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const t), except that it performs a subtraction instead of an addition. + **/ + template + CImg& operator-=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr - value,524288); + return *this; + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const char*), except that it performs a subtraction instead of an addition. + **/ + CImg& operator-=(const char *const expression) { + return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this); + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const CImg&), except that it performs a subtraction instead of an addition. + **/ + template + CImg& operator-=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this-=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator--() { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr - 1,524288); + return *this; + } + + //! In-place decrement operator (postfix). + /** + Similar to operator++(int), except that it performs a decrement instead of an increment. + **/ + CImg operator--(int) { + const CImg copy(*this,false); + --*this; + return copy; + } + + //! Replace each pixel by its opposite value. + /** + \note + - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. + For instance, the \c unsigned \c char opposite of \c 1 is \c 255. + \par Example + \code + const CImg + img1("reference.jpg"), // Load a RGB color image + img2 = -img1; // Compute its opposite (in 'unsigned char') + (img1,img2).display(); + \endcode + \image html ref_operator_minus.jpg + **/ + CImg operator-() const { + return CImg(_width,_height,_depth,_spectrum,(T)0)-=*this; + } + + //! Subtraction operator. + /** + Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const t value) const { + return CImg<_cimg_Tt>(*this,false)-=value; + } + + //! Subtraction operator. + /** + Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator-(const char *const expression) const { + return CImg(*this,false)-=expression; + } + + //! Subtraction operator. + /** + Similar to operator-=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)-=img; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const t), except that it performs a multiplication instead of an addition. + **/ + template + CImg& operator*=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr * value,262144); + return *this; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. + **/ + CImg& operator*=(const char *const expression) { + return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this)); + } + + //! In-place multiplication operator. + /** + Replace the image instance by the matrix multiplication between the image instance and the specified matrix + \c img. + \param img Second operand of the matrix multiplication. + \note + - It does \e not compute a pointwise multiplication between two images. For this purpose, use + mul(const CImg&) instead. + - The size of the image instance can be modified by this operator. + \par Example + \code + CImg A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4] + const CImg X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2] + A*=X; // Assign matrix multiplication A*X to 'A' + // 'A' is now a 1x2 vector whose values are [5;11]. + \endcode + **/ + template + CImg& operator*=(const CImg& img) { + return ((*this)*img).move_to(*this); + } + + //! Multiplication operator. + /** + Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const t value) const { + return CImg<_cimg_Tt>(*this,false)*=value; + } + + //! Multiplication operator. + /** + Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator*(const char *const expression) const { + return CImg(*this,false)*=expression; + } + + //! Multiplication operator. + /** + Similar to operator*=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const CImg& img) const { + typedef _cimg_Ttdouble Ttdouble; + typedef _cimg_Tt Tt; + if (_width!=img._height || _depth!=1 || _spectrum!=1) + throw CImgArgumentException(_cimg_instance + "operator*(): Invalid multiplication of instance by specified " + "matrix (%u,%u,%u,%u,%p).", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + CImg res(img._width,_height); + + // Check for common cases to optimize. + if (img._width==1) { // Matrix * Vector + if (_height==1) switch (_width) { // Vector^T * Vector + case 1 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0]); + return res; + case 2 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); + return res; + case 3 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2]); + return res; + case 4 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); + return res; + default : { + Ttdouble val = 0; + cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096)) + cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i]; + res[0] = val; + return res; + } + } else if (_height==_width) switch (_width) { // Square_matrix * Vector + case 2 : // 2x2_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); + res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]); + return res; + case 3 : // 3x3_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2]); + res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] + + (Ttdouble)_data[5]*img[2]); + res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] + + (Ttdouble)_data[8]*img[2]); + return res; + case 4 : // 4x4_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); + res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] + + (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]); + res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] + + (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]); + res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] + + (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]); + return res; + } + } else if (_height==_width) { + if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix + case 2 : // 2x2_matrix * 2x2_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]); + res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]); + res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]); + return res; + case 3 : // 3x3_matrix * 3x3_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] + + (Ttdouble)_data[2]*img[6]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] + + (Ttdouble)_data[2]*img[7]); + res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] + + (Ttdouble)_data[2]*img[8]); + res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] + + (Ttdouble)_data[5]*img[6]); + res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] + + (Ttdouble)_data[5]*img[7]); + res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] + + (Ttdouble)_data[5]*img[8]); + res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] + + (Ttdouble)_data[8]*img[6]); + res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] + + (Ttdouble)_data[8]*img[7]); + res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] + + (Ttdouble)_data[8]*img[8]); + return res; + case 4 : // 4x4_matrix * 4x4_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] + + (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] + + (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]); + res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] + + (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]); + res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] + + (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]); + res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] + + (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]); + res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] + + (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]); + res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] + + (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]); + res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] + + (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]); + res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] + + (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]); + res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] + + (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]); + res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] + + (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]); + res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] + + (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]); + res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] + + (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]); + res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] + + (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]); + res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] + + (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]); + res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] + + (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]); + return res; + } else switch (_width) { // Square_matrix * Matrix + case 2 : { // 2x2_matrix * Matrix + const t *const ps0 = img.data(), *const ps1 = img.data(0,1); + Tt *const pd0 = res.data(), *const pd1 = res.data(0,1); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], + a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i]; + pd0[i] = (Tt)(a0*x + a1*y); + pd1[i] = (Tt)(a2*x + a3*y); + } + return res; + } + case 3 : { // 3x3_matrix * Matrix + const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2); + Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], + a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], + a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i]; + pd0[i] = (Tt)(a0*x + a1*y + a2*z); + pd1[i] = (Tt)(a3*x + a4*y + a5*z); + pd2[i] = (Tt)(a6*x + a7*y + a8*z); + } + return res; + } + case 4 : { // 4x4_matrix * Matrix + const t + *const ps0 = img.data(), *const ps1 = img.data(0,1), + *const ps2 = img.data(0,2), *const ps3 = img.data(0,3); + Tt + *const pd0 = res.data(), *const pd1 = res.data(0,1), + *const pd2 = res.data(0,2), *const pd3 = res.data(0,3); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3], + a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], + a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11], + a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14], + a15 = (Ttdouble)_data[15]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i]; + pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c); + pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c); + pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c); + pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c); + } + return res; + } + } + } + + // Fallback to generic version. +#if cimg_use_openmp!=0 + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 && + img.size()>(cimg_openmp_sizefactor)*1024)) + cimg_forXY(res,i,j) { + Ttdouble value = 0; + cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); + res(i,j) = (Tt)value; + } +#else + Tt *ptrd = res._data; + cimg_forXY(res,i,j) { + Ttdouble value = 0; + cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); + *(ptrd++) = (Tt)value; + } +#endif + return res; + } + + //! In-place division operator. + /** + Similar to operator+=(const t), except that it performs a division instead of an addition. + **/ + template + CImg& operator/=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr / value,32768); + return *this; + } + + //! In-place division operator. + /** + Similar to operator+=(const char*), except that it performs a division instead of an addition. + **/ + CImg& operator/=(const char *const expression) { + return div((+*this)._fill(expression,true,1,0,0,"operator/=",this)); + } + + //! In-place division operator. + /** + Replace the image instance by the (right) matrix division between the image instance and the specified + matrix \c img. + \param img Second operand of the matrix division. + \note + - It does \e not compute a pointwise division between two images. For this purpose, use + div(const CImg&) instead. + - It returns the matrix operation \c A*inverse(img). + - The size of the image instance can be modified by this operator. + **/ + template + CImg& operator/=(const CImg& img) { + return (*this*img.get_invert()).move_to(*this); + } + + //! Division operator. + /** + Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const t value) const { + return CImg<_cimg_Tt>(*this,false)/=value; + } + + //! Division operator. + /** + Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator/(const char *const expression) const { + return CImg(*this,false)/=expression; + } + + //! Division operator. + /** + Similar to operator/=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const CImg& img) const { + return (*this)*img.get_invert(); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const t), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384); + return *this; + } + + //! In-place modulo operator. + /** + Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. + **/ + CImg& operator%=(const char *const expression) { + return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const CImg&), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this%=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> operator%(const t value) const { + return CImg<_cimg_Tt>(*this,false)%=value; + } + + //! Modulo operator. + /** + Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator%(const char *const expression) const { + return CImg(*this,false)%=expression; + } + + //! Modulo operator. + /** + Similar to operator%=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator%(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)%=img; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768); + return *this; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. + **/ + CImg& operator&=(const char *const expression) { + return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this); + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this&=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator&(const t value) const { + return (+*this)&=value; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator&(const char *const expression) const { + return (+*this)&=expression; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator&(const CImg& img) const { + return (+*this)&=img; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768); + return *this; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. + **/ + CImg& operator|=(const char *const expression) { + return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this); + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this|=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator|(const t value) const { + return (+*this)|=value; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator|(const char *const expression) const { + return (+*this)|=expression; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator|(const CImg& img) const { + return (+*this)|=img; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead. + **/ + template + CImg& operator^=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768); + return *this; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. + **/ + CImg& operator^=(const char *const expression) { + return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this); + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg&) instead. + **/ + template + CImg& operator^=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator^(const t value) const { + return (+*this)^=value; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator^(const char *const expression) const { + return (+*this)^=expression; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator^(const CImg& img) const { + return (+*this)^=img; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536); + return *this; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. + **/ + CImg& operator<<=(const char *const expression) { + return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this); + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator<<(const t value) const { + return (+*this)<<=value; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator<<(const char *const expression) const { + return (+*this)<<=expression; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const CImg&), except that it returns a new image instance instead of + operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator<<(const CImg& img) const { + return (+*this)<<=img; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536); + return *this; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. + **/ + CImg& operator>>=(const char *const expression) { + return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this); + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs> (int)*(ptrs++)); + for (const t *ptrs = img._data; ptrd> (int)*(ptrs++)); + } + return *this; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const t value) const { + return (+*this)>>=value; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator>>(const char *const expression) const { + return (+*this)>>=expression; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const CImg&), except that it returns a new image instance instead of + operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const CImg& img) const { + return (+*this)>>=img; + } + + //! Bitwise inversion operator. + /** + Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value. + **/ + CImg operator~() const { + CImg res(_width,_height,_depth,_spectrum); + const T *ptrs = _data; + cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; } + return res; + } + + //! Test if all pixels of an image have the same value. + /** + Return \c true is all pixels of the image instance are equal to the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator==(const t value) const { + if (is_empty()) return false; + typedef _cimg_Tt Tt; + bool is_equal = true; + for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {} + return is_equal; + } + + //! Test if all pixel values of an image follow a specified expression. + /** + Return \c true is all pixels of the image instance are equal to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator==(const char *const expression) const { + return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this); + } + + //! Test if two images have the same size and values. + /** + Return \c true if the image instance and the input image \c img have the same pixel values, + even if the dimensions of the two images do not match. It returns \c false otherwise. + \param img Input image to compare with. + \note + - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() + to return \c true. + Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different + pixel types \c T and \c t. + \par Example + \code + const CImg img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values) + const CImg img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values) + if (img1==img2) { // Test succeeds, image dimensions and values are the same + std::printf("'img1' and 'img2' have same dimensions and values."); + } + \endcode + **/ + template + bool operator==(const CImg& img) const { + typedef _cimg_Tt Tt; + const ulongT siz = size(); + bool is_equal = true; + if (siz!=img.size()) return false; + t *ptrs = img._data + siz; + for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {} + return is_equal; + } + + //! Test if pixels of an image are all different from a value. + /** + Return \c true is all pixels of the image instance are different than the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator!=(const t value) const { + return !((*this)==value); + } + + //! Test if all pixel values of an image are different from a specified expression. + /** + Return \c true is all pixels of the image instance are different to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator!=(const char *const expression) const { + return !((*this)==expression); + } + + //! Test if two images have different sizes or values. + /** + Return \c true if the image instance and the input image \c img have different dimensions or pixel values, + and \c false otherwise. + \param img Input image to compare with. + \note + - Writing \c img1!=img2 is equivalent to \c !(img1==img2). + **/ + template + bool operator!=(const CImg& img) const { + return !((*this)==img); + } + + //! Construct an image list from two images. + /** + Return a new list of image (\c CImgList instance) containing exactly two elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image \c img, at position [\c 1]. + + \param img Input image that will be the second image of the resulting list. + \note + - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow + in practice (see warning below). + - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are + inserted as new non-shared copies in the resulting list. + - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary. + \warning + - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list. + This may become very expensive in terms of speed and used memory. You should avoid using this technique to + build a new CImgList instance from several images, if you are seeking for performance. + Fast insertions of images in an image list are possible with + CImgList::insert(const CImg&,unsigned int,bool) or move_to(CImgList&,unsigned int). + \par Example + \code + const CImg + img1("reference.jpg"), + img2 = img1.get_mirror('x'), + img3 = img2.get_blur(5); + const CImgList list = (img1,img2); // Create list of two elements from 'img1' and 'img2' + (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3' + \endcode + \image html ref_operator_comma.jpg + **/ + template + CImgList<_cimg_Tt> operator,(const CImg& img) const { + return CImgList<_cimg_Tt>(*this,img); + } + + //! Construct an image list from image instance and an input image list. + /** + Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()]. + + \param list Input image list that will be appended to the image instance. + \note + - Similar to operator,(const CImg&) const, except that it takes an image list as an argument. + **/ + template + CImgList<_cimg_Tt> operator,(const CImgList& list) const { + return CImgList<_cimg_Tt>(list,false).insert(*this,0); + } + + //! Split image along specified axis. + /** + Return a new list of images (\c CImgList instance) containing the split components + of the instance image along the specified axis. + \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c') + \note + - Similar to get_split(char,int) const, with default second argument. + \par Example + \code + const CImg img("reference.jpg"); // Load a RGB color image + const CImgList list = (img<'c'); // Get a list of its three R,G,B channels + (img,list).display(); + \endcode + \image html ref_operator_less.jpg + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the number of image columns. + /** + Return the image width, i.e. the image dimension along the X-axis. + \note + - The width() of an empty image is equal to \c 0. + - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations. + - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._width. + **/ + int width() const { + return (int)_width; + } + + //! Return the number of image rows. + /** + Return the image height, i.e. the image dimension along the Y-axis. + \note + - The height() of an empty image is equal to \c 0. + - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._height. + **/ + int height() const { + return (int)_height; + } + + //! Return the number of image slices. + /** + Return the image depth, i.e. the image dimension along the Z-axis. + \note + - The depth() of an empty image is equal to \c 0. + - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image + is said to be \e volumetric. + - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._depth. + **/ + int depth() const { + return (int)_depth; + } + + //! Return the number of image channels. + /** + Return the number of image channels, i.e. the image dimension along the C-axis. + \note + - The spectrum() of an empty image is equal to \c 0. + - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 + for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel). + The number of channels of an image instance is not limited. The meaning of the pixel values is not linked + up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image). + - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._spectrum. + **/ + int spectrum() const { + return (int)_spectrum; + } + + //! Return the total number of pixel values. + /** + Return width()*\ref height()*\ref depth()*\ref spectrum(), + i.e. the total number of values of type \c T in the pixel buffer of the image instance. + \note + - The size() of an empty image is equal to \c 0. + - The allocated memory size for a pixel buffer of a non-shared \c CImg instance is equal to + size()*sizeof(T). + \par Example + \code + const CImg img(100,100,1,3); // Construct new 100x100 color image + if (img.size()==30000) // Test succeeds + std::printf("Pixel buffer uses %lu bytes", + img.size()*sizeof(float)); + \endcode + **/ + ulongT size() const { + return (ulongT)_width*_height*_depth*_spectrum; + } + + //! Return a pointer to the first pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance, + whether the instance is \c const or not. + \note + - The data() of an empty image is equal to \c 0 (null pointer). + - The allocated pixel buffer for the image instance starts from \c data() + and goes to data()+\ref size() - 1 (included). + - To get the pointer to one particular location of the pixel buffer, use + data(unsigned int,unsigned int,unsigned int,unsigned int) instead. + **/ + T* data() { + return _data; + } + + //! Return a pointer to the first pixel value \const. + const T* data() const { + return _data; + } + + //! Return a pointer to a located pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer + of the image instance, + whether the instance is \c const or not. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)). Thus, this method has the same + properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + **/ +#if cimg_verbosity>=3 + T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + const ulongT off = (ulongT)offset(x,y,z,c); + if (off>=size()) + cimg::warn(_cimg_instance + "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].", + cimg_instance, + x,y,z,c,off); + return _data + off; + } + + //! Return a pointer to a located pixel value \const. + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->data(x,y,z,c); + } +#else + T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; + } + + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; + } +#endif + + //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)) - img.data(). + Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + \par Example + \code + const CImg img(100,100,1,3); // Define a 100x100 RGB-color image + const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10) + const float val = img[off]; // Get the blue value of this pixel + \endcode + **/ + longT offset(const int x, const int y=0, const int z=0, const int c=0) const { + return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth; + } + + //! Return a CImg::iterator pointing to the first pixel value. + /** + \note + - Equivalent to data(). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + iterator begin() { + return _data; + } + + //! Return a CImg::iterator pointing to the first value of the pixel buffer \const. + const_iterator begin() const { + return _data; + } + + //! Return a CImg::iterator pointing next to the last pixel value. + /** + \note + - Writing \c img.end() is equivalent to img.data() + img.size(). + - It has been mainly defined for compatibility with STL naming conventions. + \warning + - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. + Trying to read or write the content of the returned iterator will probably result in a crash. + Use it mainly as a strict upper bound for a CImg::iterator. + \par Example + \code + CImg img(100,100,1,3); // Define a 100x100 RGB color image + // 'img.end()' used below as an upper bound for the iterator. + for (CImg::iterator it = img.begin(); it::iterator pointing next to the last pixel value \const. + const_iterator end() const { + return _data + size(); + } + + //! Return a reference to the first pixel value. + /** + \note + - Writing \c img.front() is equivalent to img[0], or img(0,0,0,0). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& front() { + return *_data; + } + + //! Return a reference to the first pixel value \const. + const T& front() const { + return *_data; + } + + //! Return a reference to the last pixel value. + /** + \note + - Writing \c img.back() is equivalent to img[img.size() - 1], or + img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& back() { + return *(_data + size() - 1); + } + + //! Return a reference to the last pixel value \const. + const T& back() const { + return *(_data + size() - 1); + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to a specified default value in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note + - Writing \c img.at(offset,out_value) is similar to img[offset], except that if \c offset + is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value + is safely returned instead. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + **/ + T& at(const int offset, const T& out_value) { + return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const. + T at(const int offset, const T& out_value) const { + return (offset<0 || offset>=(int)size())?out_value:(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to the nearest pixel location in the image instance in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \note + - Similar to at(int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified offset, i.e. + - If \c offset<0, then \c img[0] is returned. + - If \c offset>=img.size(), then \c img[img.size() - 1] is returned. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int). + **/ + T& at(const int offset) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + T& _at(const int offset) { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const. + const T& at(const int offset) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + const T& _at(const int offset) const { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to a specified default value in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value + \c out_value. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const. + T atX(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || x>=width())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified X-coordinate. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _at(int,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + T& _atX(const int x, const int y=0, const int z=0, const int c=0) { + return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const. + const T& atX(const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const { + return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates. + **/ + T& atXY(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const. + T atXY(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXY(int,int,int,int). + **/ + T& atXY(const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + T& _atXY(const int x, const int y, const int z=0, const int c=0) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1),z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const. + const T& atXY(const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + const T& _atXY(const int x, const int y, const int z=0, const int c=0) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1),z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on + X,Y and Z-coordinates. + **/ + T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const. + T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXYZ(int,int,int,int). + **/ + T& atXYZ(const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + T& _atXYZ(const int x, const int y, const int z, const int c=0) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1),c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const. + const T& atXYZ(const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + const T& _atXYZ(const int x, const int y, const int z, const int c=0) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1),c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all + X,Y,Z and C-coordinates. + **/ + T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions \const. + T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value: + (*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXYZC(int,int,int,int). + **/ + T& atXYZC(const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + T& _atXYZC(const int x, const int y, const int z, const int c) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1), + cimg::cut(c,0,spectrum() - 1)); + } + + //! Access to a pixel value, using Neumann boundary conditions \const. + const T& atXYZC(const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + const T& _atXYZC(const int x, const int y, const int z, const int c) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1), + cimg::cut(c,0,spectrum() - 1)); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by + a linear interpolation along the X-axis, if corresponding coordinates are not integers. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1; + const float + dx = fx - x; + const Tfloat + Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access along + the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns + the value of the nearest pixel in the image instance, regarding the specified X-coordinate. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX(): Empty instance.", + cimg_instance); + + return _linear_atX(fx,y,z,c); + } + + Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx - x; + const unsigned int + nx = dx>0?x + 1:x; + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate. + Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX_p(): Empty instance.", + cimg_instance); + + return _linear_atX_p(fx,y,z,c); + } + + Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx - x; + const unsigned int + nx = cimg::mod(x + 1,_width); + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved both for X and Y-coordinates. + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1; + const float + dx = fx - x, + dy = fy - y; + const Tfloat + Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), + Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXY(float,float,int,int). + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY(): Empty instance.", + cimg_instance); + + return _linear_atXY(fx,fy,z,c); + } + + Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx - x, + dy = nfy - y; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y; + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates. + Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY_p(): Empty instance.", + cimg_instance); + + return _linear_atXY_p(fx,fy,z,c); + } + + Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx - x, + dy = nfy - y; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height); + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved both for X,Y and Z-coordinates. + **/ + Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z; + const Tfloat + Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), + Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), + Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), + Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXYZ(float,float,float,int). + **/ + Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ(): Empty instance.", + cimg_instance); + + return _linear_atXYZ(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = cimg::cut(fz,0,depth() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y, + nz = dz>0?z + 1:z; + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates. + Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ_p(): Empty instance.", + cimg_instance); + + return _linear_atXYZ_p(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f), + nfz = cimg::mod(fz,_depth - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height), + nz = cimg::mod(z + 1,_depth); + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved for all X,Y,Z and C-coordinates. + **/ + Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1, + c = (int)fc - (fc>=0?0:1), nc = c + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z, + dc = fc - c; + const Tfloat + Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value), + Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value), + Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value), + Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value), + Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value), + Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value), + Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value), + Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn -Icccc); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved for all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXYZC(float,float,float,float). + **/ + Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC(): Empty instance.", + cimg_instance); + + return _linear_atXYZC(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = cimg::cut(fz,0,depth() - 1), + nfc = cimg::cut(fc,0,spectrum() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y, + nz = dz>0?z + 1:z, + nc = dc>0?c + 1:c; + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn - Icccc); + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates. + Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC_p(): Empty instance.", + cimg_instance); + + return _linear_atXYZC_p(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f), + nfz = cimg::mod(fz,_depth - 0.5f), + nfc = cimg::mod(fc,_spectrum - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height), + nz = cimg::mod(z + 1,_depth), + nc = cimg::mod(c + 1,_spectrum); + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn - Icccc); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + The cubic interpolation uses Hermite splines. + \param fx d X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is + approximated by a \e cubic interpolation along the X-axis. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2; + const float + dx = fx - x; + const Tfloat + Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value), + In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atX(fx,y,z,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access + along the X-axis. The cubic interpolation uses Hermite splines. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is + approximated by a cubic interpolation along the X-axis. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX(): Empty instance.", + cimg_instance); + return _cubic_atX(fx,y,z,c); + } + + Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1); + const int + x = (int)nfx; + const float + dx = nfx - x; + const int + px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2; + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atX_c(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX(fx,y,z,c)); + } + + T _cubic_atX_c(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX(fx,y,z,c)); + } + + //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate. + Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX_p(): Empty instance.", + cimg_instance); + return _cubic_atX_p(fx,y,z,c); + } + + Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f); + const int + x = (int)nfx; + const float + dx = nfx - x; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()); + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atX_pc(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX_p(fx,y,z,c)); + } + + T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX_p(fx,y,z,c)); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X and Y-coordinates. + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, + y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2; + const float dx = fx - x, dy = fy - y; + const Tfloat + Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), + Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value), + Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), + Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), + Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved for both X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXY(float,float,int,int). + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY(): Empty instance.", + cimg_instance); + return _cubic_atXY(fx,fy,z,c); + } + + Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), + nfy = cimg::type::is_nan(fy)?0:cimg::cut(fy,0,height() - 1); + const int x = (int)nfx, y = (int)nfy; + const float dx = nfx - x, dy = nfy - y; + const int + px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2, + py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2; + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), + Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), + Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), + Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c)); + } + + T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY(fx,fy,z,c)); + } + + //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates. + Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY_p(): Empty instance.", + cimg_instance); + return _cubic_atXY_p(fx,fy,z,c); + } + + Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f), + nfy = cimg::type::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f); + const int x = (int)nfx, y = (int)nfy; + const float dx = nfx - x, dy = nfy - y; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()), + py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()); + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), + Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), + Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), + Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY_p(fx,fy,z,c)); + } + + T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY_p(fx,fy,z,c)); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, + y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2, + z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2; + const float dx = fx - x, dy = fy - y, dz = fz - z; + const Tfloat + Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value), + Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value), + Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value), + Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value), + Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value), + Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value), + Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), + Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value), + Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value), + Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value), + Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), + Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value), + Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value), + Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value), + Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value), + Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value), + Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay + in the min/max range of the datatype \c T. + **/ + T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ(): Empty instance.", + cimg_instance); + return _cubic_atXYZ(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), + nfy = cimg::type::is_nan(fy)?0:cimg::cut(fy,0,height() - 1), + nfz = cimg::type::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1); + const int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2, + py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2, + pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2; + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c)); + } + + T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ(fx,fy,fz,c)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ_p(): Empty instance.", + cimg_instance); + return _cubic_atXYZ_p(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f), + nfy = cimg::type::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f), + nfz = cimg::type::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f); + const int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()), + py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()), + pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth()); + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ_p(fx,fy,fz,c)); + } + + T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ_p(fx,fy,fz,c)); + } + + //! Set pixel value, using linear interpolation for the X-coordinates. + /** + Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that + the value is spread amongst several neighbors if the pixel coordinates are float-valued. + \param value Pixel value to set. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image + pixel(s). + \return A reference to the current image instance. + \note + - Calling this method with out-of-bounds coordinates does nothing. + **/ + CImg& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1; + const float + dx = fx - x; + if (y>=0 && y=0 && z=0 && c=0 && x=0 && nx& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1; + const float + dx = fx - x, + dy = fy - y; + if (z>=0 && z=0 && c=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z; + if (c>=0 && c=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx image whose buffer data() is a \c char* string describing the list of all pixel values + of the image instance (written in base 10), separated by specified \c separator character. + \param separator A \c char character which specifies the separator between values in the returned C-string. + \param max_size Maximum size of the returned image (or \c 0 if no limits are set). + \param format For float/double-values, tell the printf format used to generate the text representation + of the numbers (or \c 0 for default representation). + \note + - The returned image is never empty. + - For an empty image instance, the returned string is "". + - If \c max_size is equal to \c 0, there are no limits on the size of the returned string. + - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off + and terminated by character \c '\0'. In that case, the returned image size is max_size + 1. + **/ + CImg value_string(const char separator=',', const unsigned int max_size=0, + const char *const format=0) const { + if (is_empty() || max_size==1) return CImg(1,1,1,1,0); + CImgList items; + CImg s_item(256); *s_item = 0; + const T *ptrs = _data; + unsigned int string_size = 0; + const char *const _format = format?format:cimg::type::format(); + for (ulongT off = 0, siz = size(); off::format(*(ptrs++))); + CImg item(s_item._data,printed_size); + item[printed_size - 1] = separator; + item.move_to(items); + if (max_size) string_size+=printed_size; + } + CImg res; + (items>'x').move_to(res); + if (max_size && res._width>=max_size) res.crop(0,max_size - 1); + res.back() = 0; + return res; + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Test shared state of the pixel buffer. + /** + Return \c true if image instance has a shared memory buffer, and \c false otherwise. + \note + - A shared image do not own his pixel buffer data() and will not deallocate it on destruction. + - Most of the time, a \c CImg image instance will \e not be shared. + - A shared image can only be obtained by a limited set of constructors and methods (see list below). + **/ + bool is_shared() const { + return _is_shared; + } + + //! Test if image instance is empty. + /** + Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions + \c 0 x \c 0 x \c 0 x \c 0 and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise. + **/ + bool is_empty() const { + return !(_data && _width && _height && _depth && _spectrum); + } + + //! Test if image instance contains a 'inf' value. + /** + Return \c true, if image instance contains a 'inf' value, and \c false otherwise. + **/ + bool is_inf() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_inf((float)*p)) return true; + return false; + } + + //! Test if image instance contains a NaN value. + /** + Return \c true, if image instance contains a NaN value, and \c false otherwise. + **/ + bool is_nan() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_nan((float)*p)) return true; + return false; + } + + //! Test if image width is equal to specified value. + bool is_sameX(const unsigned int size_x) const { + return _width==size_x; + } + + //! Test if image width is equal to specified value. + template + bool is_sameX(const CImg& img) const { + return is_sameX(img._width); + } + + //! Test if image width is equal to specified value. + bool is_sameX(const CImgDisplay& disp) const { + return is_sameX(disp._width); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const unsigned int size_y) const { + return _height==size_y; + } + + //! Test if image height is equal to specified value. + template + bool is_sameY(const CImg& img) const { + return is_sameY(img._height); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const CImgDisplay& disp) const { + return is_sameY(disp._height); + } + + //! Test if image depth is equal to specified value. + bool is_sameZ(const unsigned int size_z) const { + return _depth==size_z; + } + + //! Test if image depth is equal to specified value. + template + bool is_sameZ(const CImg& img) const { + return is_sameZ(img._depth); + } + + //! Test if image spectrum is equal to specified value. + bool is_sameC(const unsigned int size_c) const { + return _spectrum==size_c; + } + + //! Test if image spectrum is equal to specified value. + template + bool is_sameC(const CImg& img) const { + return is_sameC(img._spectrum); + } + + //! Test if image width and height are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified. + **/ + bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const { + return _width==size_x && _height==size_y; + } + + //! Test if image width and height are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameY(const CImg&) const are both verified. + **/ + template + bool is_sameXY(const CImg& img) const { + return is_sameXY(img._width,img._height); + } + + //! Test if image width and height are the same as that of an existing display window. + /** + Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified. + **/ + bool is_sameXY(const CImgDisplay& disp) const { + return is_sameXY(disp._width,disp._height); + } + + //! Test if image width and depth are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const { + return _width==size_x && _depth==size_z; + } + + //! Test if image width and depth are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXZ(const CImg& img) const { + return is_sameXZ(img._width,img._depth); + } + + //! Test if image width and spectrum are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const { + return _width==size_x && _spectrum==size_c; + } + + //! Test if image width and spectrum are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXC(const CImg& img) const { + return is_sameXC(img._width,img._spectrum); + } + + //! Test if image height and depth are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const { + return _height==size_y && _depth==size_z; + } + + //! Test if image height and depth are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameYZ(const CImg& img) const { + return is_sameYZ(img._height,img._depth); + } + + //! Test if image height and spectrum are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const { + return _height==size_y && _spectrum==size_c; + } + + //! Test if image height and spectrum are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYC(const CImg& img) const { + return is_sameYC(img._height,img._spectrum); + } + + //! Test if image depth and spectrum are equal to specified values. + /** + Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const { + return _depth==size_z && _spectrum==size_c; + } + + //! Test if image depth and spectrum are the same as that of another image. + /** + Test if is_sameZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameZC(const CImg& img) const { + return is_sameZC(img._depth,img._spectrum); + } + + //! Test if image width, height and depth are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const { + return is_sameXY(size_x,size_y) && _depth==size_z; + } + + //! Test if image width, height and depth are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXYZ(const CImg& img) const { + return is_sameXYZ(img._width,img._height,img._depth); + } + + //! Test if image width, height and spectrum are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const { + return is_sameXY(size_x,size_y) && _spectrum==size_c; + } + + //! Test if image width, height and spectrum are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYC(const CImg& img) const { + return is_sameXYC(img._width,img._height,img._spectrum); + } + + //! Test if image width, depth and spectrum are equal to specified values. + /** + Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const { + return is_sameXZ(size_x,size_z) && _spectrum==size_c; + } + + //! Test if image width, depth and spectrum are the same as that of another image. + /** + Test if is_sameXZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXZC(const CImg& img) const { + return is_sameXZC(img._width,img._depth,img._spectrum); + } + + //! Test if image height, depth and spectrum are equal to specified values. + /** + Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const { + return is_sameYZ(size_y,size_z) && _spectrum==size_c; + } + + //! Test if image height, depth and spectrum are the same as that of another image. + /** + Test if is_sameYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYZC(const CImg& img) const { + return is_sameYZC(img._height,img._depth,img._spectrum); + } + + //! Test if image width, height, depth and spectrum are equal to specified values. + /** + Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both + verified. + **/ + bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c) const { + return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c; + } + + //! Test if image width, height, depth and spectrum are the same as that of another image. + /** + Test if is_sameXYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYZC(const CImg& img) const { + return is_sameXYZC(img._width,img._height,img._depth,img._spectrum); + } + + //! Test if specified coordinates are inside image bounds. + /** + Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance, + and \c false otherwise. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Return \c true only if all these conditions are verified: + - The image instance is \e not empty. + - 0<=x<=\ref width() - 1. + - 0<=y<=\ref height() - 1. + - 0<=z<=\ref depth() - 1. + - 0<=c<=\ref spectrum() - 1. + **/ + bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const { + return !is_empty() && x>=0 && x=0 && y=0 && z=0 && c img(100,100,1,3); // Construct a 100x100 RGB color image + const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0) + unsigned int x,y,z,c; + if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates + std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n", + offset,x,y,z,c); + } + \endcode + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z, t& c) const { + const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = (ulongT)(ppixel - _data); + const ulongT nc = off/whd; + off%=whd; + const ulongT nz = off/wh; + off%=wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc; + return true; + } + + //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z) const { + const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((ulongT)(ppixel - _data))%whd; + const ulongT nz = off/wh; + off%=wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; z = (t)nz; + return true; + } + + //! Test if pixel value is inside image bounds and get its X and Y-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y) const { + const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((unsigned int)(ppixel - _data))%wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; + return true; + } + + //! Test if pixel value is inside image bounds and get its X-coordinate. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set. + **/ + template + bool contains(const T& pixel, t& x) const { + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false; + x = (t)(((ulongT)(ppixel - _data))%_width); + return true; + } + + //! Test if pixel value is inside image bounds. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set. + **/ + bool contains(const T& pixel) const { + const T *const ppixel = &pixel; + return !is_empty() && ppixel>=_data && ppixel<_data + size(); + } + + //! Test if pixel buffers of instance and input images overlap. + /** + Return \c true, if pixel buffers attached to image instance and input image \c img overlap, + and \c false otherwise. + \param img Input image to compare with. + \note + - Buffer overlapping may happen when manipulating \e shared images. + - If two image buffers overlap, operating on one of the image will probably modify the other one. + - Most of the time, \c CImg instances are \e non-shared and do not overlap between each others. + \par Example + \code + const CImg + img1("reference.jpg"), // Load RGB-color image + img2 = img1.get_shared_channel(1); // Get shared version of the green channel + if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps + std::printf("Buffers overlap!\n"); + } + \endcode + **/ + template + bool is_overlapped(const CImg& img) const { + const ulongT csiz = size(), isiz = img.size(); + return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz)); + } + + //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object. + /** + Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a + valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image. + \param primitives List of primitives of the 3D object. + \param colors List of colors of the 3D object. + \param opacities List (or image) of opacities of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of + each 3D object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + template + bool is_object3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true, + char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check consistency for the particular case of an empty 3D object. + if (is_empty()) { + if (primitives || colors || opacities) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines no vertices but %u primitives, " + "%u colors and %lu opacities", + _width,primitives._width,primitives._width, + colors._width,(unsigned long)opacities.size()); + return false; + } + return true; + } + + // Check consistency of vertices. + if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)", + _width,primitives._width,_width,_height,_depth,_spectrum); + return false; + } + if (colors._width>primitives._width + 1) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines %u colors", + _width,primitives._width,colors._width); + return false; + } + if (opacities.size()>primitives._width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines %lu opacities", + _width,primitives._width,(unsigned long)opacities.size()); + return false; + } + if (!full_check) return true; + + // Check consistency of primitives. + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + const unsigned int psiz = (unsigned int)primitive.size(); + switch (psiz) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)primitive(0); + if (i0>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex index %u in " + "point primitive [%u]", + _width,primitives._width,i0,l); + return false; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + if (i0>=_width || i1>=_width || i2>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + _width,primitives._width,i0,i1,i2,l); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = (unsigned int)primitive(3); + if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + _width,primitives._width,i0,i1,i2,i3,l); + return false; + } + } break; + default : + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines an invalid primitive [%u] of size %u", + _width,primitives._width,l,(unsigned int)psiz); + return false; + } + } + + // Check consistency of colors. + cimglist_for(colors,c) { + const CImg& color = colors[c]; + if (!color) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines no color for primitive [%u]", + _width,primitives._width,c); + return false; + } + } + + // Check consistency of light texture. + if (colors._width>primitives._width) { + const CImg &light = colors.back(); + if (!light || light._depth>1) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", + _width,primitives._width,light._width, + light._height,light._depth,light._spectrum); + return false; + } + } + + return true; + } + + //! Test if image instance represents a valid serialization of a 3D object. + /** + Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise. + \param full_check Tells if full checking of the instance must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of + each 3D object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + bool is_CImg3d(const bool full_check=true, char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check instance dimension and header. + if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { + if (error_message) cimg_sprintf(error_message, + "CImg3d has invalid dimensions (%u,%u,%u,%u)", + _width,_height,_depth,_spectrum); + return false; + } + const T *ptrs = _data, *const ptre = end(); + if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || + !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { + if (error_message) cimg_sprintf(error_message, + "CImg3d header not found"); + return false; + } + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + + // Check consistency of number of vertices / primitives. + if (!full_check) { + const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives; + if (_data + minimal_size>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", + nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); + return false; + } + } + + // Check consistency of vertex data. + if (!nb_points) { + if (nb_primitives) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no vertices but %u primitives", + nb_points,nb_primitives,nb_primitives); + return false; + } + if (ptrs!=ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) is an empty object but contains %u value%s " + "more than expected", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); + return false; + } + return true; + } + if (ptrs + 3*nb_points>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines only %u vertices data", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); + return false; + } + ptrs+=3*nb_points; + + // Check consistency of primitive data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines %u vertices but no primitive", + nb_points,nb_primitives,nb_points); + return false; + } + + if (!full_check) return true; + + for (unsigned int p = 0; p=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]", + nb_points,nb_primitives,i0,p); + return false; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + ptrs+=3; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==6) ptrs+=4; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==9) ptrs+=6; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,p); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)), + i3 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==12) ptrs+=8; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,i3,p); + return false; + } + } break; + default : + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", + nb_points,nb_primitives,p,nb_inds); + return false; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); + return false; + } + } + + // Check consistency of color data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no color/texture data", + nb_points,nb_primitives); + return false; + } + for (unsigned int c = 0; c=c) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,c); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); + return false; + } + } + + // Check consistency of opacity data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no opacity data", + nb_points,nb_primitives); + return false; + } + for (unsigned int o = 0; o=o) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared opacity index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,o); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", + nb_points,nb_primitives,o); + return false; + } + } + + // Check end of data. + if (ptrs1?"s":""); + return false; + } + return true; + } + + static bool _is_CImg3d(const T val, const char c) { + return val>=(T)c && val<(T)(c + 1); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + // Define the math formula parser/compiler and expression evaluator. + struct _cimg_math_parser { + CImg mem; + CImg memtype, memmerge; + CImgList _code, &code, code_begin, code_end, + _code_begin_t, &code_begin_t, _code_end_t, &code_end_t; + CImg opcode; + const CImg *p_code_end, *p_code; + const CImg *const p_break; + + CImg expr, pexpr; + const CImg& imgin; + const CImgList& listin; + CImg &imgout; + CImgList& listout; + + CImg _img_stats, &img_stats, constcache_vals; + CImgList _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm; + CImg mem_img_stats, constcache_inds; + + CImg level, variable_pos, reserved_label; + CImgList variable_def, macro_def, macro_body; + CImgList macro_body_is_string; + char *user_macro; + + unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type, + constcache_size; + bool is_parallelizable, is_end_code, is_fill, return_new_comp, need_input_copy; + double *result; + cimg_uint64 rng; + const char *const calling_function, *s_op, *ss_op; + typedef double (*mp_func)(_cimg_math_parser&); + +#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value? +#define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value? +#define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? +#define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)? +#define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? +#define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN) +#define _cimg_mp_calling_function s_calling_function()._data +#define _cimg_mp_op(s) s_op = s; ss_op = ss +#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char) +#define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char) +#define _cimg_mp_check_constant_index(arg) check_constant_index(arg,ss,se,saved_char) +#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) +#define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char) +#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) +#define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; } +#define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan) +#define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val))) +#define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) +#define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) +#define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) +#define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3)) +#define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4)) +#define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5)) +#define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6)) +#define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7)) +#define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1)) +#define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2)) +#define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) +#define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) +#define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) +#define _cimg_mp_strerr \ + *se = saved_char; \ + for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \ + if (*s0==';') ++s0; \ + while (cimg::is_blank(*s0)) ++s0; \ + cimg::strellipsize(s0,64) + + // Constructors / Destructors. + ~_cimg_math_parser() { + cimg::srand(rng); + } + + _cimg_math_parser(const char *const expression, const char *const funcname=0, + const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0, + const bool _is_fill=false): + code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), + p_break((CImg*)(cimg_ulong)-2), + imgin(img_input),listin(list_inputs?*list_inputs:CImgList::const_empty()), + imgout(img_output?*img_output:CImg::empty()),listout(list_outputs?*list_outputs:CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0), + mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0), + constcache_size(0),is_parallelizable(true),is_fill(_is_fill),need_input_copy(false), + rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") { + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + if (!expression || !*expression) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Empty expression.", + pixel_type(),_cimg_mp_calling_function); + const char *_expression = expression; + while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression; + CImg::string(_expression).move_to(expr); + char *ps = &expr.back() - 1; + while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps; + *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1); + + // Ease the retrieval of previous non-space characters afterwards. + pexpr.assign(expr._width); + char c, *pe = pexpr._data; + for (ps = expr._data, c = ' '; *ps; ++ps) { + if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' '; + *(pe++) = c; + } + *pe = 0; + level = get_level(expr); + + // Init constant values. +#define _cimg_mp_interpolation (reserved_label[30]!=~0U?reserved_label[30]:0) +#define _cimg_mp_boundary (reserved_label[31]!=~0U?reserved_label[31]:0) +#define _cimg_mp_slot_t 17 +#define _cimg_mp_slot_nan 29 +#define _cimg_mp_slot_x 30 +#define _cimg_mp_slot_y 31 +#define _cimg_mp_slot_z 32 +#define _cimg_mp_slot_c 33 + + mem.assign(96); + for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10 + for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5 + mem[16] = 0.5; + mem[_cimg_mp_slot_t] = 0; // thread_id + mem[18] = (double)imgin._width; // w + mem[19] = (double)imgin._height; // h + mem[20] = (double)imgin._depth; // d + mem[21] = (double)imgin._spectrum; // s + mem[22] = (double)imgin._is_shared; // r + mem[23] = (double)imgin._width*imgin._height; // wh + mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd + mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds + mem[26] = (double)listin._width; // l + mem[27] = std::exp(1.); // e + mem[28] = cimg::PI; // pi + mem[_cimg_mp_slot_nan] = cimg::type::nan(); // nan + + // Set value property : + // { -1 = reserved (e.g. variable) | 0 = computation value | + // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }. + memtype.assign(mem._width,1,1,1,0); + for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1; + memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = + memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1; + mempos = _cimg_mp_slot_c + 1; + variable_pos.assign(8); + + reserved_label.assign(128,1,1,1,~0U); + // reserved_label[0-31] are used to store the memory index of these variables: + // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, + // [8] = is, [9] = ip, [10] = ic, [11] = in, [12] = xm, [13] = ym, [14] = zm, [15] = cm, [16] = xM, + // [17] = yM, [18] = zM, [19] = cM, [20] = i0...[29] = i9, [30] = interpolation, [31] = boundary + + // Compile expression into a sequence of opcodes. + s_op = ""; ss_op = expr._data; + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false); + if (!_cimg_mp_is_constant(ind_result)) { + if (_cimg_mp_is_vector(ind_result)) + CImg(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true). + fill(cimg::type::nan()); + else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type::nan(); + } + + // Free resources used for compiling expression and prepare evaluation. + result_dim = _cimg_mp_size(ind_result); + if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1); + result = mem._data + ind_result; + memtype.assign(); + constcache_vals.assign(); + constcache_inds.assign(); + level.assign(); + variable_pos.assign(); + reserved_label.assign(); + expr.assign(); + pexpr.assign(); + opcode.assign(); + opcode._is_shared = true; + + // Execute begin() bloc if any specified. + if (code_begin) { + mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_begin.end(); + for (p_code = code_begin; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + p_code_end = code.end(); + } + + _cimg_math_parser(): + code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), + p_code_end(0),p_break((CImg*)(cimg_ulong)-2), + imgin(CImg::const_empty()),listin(CImgList::const_empty()), + imgout(CImg::empty()),listout(CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0), + result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false), + need_input_copy(false),rng(0),calling_function(0) { + mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() + result = mem._data; + } + + _cimg_math_parser(const _cimg_math_parser& mp): + mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t), + p_code_end(mp.p_code_end),p_break(mp.p_break), + imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout), + img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm), + debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0), + is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill), + need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)), + rng((cimg::_rand(),cimg::rng())),calling_function(0) { + +#if cimg_use_openmp!=0 + mem[_cimg_mp_slot_t] = omp_get_thread_num(); + rng+=omp_get_thread_num(); +#endif + opcode.assign(); + opcode._is_shared = true; + } + + // Compilation procedure. + unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref, + const bool is_critical) { + if (depth>256) { + cimg::strellipsize(expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Call stack overflow (infinite recursion?), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + char c1, c2; + + // Simplify expression when possible. + do { + c2 = 0; + if (ssss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; + } + while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { + ++ss; --se; c2 = 1; + } + } while (c2 && ss::%s: %s%s Missing %s, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + *s_op=='F'?"argument":"item", + (ss_op - 4)>expr._data?"...":"", + (ss_op - 4)>expr._data?ss_op - 4:expr._data, + ss_op + std::strlen(ss_op)<&expr.back()?"...":""); + } + + static const size_t siz_ref = 7*sizeof(unsigned int); + const char *const previous_s_op = s_op, *const previous_ss_op = ss_op; + const unsigned int depth1 = depth + 1; + unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; + char + *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, + *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4, + *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8, + *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; + double val = 0, val1, val2; + mp_func op; + return_new_comp = false; + + // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value + // linked to the returned memory slot (reference that cannot be determined at compile time). + // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | + // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | + // 5 = image value as a vector (coordinates) }. + // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: + // When p_ref[0]==0, p_ref is actually unlinked. + // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ]. + // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ]. + // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ]. + if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; } + + const char saved_char = *se; *se = 0; + const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; + bool is_sth, is_relative; + CImg ref; + CImg variable_name; + CImgList l_opcode; + + // Look for a single value or a pre-defined variable. + int nb = 0; + s = ss + (*ss=='+' || *ss=='-'?1:0); + if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf + is_sth = *ss=='-'; + if (!cimg::strcasecmp(s,"inf")) { val = cimg::type::inf(); nb = 1; } + else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); nb = 1; } + if (nb==1 && is_sth) val = -val; + } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number + is_sth = *ss=='-'; + if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) { + nb = 1; + val = (double)arg1; + if (is_sth) val = -val; + } + } + if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); + if (nb==1) _cimg_mp_constant(val); + if (nb==2 && sep=='%') _cimg_mp_constant(val/100); + + if (ss1==se) switch (*ss) { // One-char reserved variable + case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c); + case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20); + case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27); + case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19); + case 'k' : + if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']); + pos = get_mem_img_index(); + if (pos!=~0U) _cimg_mp_return(pos); + _cimg_mp_return_nan(); + case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26); + case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22); + case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21); + case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t); + case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18); + case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x); + case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y); + case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z); + case 'u' : + if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']); + _cimg_mp_scalar2(mp_u,0,1); + case 'g' : + if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']); + _cimg_mp_scalar0(mp_g); + case 'i' : + if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']); + _cimg_mp_scalar0(mp_i); + case 'I' : + _cimg_mp_op("Variable 'I'"); + if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']); + if (!imgin._spectrum) _cimg_mp_return(0); + need_input_copy = true; + pos = vector(imgin._spectrum); + CImg::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + case 'R' : + if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0); + case 'G' : + if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0); + case 'B' : + if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0); + case 'A' : + if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0); + } + else if (ss2==se) { // Two-chars reserved variable + arg1 = arg2 = ~0U; + if (*ss=='w' && *ss1=='h') // wh + _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23); + if (*ss=='p' && *ss1=='i') // pi + _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28); + if (*ss=='i') { + if (*ss1>='0' && *ss1<='9') { // i0...i9 + pos = 20 + *ss1 - '0'; + if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0); + } + switch (*ss1) { + case 'm' : arg1 = 4; arg2 = 0; break; // im + case 'M' : arg1 = 5; arg2 = 1; break; // iM + case 'a' : arg1 = 6; arg2 = 2; break; // ia + case 'v' : arg1 = 7; arg2 = 3; break; // iv + case 's' : arg1 = 8; arg2 = 12; break; // is + case 'p' : arg1 = 9; arg2 = 13; break; // ip + case 'c' : // ic + if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); + if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; + _cimg_mp_return(mem_img_median); + break; + case 'n' : // in + if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); + if (mem_img_norm==~0U) mem_img_norm = imgin?constant(imgin.magnitude()):0; + _cimg_mp_return(mem_img_norm); + } + } + else if (*ss1=='m') switch (*ss) { + case 'x' : arg1 = 12; arg2 = 4; break; // xm + case 'y' : arg1 = 13; arg2 = 5; break; // ym + case 'z' : arg1 = 14; arg2 = 6; break; // zm + case 'c' : arg1 = 15; arg2 = 7; break; // cm + } + else if (*ss1=='M') switch (*ss) { + case 'x' : arg1 = 16; arg2 = 8; break; // xM + case 'y' : arg1 = 17; arg2 = 9; break; // yM + case 'z' : arg1 = 18; arg2 = 10; break; // zM + case 'c' : arg1 = 19; arg2 = 11; break; // cM + } + if (arg1!=~0U) { + if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); + if (!img_stats) { + img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); + mem_img_stats.assign(1,14,1,1,~0U); + } + if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); + _cimg_mp_return(mem_img_stats[arg2]); + } + } else if (ss3==se) { // Three-chars reserved variable + if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd + _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24); + } else if (ss4==se) { // Four-chars reserved variable + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds + _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25); + } + + pos = ~0U; + is_sth = false; + for (s0 = ss, s = ss1; s='i'?1:3,0); + if (_cimg_mp_is_vector(arg2)) { + if (p1!=~0U) { + _cimg_mp_check_constant_index(p1); + p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + p2 = listin[p3]._spectrum; + } else p2 = imgin._spectrum; + if (!p2) _cimg_mp_return(0); + _cimg_mp_check_type(arg2,2,2,p2); + } else p2 = 0; + + if (p_ref) { + *p_ref = _cimg_mp_is_vector(arg2)?4:2; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + if (_cimg_mp_is_vector(arg2)) + set_reserved_vector(arg2); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1; + } + + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg1,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value + if (!is_critical) is_parallelizable = false; + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0='i'?1:3,0); + if (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + } else if (s1='i') + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg5,p1,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg5,p1,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg5); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg5,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg5,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); + } + _cimg_mp_return(arg5); + } + } + + // Assign vector value (direct). + if (l_variable_name>3 && *ve1==']' && *ss!='[') { + s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss && is_varname(ss,s0 - ss)) { + variable_name[s0 - ss] = 0; // Remove brackets in variable name + get_variable_pos(variable_name,arg1,arg2); + arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot + if (arg1==~0U || _cimg_mp_is_scalar(arg1)) + compile(ss,s0,depth1,0,is_critical); // Variable does not exist or is not a vector -> error + + arg2 = compile(++s0,ve1,depth1,0,is_critical); // Index + arg3 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + _cimg_mp_check_type(arg3,2,1,0); + + if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) { + arg1+=nb + 1; + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + _cimg_mp_return(arg1); + } + compile(ss,s,depth1,0,is_critical); // Out-of-bounds reference -> error + } + + // Case of non-constant index -> return assigned value + linked reference + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + } + CImg::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg3); + } + } + + // Assign user-defined macro. + if (l_variable_name>2 && *ve1==')' && *ss!='(') { + s0 = ve1; while (s0>ss && *s0!='(') --s0; + if (is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) && + std::strncmp(variable_name,"print(",6)) { // Valid macro name + s0 = variable_name._data + (s0 - ss); + *s0 = 0; + s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis + CImg(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0); + ++s; while (*s && cimg::is_blank(*s)) ++s; + CImg(s,(unsigned int)(se - s + 1)).move_to(macro_body,0); + + p1 = 1; // Index of current parsed argument + for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments + if (p1>24) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro " + "definition '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && p1==1) break; // Function has no arguments + + s2 = s; // Start of the argument name + is_sth = true; // is_valid_argument_name? + if (*s>='0' && *s<='9') is_sth = false; + else for (ns = s; ns::%s: %s: %s name specified for argument %u when defining " + "macro '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + is_sth?"Empty":"Invalid",p1, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (ns==s1 || *ns==',') { // New argument found + *s3 = 0; + p2 = (unsigned int)(s3 - s2); // Argument length + for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number + if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) || + (ps + p2macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign + *(ps - 1) = (char)p1; + if (ps + p26 && !std::strncmp(variable_name,"const ",6); + s0 = variable_name._data; + if (is_const) { + s0+=6; while (cimg::is_blank(*s0)) ++s0; + variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1); + } + if (is_varname(variable_name)) { // Valid variable name + + // Assign variable (direct). + get_variable_pos(variable_name,arg1,arg2); + arg3 = compile(s + 1,se,depth1,0,is_critical); + is_sth = return_new_comp; // is arg3 a new blank object? + if (is_const) _cimg_mp_check_constant(arg3,2,0); + arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; + + if (arg1==~0U) { // Create new variable + if (_cimg_mp_is_vector(arg3)) { // Vector variable + arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3); + set_reserved_vector(arg1); // Prevent from being used in further optimization + } else { // Scalar variable + if (is_const) arg1 = arg3; + else { + arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3); + memtype[arg1] = -1; + } + } + + if (arg2!=~0U) reserved_label[arg2] = arg1; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg1; + variable_name.move_to(variable_def); + } + + } else { // Variable already exists -> assign a new value + if (is_const || _cimg_mp_is_constant(arg1)) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"already-defined ":"non-", + variable_name._data, + !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1)) { // Vector + if (_cimg_mp_is_vector(arg3)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3). + move_to(code); + } else // Scalar + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + } + return_new_comp = false; + _cimg_mp_return(arg1); + } + + // Assign lvalue (variable name was not valid for a direct assignment). + arg1 = ~0U; + is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? + if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment + + if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { + ref.assign(7); + arg1 = compile(ss,s,depth1,ref,is_critical); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + + if (*ref==1) { // Vector value (scalar): V[k] = scalar + _cimg_mp_check_type(arg2,2,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(arg2); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg3).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg2,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg2,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg3).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code); + } + + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg3,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg2,p1,arg3,arg4,arg5).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); + } + + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg2,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar + _cimg_mp_check_type(arg2,2,1,0); + CImg::vector((ulongT)mp_copy,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + } + + // No assignment expressions match -> error + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++. + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps && + level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=) + _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"); + + ref.assign(7); + arg1 = compile(ss,ns,depth1,ref,is_critical); // Vector slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Right operand + _cimg_mp_check_type(arg1,1,2,2); + _cimg_mp_check_type(arg2,2,3,2); + if (_cimg_mp_is_vector(arg2)) { // Complex **= complex + if (*ps=='*') + CImg::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code); + else if (*ps=='/') + CImg::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code); + } else { // Complex **= scalar + if (*ps=='*') { + if (arg2==1) _cimg_mp_return(arg1); + self_vector_s(arg1,mp_self_mul,arg2); + } else if (*ps=='/') { + if (arg2==1) _cimg_mp_return(arg1); + self_vector_s(arg1,mp_self_div,arg2); + } else { + if (arg2==1) _cimg_mp_return(arg1); + CImg::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code); + } + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + + } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + + _cimg_mp_return(arg1); + } + + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || + *ps=='&' || *ps=='^' || *ps=='|' || + (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && + level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) + switch (*ps) { + case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break; + case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break; + case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break; + case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break; + case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break; + case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break; + case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break; + case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break; + case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break; + default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break; + } + s1 = *ps=='>' || *ps=='<'?ns:ps; + + ref.assign(7); + arg1 = compile(ss,s1,depth1,ref,is_critical); // Variable slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to apply + + // Check for particular case to be simplified. + if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1); + if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1); + + // Apply operator on a copy to prevent modifying a constant or a variable. + if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k] += scalar + _cimg_mp_check_type(arg2,2,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(arg1); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector + else self_vector_s(arg1,op,arg2); // Vector += scalar + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar + _cimg_mp_check_type(arg2,2,1,0); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + + variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; + cimg::strpare(variable_name,false,true); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + for (s = ss1; s::vector((ulongT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') + _cimg_mp_op("Operator '||'"); + arg1 = compile(ss,s,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,1,0); + if (arg1>0 && arg1<=16) _cimg_mp_return(1); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] || mem[arg2]); + if (!arg1) _cimg_mp_return(arg2); + pos = scalar(); + CImg::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') + _cimg_mp_op("Operator '&&'"); + arg1 = compile(ss,s,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,1,0); + if (!arg1) _cimg_mp_return(0); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] && mem[arg2]); + if (arg1>0 && arg1<=16) _cimg_mp_return(arg2); + pos = scalar(); + CImg::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se2; s>ss; --s) + if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') + _cimg_mp_op("Operator '|'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + if (!arg1) _cimg_mp_return(arg2); + _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]); + if (!arg2) _cimg_mp_return(arg1); + if (!arg1) _cimg_mp_return(arg2); + _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); + } + + for (s = se2; s>ss; --s) + if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') + _cimg_mp_op("Operator '&'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]); + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') + _cimg_mp_op("Operator '!='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + if (arg1==arg2) _cimg_mp_return(0); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); + if (p1 || p2) { + if (p1 && p2 && p1!=p2) _cimg_mp_return(1); + pos = scalar(); + CImg::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); + _cimg_mp_scalar2(mp_neq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') + _cimg_mp_op("Operator '=='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + if (arg1==arg2) _cimg_mp_return(1); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); + if (p1 || p2) { + if (p1 && p2 && p1!=p2) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); + _cimg_mp_scalar2(mp_eq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') + _cimg_mp_op("Operator '<='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); + if (arg1==arg2) _cimg_mp_return(1); + _cimg_mp_scalar2(mp_lte,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') + _cimg_mp_op("Operator '>='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); + if (arg1==arg2) _cimg_mp_return(1); + _cimg_mp_scalar2(mp_gte,arg1,arg2); + } + + for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) + if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') + _cimg_mp_op("Operator '<'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) + if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>') + _cimg_mp_op("Operator '>'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); + if (arg1==arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_gt,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') + _cimg_mp_op("Operator '<<'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]); + if (!arg1) _cimg_mp_return(0); + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') + _cimg_mp_op("Operator '>>'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]); + if (!arg1) _cimg_mp_return(0); + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Addition ('+') + _cimg_mp_op("Operator '+'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (!arg2) _cimg_mp_return(arg1); + if (!arg1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]); + if (code) { // Try to spot linear case 'a*b + c' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); + _cimg_mp_return(arg3); + } + } + if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1); + if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2); + _cimg_mp_scalar2(mp_add,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Subtraction ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (!arg2) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2); + _cimg_mp_vector2_sv(mp_sub,arg1,arg2); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]); + if (!arg1) _cimg_mp_scalar1(mp_minus,arg2); + if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right), + arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code); + _cimg_mp_return(arg3); + } + } + if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1); + _cimg_mp_scalar2(mp_sub,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**') + _cimg_mp_op("Operator '**'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + if (arg1==1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') + _cimg_mp_op("Operator '//'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (!arg1) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') + _cimg_mp_op("Operator '*'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + p2 = _cimg_mp_size(arg2); + if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication + pos = vector(p2); + CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (arg1==1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + + if (code) { // Try to spot double multiplication 'a*b*c' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); + _cimg_mp_return(arg3); + } + } + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') + _cimg_mp_op("Operator '/'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (!arg1) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2, ns = se1; s>ss; --s, --ns) + if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') + _cimg_mp_op("Operator '%'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); + _cimg_mp_scalar2(mp_modulo,arg1,arg2); + } + + if (se1>ss) { + if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) { // Unary plus ('+') + _cimg_mp_op("Operator '+'"); + _cimg_mp_return(compile(ss1,se,depth1,0,is_critical)); + } + + if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); + _cimg_mp_scalar1(mp_minus,arg1); + } + + if (*ss=='!') { // Logical not ('!') + _cimg_mp_op("Operator '!'"); + if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)' + arg1 = compile(ss2,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + _cimg_mp_scalar1(mp_bool,arg1); + } + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); + _cimg_mp_scalar1(mp_logical_not,arg1); + } + + if (*ss=='~') { // Bitwise not ('~') + _cimg_mp_op("Operator '~'"); + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]); + _cimg_mp_scalar1(mp_bitwise_not,arg1); + } + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') + _cimg_mp_op("Operator '^^'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + pos = vector(2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + CImg::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); + else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); + else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + CImg::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se2; s>ss; --s) + if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') + _cimg_mp_op("Operator '^'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + switch (arg2) { + case 0 : _cimg_mp_return(1); + case 2 : _cimg_mp_scalar1(mp_sqr,arg1); + case 3 : _cimg_mp_scalar1(mp_pow3,arg1); + case 4 : _cimg_mp_scalar1(mp_pow4,arg1); + default : + if (_cimg_mp_is_constant(arg2)) { + if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); } + else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); } + } + _cimg_mp_scalar2(mp_pow,arg1,arg2); + } + } + + // Percentage computation. + if (*se1=='%') { + arg1 = compile(ss,se1,depth1,0,is_critical); + arg2 = _cimg_mp_is_constant(arg1)?0:constant(100); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment + if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { + _cimg_mp_op("Operator '++'"); + op = mp_self_increment; + } else { + _cimg_mp_op("Operator '--'"); + op = mp_self_decrement; + } + ref.assign(7); + arg1 = is_sth?compile(ss2,se,depth1,ref,is_critical): + compile(ss,se2,depth1,ref,is_critical); // Variable slot + + // Apply operator on a copy to prevent modifying a constant or a variable. + if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (is_sth) pos = arg1; // Determine return index, depending on pre/post action + else { + if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1); + else pos = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k]++ + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1,1).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(pos); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++ + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + _cimg_mp_return(pos); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++ + CImg::vector((ulongT)op,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1)); + else variable_name.assign(ss,(unsigned int)(se1 - ss)); + variable_name.back() = 0; + cimg::strpare(variable_name,false,true); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'. + if (*se1==']') { + _cimg_mp_op("Value accessor '[]'"); + + // Find opening bracket for the offset. + s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } + is_sth=s0>ss && *(s0-1)==']'; // Particular case s.a. '..[..][..]' ? + is_relative = *ss=='j' || *ss=='J'; + + if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' && + (reserved_label[(int)*ss]==~0U || + !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff), + pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); + } else { + need_input_copy = true; + CImg::vector((ulongT)(is_relative?mp_Joff:mp_Ioff), + pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' && + (reserved_label[(int)*ss]==~0U || + !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { // Vector element + arg1 = compile(ss,s0,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) { + variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + + } + s1 = s0 + 1; while (s1 sub-vector extraction + p1 = _cimg_mp_size(arg1); + arg2 = compile(++s0,s1,depth1,0,is_critical); // Starting index + s0 = ++s1; while (s0::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // One argument -> vector value reference + arg2 = compile(++s0,se1,depth1,0,is_critical); + if (_cimg_mp_is_constant(arg2)) { // Constant index + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); + variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " + "(vector '%s' has dimension %u), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data,nb, + variable_name._data,_cimg_mp_size(arg1), + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization + } + pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2); + memtype[pos] = -1; // Prevent from being used in further optimization + _cimg_mp_return(pos); + } + } + + // Look for a function call, an access to image value, or a parenthesis. + if (*se1==')') { + if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_critical)); // Simple parentheses + _cimg_mp_op("Value accessor '()'"); + is_relative = *ss=='j' || *ss=='J'; + s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } + + // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions) + if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + if (s1::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz), + pos,p1,arg1,arg2,arg3, + arg4==~0U?_cimg_mp_interpolation:arg4, + arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); + else { + need_input_copy = true; + CImg::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz), + pos,arg1,arg2,arg3, + arg4==~0U?_cimg_mp_interpolation:arg4, + arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions) + if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + if (s1::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode); + for (s = ++s2; s::vector(arg3).move_to(l_opcode); + ++p3; + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (_cimg_mp_is_constant(arg1)) { + p3-=1; // Number of args + if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1); + else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]); + if (arg1::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test) + _cimg_mp_op("Function 'breakpoint()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"bool(",5)) { // Boolean cast + _cimg_mp_op("Function 'bool()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + _cimg_mp_scalar1(mp_bool,arg1); + } + + if (!std::strncmp(ss,"begin(",6)) { // Begin + _cimg_mp_op("Function 'begin()'"); + code.swap(code_begin); + arg1 = compile(ss6,se1,depth1,p_ref,true); + code.swap(code_begin); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread + _cimg_mp_op("Function 'begin_t()'"); + code.swap(code_begin_t); + arg1 = compile(ss8,se1,depth1,p_ref,true); + code.swap(code_begin_t); + _cimg_mp_return(arg1); + } + break; + + case 'c' : + if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value + _cimg_mp_op("Function 'cabs()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0); + _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2); + } + + if (!std::strncmp(ss,"carg(",5)) { // Complex argument + _cimg_mp_op("Function 'carg()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1); + _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1); + } + + if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root + _cimg_mp_op("Function 'cbrt()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1])); + _cimg_mp_scalar1(mp_cbrt,arg1); + } + + if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate + _cimg_mp_op("Function 'cconj()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ceil(",5)) { // Ceil + _cimg_mp_op("Function 'ceil()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1])); + _cimg_mp_scalar1(mp_ceil,arg1); + } + + if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential + _cimg_mp_op("Function 'cexp()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm + _cimg_mp_op("Function 'clog()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine + _cimg_mp_op("Function 'ccos()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"csin(",5)) { // Complex sine + _cimg_mp_op("Function 'csin()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent + _cimg_mp_op("Function 'ctan()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine + _cimg_mp_op("Function 'ccosh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine + _cimg_mp_op("Function 'csinh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent + _cimg_mp_op("Function 'ctanh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"continue(",9)) { // Continue loop + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"copy(",5)) { // Memory copy + _cimg_mp_op("Function 'copy()'"); + ref.assign(14); + s1 = ss5; while (s1=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]); + } + if (_cimg_mp_is_vector(arg2)) { + if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2)); + if (!ref[7]) ++arg2; + if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]); + } + if (arg3==~0U) arg3 = 1; + if (arg4==~0U) arg4 = 1; + if (arg5==~0U) arg5 = 1; + _cimg_mp_check_type(arg3,3,1,0); + _cimg_mp_check_type(arg4,4,1,0); + _cimg_mp_check_type(arg5,5,1,0); + _cimg_mp_check_type(arg6,5,1,0); + CImg(1,22).move_to(code); + code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6); + code.back().get_shared_rows(8,21).fill(ref); + _cimg_mp_return(p1); + } + + if (!std::strncmp(ss,"cos(",4)) { // Cosine + _cimg_mp_op("Function 'cos()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); + _cimg_mp_scalar1(mp_cos,arg1); + } + + if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine + _cimg_mp_op("Function 'cosh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); + _cimg_mp_scalar1(mp_cosh,arg1); + } + + if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time) + _cimg_mp_op("Function 'critical()'"); + p1 = code._width; + arg1 = compile(ss + 9,se1,depth1,p_ref,true); + CImg::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"crop(",5)) { // Image crop + _cimg_mp_op("Function 'crop()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)); + opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode); + is_sth = true; + } else { + _cimg_mp_check_type(arg1,pos + 1,1,0); + CImg::vector(arg1).move_to(l_opcode); + } + s = ns; + } + (l_opcode>'y').move_to(opcode); + + arg1 = 0; arg2 = (p1!=~0U); + switch (opcode._height) { + case 0 : case 1 : + CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); + break; + case 2 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + 2; + break; + case 3 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); + arg1 = arg2 + 2; + break; + case 4 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). + move_to(opcode); + arg1 = arg2 + (is_sth?2:3); + break; + case 5 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). + move_to(opcode); + arg1 = arg2 + (is_sth?2:3); + break; + case 6 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + _cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + (is_sth?2:4); + break; + case 7 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + opcode[6]).move_to(opcode); + arg1 = arg2 + (is_sth?2:4); + break; + case 8 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], + opcode[7],_cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + (is_sth?2:5); + break; + case 9 : + arg1 = arg2 + (is_sth?2:5); + break; + default : // Error -> too much arguments + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too much arguments specified, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0); + _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0); + _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0); + _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0); + if (opcode[4]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3); + opcode[4] = (ulongT)mem[opcode[4]]; + } + if (opcode[5]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3); + opcode[5] = (ulongT)mem[opcode[5]]; + } + if (opcode[6]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3); + opcode[6] = (ulongT)mem[opcode[6]]; + } + if (opcode[7]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3); + opcode[7] = (ulongT)mem[opcode[7]]; + } + _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0); + + if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U || + opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) { + p2 = 0; + if (p1!=~0U) { + _cimg_mp_check_constant(p1,1,1); + p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + } + const CImg &img = p1!=~0U?listin[p2]:imgin; + if (!img) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Cannot crop empty image when " + "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width; + if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height; + if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth; + if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum; + } + + pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); + CImg::vector((ulongT)mp_crop, + pos,p1, + *opcode,opcode[1],opcode[2],opcode[3], + opcode[4],opcode[5],opcode[6],opcode[7], + opcode[8]).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cross(",6)) { // Cross product + _cimg_mp_op("Function 'cross()'"); + s1 = ss6; while (s1::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cut(",4)) { // Cut + _cimg_mp_op("Function 'cut()'"); + s1 = ss4; while (s1val2?val2:val); + } + _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); + } + + if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate + is_sth = *ss2=='n'; // is_convolve? + _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'"); + op = is_sth?mp_convolve:mp_correlate; + const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector + 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA + 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM + 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode + 11,11,11, // [15]=xcenter, [16]=ycenter, [17]=zcenter (default value:-1) + 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart + 11,11,11, // [21]=xend, [22]=yend, [23]=zend (default value: -1) + 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride + 1,1,1 }; // [27]=xdilation, [28]=ydilation, [29]=zdilation + + l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'! + CImg(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode); + + arg1 = 2; + for (s = std::strchr(ss,'(') + 1; s=opcode._height) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: %s arguments provided, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg1<12?"Not enough":"Too much", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + _cimg_mp_check_type(opcode[2],1,2,0); // A + _cimg_mp_check_constant(opcode[3],2,3); // wA + _cimg_mp_check_constant(opcode[4],3,3); // hA + _cimg_mp_check_constant(opcode[5],4,3); // dA + _cimg_mp_check_constant(opcode[6],5,3); // sA + _cimg_mp_check_type(opcode[7],6,2,0); // M + _cimg_mp_check_constant(opcode[8],7,3); // wM + _cimg_mp_check_constant(opcode[9],8,3); // hM + _cimg_mp_check_constant(opcode[10],9,3); // dM + _cimg_mp_check_constant(opcode[11],10,3); // sM + _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions + _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized + _cimg_mp_check_constant(opcode[14],13,1); // channel_mode + _cimg_mp_check_type(opcode[15],14,1,0); // xcenter + _cimg_mp_check_type(opcode[16],15,1,0); // ycenter + _cimg_mp_check_type(opcode[17],16,1,0); // zcenter + _cimg_mp_check_constant(opcode[18],17,1); // xstart + _cimg_mp_check_constant(opcode[19],18,1); // ystart + _cimg_mp_check_constant(opcode[20],19,1); // zstart + _cimg_mp_check_constant(opcode[21],20,1); // xend + _cimg_mp_check_constant(opcode[22],21,1); // yend + _cimg_mp_check_constant(opcode[23],22,1); // zend + _cimg_mp_check_constant(opcode[24],23,3); // xstride + _cimg_mp_check_constant(opcode[25],24,3); // ystride + _cimg_mp_check_constant(opcode[26],25,3); // zstride + _cimg_mp_check_type(opcode[27],26,1,0); // xdilation + _cimg_mp_check_type(opcode[28],27,1,0); // ydilation + _cimg_mp_check_type(opcode[29],28,1,0); // zdilation + + const unsigned int + wA = (unsigned int)mem[opcode[3]], + hA = (unsigned int)mem[opcode[4]], + dA = (unsigned int)mem[opcode[5]], + sA = (unsigned int)mem[opcode[6]], + wM = (unsigned int)mem[opcode[8]], + hM = (unsigned int)mem[opcode[9]], + dM = (unsigned int)mem[opcode[10]], + sM = (unsigned int)mem[opcode[11]], + channel_mode = (unsigned int)mem[opcode[14]], + xstart = std::min((unsigned int)mem[opcode[18]],wA - 1), + ystart = std::min((unsigned int)mem[opcode[19]],hA - 1), + zstart = std::min((unsigned int)mem[opcode[20]],dA - 1), + xend = std::min((unsigned int)mem[opcode[21]],wA - 1), + yend = std::min((unsigned int)mem[opcode[22]],hA - 1), + zend = std::min((unsigned int)mem[opcode[23]],dA - 1); + + if (xstart>xend || ystart>yend || zstart>zend) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid xyz-start/end arguments " + "(start = (%u,%u,%u), end = (%u,%u,%u)), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + xstart,ystart,zstart,xend,yend,zend, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + const float + xstride = (float)mem[opcode[24]], + ystride = (float)mem[opcode[25]], + zstride = (float)mem[opcode[26]]; + + if (xstride<=0 || ystride<=0 || zstride<=0) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + xstride,ystride,zstride, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + arg2 = 1 + (unsigned int)std::floor((xend - xstart)/xstride); + arg3 = 1 + (unsigned int)std::floor((yend - ystart)/ystride); + arg4 = 1 + (unsigned int)std::floor((zend + zstart)/zstride); + arg5 = channel_mode==0?sM:channel_mode==1?std::max(sA,sM):sA*sM; + + opcode[1] = pos = vector(arg2*arg3*arg4*arg5); + opcode[3] = (ulongT)wA; + opcode[4] = (ulongT)hA; + opcode[5] = (ulongT)dA; + opcode[6] = (ulongT)sA; + opcode[8] = (ulongT)wM; + opcode[9] = (ulongT)hM; + opcode[10] = (ulongT)dM; + opcode[11] = (ulongT)sM; + opcode[14] = (ulongT)channel_mode; + opcode[18] = (ulongT)xstart; + opcode[19] = (ulongT)ystart; + opcode[20] = (ulongT)zstart; + opcode[21] = (ulongT)xend; + opcode[22] = (ulongT)yend; + opcode[23] = (ulongT)zend; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'd' : + if (*ss1=='(') { // Image depth + _cimg_mp_op("Function 'd()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_d,p1); + } + + if (!std::strncmp(ss,"date(",5)) { // Current date or file date + _cimg_mp_op("Function 'date()'"); + s1 = ss5; while (s1::vector((ulongT)mp_date,pos,_cimg_mp_size(pos), + arg1,arg1==~0U?~0U:_cimg_mp_size(arg1), + arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"debug(",6)) { // Print debug info + _cimg_mp_op("Function 'debug()'"); + p1 = code._width; + arg1 = compile(ss6,se1,depth1,p_ref,is_critical); + *se1 = 0; + variable_name.assign(CImg::string(ss6,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + ((CImg::vector((ulongT)mp_debug,arg1,0,code._width - p1), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code,p1); + *se1 = ')'; + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image + _cimg_mp_op("Function 'display()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + if (*ss8!='#') { // Vector + s1 = ss8; while (s1::string(ss8,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(arg1)) + ((CImg::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0), + variable_name)>'y').move_to(opcode); + else + ((CImg::vector((ulongT)mp_print,arg1,0,0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + + ((CImg::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1), + arg2,arg3,arg4,arg5), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *s1 = c1; + _cimg_mp_return(arg1); + + } else { // Image + p1 = compile(ss8 + 1,se1,depth1,0,is_critical); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"det(",4)) { // Matrix determinant + _cimg_mp_op("Function 'det()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + _cimg_mp_scalar2(mp_det,arg1,p1); + } + + if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix + _cimg_mp_op("Function 'diag()'"); + CImg::vector((ulongT)mp_diag,0,0).move_to(l_opcode); + for (s = ss5; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + arg1 = opcode._height - 3; + pos = vector(arg1*arg1); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"dot(",4)) { // Dot product + _cimg_mp_op("Function 'dot()'"); + s1 = ss4; while (s1::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1), + p1>=arg6 && !_cimg_mp_is_constant(p1), + p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + _cimg_mp_return(p1); + } + + if (!std::strncmp(ss,"draw(",5)) { // Draw image + if (!is_critical) is_parallelizable = false; + _cimg_mp_op("Function 'draw()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s01) { + arg3 = arg2 + 1; + if (p2>2) { + arg4 = arg3 + 1; + if (p2>3) arg5 = arg4 + 1; + } + } + ++s0; + is_sth = true; + } else { + if (s0::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5, + 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode); + + arg2 = arg3 = arg4 = arg5 = ~0U; + p2 = p1!=~0U?0:1; + if (s0::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode); + for (s = ss5; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector + _cimg_mp_op("Function 'eig()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + pos = vector((p1 + 1)*p1); + CImg::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing + if (!is_critical) is_parallelizable = false; + _cimg_mp_op("Function 'ellipse()'"); + if (*ss8=='#') { // Index specified + s0 = ss + 9; while (s0::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"erf(",4)) { // Error function + _cimg_mp_op("Function 'erf()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::erf(mem[arg1])); + _cimg_mp_scalar1(mp_erf,arg1); + } + + if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function + _cimg_mp_op("Function 'erfinv()'"); + arg1 = compile(ss7,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::erfinv(mem[arg1])); + _cimg_mp_scalar1(mp_erfinv,arg1); + } + + if (!std::strncmp(ss,"exp(",4)) { // Exponential + _cimg_mp_op("Function 'exp()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); + _cimg_mp_scalar1(mp_exp,arg1); + } + + if (!std::strncmp(ss,"expr(",5)) { // Vector from expression + _cimg_mp_op("Function 'expr()'"); + s1 = ss5; while (s1::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"eye(",4)) { // Identity matrix + _cimg_mp_op("Function 'eye()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_constant(arg1,1,3); + p1 = (unsigned int)mem[arg1]; + pos = vector(p1*p1); + CImg::vector((ulongT)mp_eye,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"end(",4)) { // End + _cimg_mp_op("Function 'end()'"); + code.swap(code_end); + compile(ss4,se1,depth1,p_ref,true); + code.swap(code_end); + is_end_code = true; + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"end_t(",6)) { // End thread + _cimg_mp_op("Function 'end_t()'"); + code.swap(code_end_t); + compile(ss6,se1,depth1,p_ref,true); + code.swap(code_end_t); + is_end_code = true; + _cimg_mp_return_nan(); + } + break; + + case 'f' : + if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion + _cimg_mp_op("Function 'f2ui()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::float2uint((float)mem[arg1])); + _cimg_mp_scalar1(mp_f2ui,arg1); + } + + if (!std::strncmp(ss,"fact(",5)) { // Factorial + _cimg_mp_op("Function 'fact()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1])); + _cimg_mp_scalar1(mp_factorial,arg1); + } + + if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci + _cimg_mp_op("Function 'fibo()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1])); + _cimg_mp_scalar1(mp_fibonacci,arg1); + } + + if (!std::strncmp(ss,"fill(",5)) { // Fill + _cimg_mp_op("Function 'fill()'"); + s0 = ss5; while (s0::%s: %s: Target scalar is constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + ss>expr._data?"...":"",ss,se<&expr.back()?"...":""); + s1 = ++s0; while (s1::%s: %s: Invalid loop variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg3,3,1,0); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,is_critical); + } + // arg2 = variable slot, arg3 = fill expression. + _cimg_mp_check_type(arg3,3,1,0); + CImg::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1). + move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"find(",5)) { // Find + _cimg_mp_op("Function 'find()'"); + + // First argument: data to look at. + s0 = ss5; while (s01) + _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4); + _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4); + } + if (_cimg_mp_size(arg2)>1) + _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4); + _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4); + } + + if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop + _cimg_mp_op("Function 'for()'"); + s1 = ss4; while (s1::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2, + arg4 - arg3,code._width - arg4, + p3>=arg6 && !_cimg_mp_is_constant(p3), + p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + _cimg_mp_return(p3); + } + + if (!std::strncmp(ss,"floor(",6)) { // Floor + _cimg_mp_op("Function 'floor()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1])); + _cimg_mp_scalar1(mp_floor,arg1); + } + + if (!std::strncmp(ss,"fsize(",6)) { // File size + _cimg_mp_op("Function 'fsize()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,2,0); + pos = scalar(); + CImg::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'g' : + if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function + _cimg_mp_op("Function 'gauss()'"); + s1 = ss6; while (s1::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + break; + + case 'h' : + if (*ss1=='(') { // Image height + _cimg_mp_op("Function 'h()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_h,p1); + } + break; + + case 'i' : + if (*ss1=='c' && *ss2=='(') { // Image median + _cimg_mp_op("Function 'ic()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_median,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (*ss1=='n' && *ss2=='(') { // Image norm + _cimg_mp_op("Function 'in()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_norm,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (*ss1=='f' && *ss2=='(') { // If..then[..else.] + _cimg_mp_op("Function 'if()'"); + s1 = ss3; while (s1::vector((ulongT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"inrange(",8)) { // Check value range + _cimg_mp_op("Function 'inrange()'"); + s1 = ss8; while (s1=val1) + is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"int(",4)) { // Integer cast + _cimg_mp_op("Function 'int()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]); + _cimg_mp_scalar1(mp_int,arg1); + } + + if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion + _cimg_mp_op("Function 'invert()'"); + s1 = ss7; while (s1::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]); + _cimg_mp_scalar2(mp_div,1,arg1); + } + + if (*ss1=='s') { // Family of 'is_?()' functions + + if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? + _cimg_mp_op("Function 'isbool()'"); + if (ss7==se1) _cimg_mp_return(0); + try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + catch(CImgException&) { _cimg_mp_return(0); } + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.); + _cimg_mp_scalar1(mp_isbool,arg1); + } + + if (!std::strncmp(ss,"isdir(",6)) { // Is directory? + _cimg_mp_op("Function 'isdir()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isfile(",7)) { // Is file? + _cimg_mp_op("Function 'isfile()'"); + arg1 = compile(ss7,se1,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector? + if (ss5>=se1) _cimg_mp_return(0); + _cimg_mp_op("Function 'isin()'"); + pos = scalar(); + CImg::vector((ulongT)mp_isin,pos,0).move_to(l_opcode); + for (s = ss5; s::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)). + move_to(l_opcode); + else CImg::vector(arg1).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? + _cimg_mp_op("Function 'isinf()'"); + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); + _cimg_mp_scalar1(mp_isinf,arg1); + } + + if (!std::strncmp(ss,"isint(",6)) { // Is integer? + _cimg_mp_op("Function 'isint()'"); + if (ss6==se1) _cimg_mp_return(0); + try { arg1 = compile(ss6,se1,depth1,0,is_critical); } + catch(CImgException&) { _cimg_mp_return(0); } + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1])); + _cimg_mp_scalar1(mp_isint,arg1); + } + + if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? + _cimg_mp_op("Function 'isnan()'"); + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); + _cimg_mp_scalar1(mp_isnan,arg1); + } + + if (!std::strncmp(ss,"isnum(",6)) { // Is number? + _cimg_mp_op("Function 'isnum()'"); + val = 0; + if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); + _cimg_mp_return(0); + } + + if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression? + _cimg_mp_op("Function 'isexpr()'"); + if (ss7==se1) _cimg_mp_return(0); + try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + catch (CImgException&) { _cimg_mp_return(0); } + _cimg_mp_return(1); + } + } + break; + + case 'l' : + if (*ss1=='(') { // Size of image list + _cimg_mp_op("Function 'l()'"); + if (ss2!=se1) break; + _cimg_mp_scalar0(mp_list_l); + } + + if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation + _cimg_mp_op("Function 'lerp()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"log(",4)) { // Natural logarithm + _cimg_mp_op("Function 'log()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); + _cimg_mp_scalar1(mp_log,arg1); + } + + if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm + _cimg_mp_op("Function 'log2()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); + _cimg_mp_scalar1(mp_log2,arg1); + } + + if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm + _cimg_mp_op("Function 'log10()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); + _cimg_mp_scalar1(mp_log10,arg1); + } + + if (!std::strncmp(ss,"lowercase(",10)) { // Lower case + _cimg_mp_op("Function 'lowercase()'"); + arg1 = compile(ss + 10,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1])); + _cimg_mp_scalar1(mp_lowercase,arg1); + } + break; + + case 'm' : + if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication + _cimg_mp_op("Function 'mul()'"); + s1 = ss4; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " + "do not match with third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary + _cimg_mp_op("Function 'mproj()'"); + s1 = ss6; while (s1::%s: %s: Type of first argument ('%s') " + "do not match with second argument 'nb_colsS=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,wS, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (wD*hD!=p2) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of third argument ('%s') " + "do not match with fourth argument 'nb_colsD=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg3)._data,wD, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (hS!=hD) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of first argument ('%s') " + "do not match with third argument ('%s'), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg3)._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(wS*wD); + CImg::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables + _cimg_mp_op("Function 'merge()'"); + s1 = ss6; while (s1::%s: %s: Merge has already been requested before " + "for specified variable " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (arg1==~0U) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified operator " + "(should be one of '=,+,-,*,/,min,max'), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + memmerge.resize(3,memmerge._height + 1,1,1,0,0); + memmerge(0,memmerge._height - 1) = (int)pos; + memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos); + memmerge(2,memmerge._height - 1) = (int)arg1; + _cimg_mp_return(pos); + } + break; + + case 'n' : +#ifdef cimg_mp_func_name + if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector + _cimg_mp_op("Function 'name()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::vector((ulongT)mp_name,pos,p1,arg1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + + if (!std::strncmp(ss,"narg(",5)) { // Number of arguments + _cimg_mp_op("Function 'narg()'"); + if (ss5>=se1) _cimg_mp_return(0); + arg1 = 0; + for (s = ss5; s::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break; + case 1 : + CImg::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break; + case 2 : + CImg::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break; + case ~0U : + CImg::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break; + default : + CImg::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). + move_to(l_opcode); + } + for ( ; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + + (l_opcode>'y').move_to(opcode); + if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 + _cimg_mp_scalar1(mp_abs,opcode[3]); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'p' : + if (!std::strncmp(ss,"permut(",7)) { // Number of permutations + _cimg_mp_op("Function 'permut()'"); + s1 = ss7; while (s1::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions + is_sth = ss[5]=='s'; // is prints() + _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); + s0 = is_sth?ss7:ss6; + if (*s0!='#' || is_sth) { // Regular expression + for (s = s0; s::string(s,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), + variable_name)>'y').move_to(opcode); + else // Scalar + ((CImg::vector((ulongT)mp_print,pos,0,is_sth?1:0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *ns = c1; s = ns; + } + _cimg_mp_return(pos); + } else { // Image + p1 = compile(ss7,se1,depth1,0,is_critical); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion + _cimg_mp_op("Function 'pseudoinvert()'"); + s1 = ss + 13; while (s1::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1); + CImg::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'r' : + if (!std::strncmp(ss,"ref(",4)) { // Variable declaration + _cimg_mp_op("Function 'ref()'"); + s1 = ss4; while (s1=se1 || !*s1) compile(s1,s1,depth1,0,is_critical); // Will throw missing argument error + arg3 = compile(ss4,s1++,depth1,p_ref,is_critical); + *se1 = 0; + + if (!is_varname(s1)) { // Invalid variable name + variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0; + cimg::strellipsize(variable_name,64); + *se1 = ')'; + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + + } + get_variable_pos(s1,arg1,arg2); + if (arg2!=~0U) reserved_label[arg2] = arg3; + else if (arg1!=~0U) variable_pos[arg1] = arg3; + else { // New variable + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg3; + CImg::string(s1).move_to(variable_def); + } + if (_cimg_mp_is_vector(arg3)) + set_reserved_vector(arg3); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; + *se1 = ')'; + _cimg_mp_return(arg3); + } + + if (!std::strncmp(ss,"repeat(",7)) { // Repeat + _cimg_mp_op("Function 'repeat()'"); + s0 = ss7; while (s0::%s: %s: Invalid loop variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,is_critical); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,is_critical); + } + // arg2 = variable slot, arg3 = fill expression. + CImg::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg3); + } + + if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize + _cimg_mp_op("Function 'resize()'"); + if (*ss7!='#') { // Vector + s1 = ss7; while (s1::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1), + arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + + } else { // Image + if (!is_critical) is_parallelizable = false; + s0 = ss8; while (s0::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). + move_to(l_opcode); + pos = 0; + for (s = s0; s10) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + pos<1?"Missing":"Too much", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + l_opcode[0].move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse + _cimg_mp_op("Function 'reverse()'"); + arg1 = compile(ss8,se1,depth1,0,is_critical); + if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); + p1 = _cimg_mp_size(arg1); + pos = vector(p1); + CImg::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation + _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'"); + s1 = ss4; while (s11) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + arg4 = compile(++s1,se1,depth1,0,is_critical); + } else { + s2 = s1 + 1; while (s2::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code); + } else { // 2D rotation + _cimg_mp_check_type(arg1,1,1,0); + pos = vector(4); + CImg::vector((ulongT)mp_rot2d,pos,arg1).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"round(",6)) { // Value rounding + _cimg_mp_op("Function 'round()'"); + s1 = ss6; while (s1::vector((ulongT)mp_run,0,0).move_to(l_opcode); + pos = 1; + for (s = ss4; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + pos = scalar(); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + break; + + case 's' : + if (*ss1=='(') { // Image spectrum + _cimg_mp_op("Function 's()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_s,p1); + } + + if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values + _cimg_mp_op("Function 'same()'"); + s1 = ss5; while (s1::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sign(",5)) { // Sign + _cimg_mp_op("Function 'sign()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1])); + _cimg_mp_scalar1(mp_sign,arg1); + } + + if (!std::strncmp(ss,"sin(",4)) { // Sine + _cimg_mp_op("Function 'sin()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1])); + _cimg_mp_scalar1(mp_sin,arg1); + } + + if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal + _cimg_mp_op("Function 'sinc()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1])); + _cimg_mp_scalar1(mp_sinc,arg1); + } + + if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine + _cimg_mp_op("Function 'sinh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1])); + _cimg_mp_scalar1(mp_sinh,arg1); + } + + if (!std::strncmp(ss,"size(",5)) { // Vector size + _cimg_mp_op("Function 'size()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); + } + + if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system + _cimg_mp_op("Function 'solve()'"); + s1 = ss6; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " + "do not match with third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sort(",5)) { // Sort vector + _cimg_mp_op("Function 'sort()'"); + s1 = ss5; while (s1::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sqr(",4)) { // Square + _cimg_mp_op("Function 'sqr()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); + _cimg_mp_scalar1(mp_sqr,arg1); + } + + if (!std::strncmp(ss,"sqrt(",5)) { // Square root + _cimg_mp_op("Function 'sqrt()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); + _cimg_mp_scalar1(mp_sqrt,arg1); + } + + if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed + _cimg_mp_op("Function 'srand()'"); + arg1 = ss6::vector((ulongT)mp_image_stats,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + +#ifdef cimg_mp_func_store + if (!std::strncmp(ss,"store(",6)) { // Store vector to variable + _cimg_mp_op("Function 'store()'"); + s1 = ss6; while (s1::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2, + arg3,arg4,arg5,arg6,pos).move_to(code); + _cimg_mp_return_nan(); + } +#endif + + if (!std::strncmp(ss,"stov(",5)) { // String to double + _cimg_mp_op("Function 'stov()'"); + s1 = ss5; while (s1::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments + _cimg_mp_op("Function 'string()'"); + CImg::vector((ulongT)mp_string,0,0,0).move_to(l_opcode); + + if (*ss7=='#') { // Output vector size specified, with '#' + s0 = ss8; while (s0::vector(arg2,p2).move_to(l_opcode); + s = ns; + } + if (arg1==~0U) arg1 = p1; + pos = vector(arg1,0); + (l_opcode>'y').move_to(opcode); + opcode[1] = pos; + opcode[2] = arg1; + opcode[3] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD + _cimg_mp_op("Function 'svd()'"); + s1 = ss4; while (s1::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1 + p2 + p2*p2); + CImg::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"swap(",5)) { // Swap values + _cimg_mp_op("Function 'swap()'"); + s1 = ss5; while (s1::%s: %s: %s argument cannot be a constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"First":"Second", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + CImg::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code); + + // Write back values of linked arg1 and arg2. + const unsigned int *_ref = ref; + is_sth = true; // Is first argument? + do { + switch (*_ref) { + case 1 : // arg1: V[k] + arg3 = _ref[1]; // Vector slot + arg4 = _ref[2]; // Index + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + break; + case 2 : // arg1: i/j[_#ind,off] + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // Offset + if (p1!=~0U) { + if (listout) + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (imgout) + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + break; + case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c) + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // X + arg4 = _ref[4]; // Y + arg5 = _ref[5]; // Z + arg6 = _ref[6]; // C + if (p1!=~0U) { + if (listout) + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (imgout) + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + break; + case 4: // arg1: I/J[_#ind,off] + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // Offset + if (p1!=~0U) { + if (listout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg1,p1,arg3).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + } + } else { + if (imgout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg1,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + } + break; + case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c) + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // X + arg4 = _ref[4]; // Y + arg5 = _ref[5]; // Z + if (p1!=~0U) { + if (listout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg1,p1,arg3,arg4,arg5).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + } else { + if (imgout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg1,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + break; + } + + _ref+=7; + arg1 = arg2; + is_sth = !is_sth; + } while (!is_sth); + + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + _cimg_mp_return(arg1); + } + break; + + case 't' : + if (!std::strncmp(ss,"tan(",4)) { // Tangent + _cimg_mp_op("Function 'tan()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); + _cimg_mp_scalar1(mp_tan,arg1); + } + + if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent + _cimg_mp_op("Function 'tanh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); + _cimg_mp_scalar1(mp_tanh,arg1); + } + + if (!std::strncmp(ss,"trace(",6)) { // Matrix trace + _cimg_mp_op("Function 'trace()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + _cimg_mp_scalar2(mp_trace,arg1,p1); + } + + if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose + _cimg_mp_op("Function 'transpose()'"); + s1 = ss + 10; while (s1::%s: %s: Size of first argument ('%s') does not match " + "second argument 'nb_cols=%u', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p3*p2); + CImg::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'u' : + if (*ss1=='(') { // Random value with uniform distribution + _cimg_mp_op("Function 'u()'"); + if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); + s1 = ss2; while (s1float conversion + _cimg_mp_op("Function 'ui2f()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::uint2float((unsigned int)mem[arg1])); + _cimg_mp_scalar1(mp_ui2f,arg1); + } + + if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable + _cimg_mp_op("Function 'unref()'"); + arg1=~0U; + for (s0 = ss6; s0ss6 && *s0==',') ++s0; + s1 = s0; while (s1s0) { + *s1 = 0; + get_variable_pos(s0,arg1,arg2); + if (arg2!=~0U) reserved_label[arg2] = ~0U; + else if (arg1!=~0U) { + variable_def.remove(arg1); + if (arg10) || + !std::strncmp(ss,"vector(",7) || + (!std::strncmp(ss,"vector",6) && ss7::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode); + arg2+=arg4; + } else { CImg::vector(arg3).move_to(l_opcode); ++arg2; } + s = ns; + } + if (arg1==~0U) arg1 = arg2; + if (!arg1) _cimg_mp_return(0); + pos = vector(arg1); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) || + !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) || + !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) || + !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) || + !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) || + !std::strncmp(ss,"vprod(",6) || + !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) || + !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) || + !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions + _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'": + ss[4]=='k'?"Function 'vargkth()'": + ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'": + ss[5]=='i'?"Function vargminabs()'": + ss[7]=='('?"Function 'vargmax()'": + "Function 'vargmaxabs()'"): + ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"): + ss[1]=='k'?"Function 'vkth()'": + ss[1]=='p'?"Function 'vprod()'": + ss[1]=='v'?"Function 'vvar()'": + ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'": + "Function 'vminabs()'"): + ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'": + "Function 'vmaxabs()'"): + "Function 'vmed()'"); + op = ss[1]=='a'?(ss[2]=='v'?mp_vavg: + ss[4]=='k'?mp_vargkth: + ss[5]=='i' && ss[7]=='('?mp_vargmin: + ss[5]=='i'?mp_vargminabs: + ss[7]=='('?mp_vargmax:mp_vargmaxabs): + ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd): + ss[1]=='k'?mp_vkth: + ss[1]=='p'?mp_vprod: + ss[1]=='v'?mp_vvar: + ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs): + ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs): + mp_vmedian; + CImg::vector((ulongT)op,0,0,0).move_to(l_opcode); + p1 = ~0U; + p3 = 1; + for (s = std::strchr(ss,'(') + 1; s::vector(arg2,p2).move_to(l_opcode); + s = ns; + ++p3; + } + (l_opcode>'y').move_to(opcode); + if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1); + opcode[1] = pos; + opcode[2] = p1; + opcode[3] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string + _cimg_mp_op("Function 'vtos()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'w' : + if (*ss1=='(') { // Image width + _cimg_mp_op("Function 'w()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_w,p1); + } + + if (*ss1=='h' && *ss2=='(') { // Image width*height + _cimg_mp_op("Function 'wh()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_wh,p1); + } + + if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth + _cimg_mp_op("Function 'whd()'"); + if (*ss4=='#') { // Index specified + p1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss4!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_whd,p1); + } + + if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum + _cimg_mp_op("Function 'whds()'"); + if (*ss5=='#') { // Index specified + p1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss5!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_whds,p1); + } + + if (!std::strncmp(ss,"while(",6)) { // While...do + _cimg_mp_op("Function 'while()'"); + s0 = *ss5=='('?ss6:ss8; + s1 = s0; while (s1::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2, + pos>=arg6 && !_cimg_mp_is_constant(pos), + arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1); + _cimg_mp_return(pos); + } + break; + + case 'x' : + if (!std::strncmp(ss,"xor(",4)) { // Xor + _cimg_mp_op("Function 'xor()'"); + s1 = ss4; while (s1::vector((ulongT)op,pos,0).move_to(l_opcode); + for (s = std::strchr(ss,'(') + 1; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + is_sth&=_cimg_mp_is_constant(arg2); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (is_sth) _cimg_mp_constant(op(*this)); + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // No corresponding built-in function -> Look for a user-defined macro call. + s0 = strchr(ss,'('); + if (s0) { + variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; + + // Count number of specified arguments. + p1 = 0; + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && !p1) break; + ns = s; while (ns _expr = macro_body[l]; // Expression to be substituted + + p1 = 1; // Index of current parsed argument + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && p1==1) break; // Function has no arguments + if (p1>p2) { ++p1; break; } + ns = s; while (ns _pexpr(_expr._width); + ns = _pexpr._data; + for (ps = _expr._data, c1 = ' '; *ps; ++ps) { + if (!cimg::is_blank(*ps)) c1 = *ps; + *(ns++) = c1; + } + *ns = 0; + + CImg _level = get_level(_expr); + expr.swap(_expr); + pexpr.swap(_pexpr); + level.swap(_level); + s0 = user_macro; + user_macro = macro_def[l]; + pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_critical); + user_macro = s0; + level.swap(_level); + pexpr.swap(_pexpr); + expr.swap(_expr); + _cimg_mp_return(pos); + } + + if (arg3) { // Macro name matched but number of arguments does not + CImg sig_nargs(arg3); + arg1 = 0; + cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name)) + sig_nargs[arg1++] = (unsigned int)macro_def[l].back(); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + if (sig_nargs._width>1) { + sig_nargs.sort(); + arg1 = sig_nargs.back(); + --sig_nargs._width; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " + "does not match macro declaration (defined for %s or %u arguments), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p1,sig_nargs.value_string()._data,arg1, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " + "does not match macro declaration (defined for %u argument%s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p1,*sig_nargs,*sig_nargs!=1?"s":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + } // if (se1==')') + + // Char / string initializer. + if (*se1=='\'' && + ((se1>ss && *ss=='\'') || + (se1>ss1 && *ss=='_' && *ss1=='\''))) { + if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; } + else { _cimg_mp_op("String initializer"); s1 = ss1; } + arg1 = (unsigned int)(se1 - s1); // Original string length + if (arg1) { + CImg(s1,arg1 + 1).move_to(variable_name).back() = 0; + cimg::strunescape(variable_name); + arg1 = (unsigned int)std::strlen(variable_name); + } + if (!arg1) _cimg_mp_return(0); // Empty string -> 0 + if (*ss=='_') { + if (arg1==1) _cimg_mp_constant((unsigned char)*variable_name); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Literal %s contains more than one byte, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + ss1, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg1); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // Vector initializer [ ... ]. + if (*ss=='[' && *se1==']') { + _cimg_mp_op("Vector initializer"); + s1 = ss1; while (s1s1 && cimg::is_blank(*s2)) --s2; + if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string + arg1 = (unsigned int)(s2 - s1 - 1); // Original string length + if (arg1) { + CImg(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0; + cimg::strunescape(variable_name); + arg1 = (unsigned int)std::strlen(variable_name); + } + if (!arg1) _cimg_mp_return(0); // Empty string -> 0 + pos = vector(arg1); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); + } else { // Vector values provided as list of items + arg1 = 0; // Number of specified values + if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode); + arg1+=arg3; + } else { CImg::vector(arg2).move_to(l_opcode); ++arg1; } + s = ns; + } + if (!arg1) _cimg_mp_return(0); + pos = vector(arg1); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + // Variables related to the input list of images. + if (*ss1=='#' && ss2::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + case 'R' : // R#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0, + 0,_cimg_mp_boundary); + case 'G' : // G#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1, + 0,_cimg_mp_boundary); + case 'B' : // B#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2, + 0,_cimg_mp_boundary); + case 'A' : // A#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3, + 0,_cimg_mp_boundary); + } + } + + if (*ss1 && *ss2=='#' && ss3='0' && *ss1<='9') { // i0#ind...i9#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0', + 0,_cimg_mp_boundary); + } + + if (*ss1=='c') { // ic#ind + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_median) list_median.assign(listin._width); + if (!list_median[p1]) CImg::vector(listin[p1].median()).move_to(list_median[p1]); + _cimg_mp_constant(*list_median[p1]); + } + _cimg_mp_scalar1(mp_list_median,arg1); + } + + if (*ss1=='n') { // in#ind + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_norm) list_norm.assign(listin._width); + if (!list_norm[p1]) CImg::vector(listin[p1].magnitude()).move_to(list_norm[p1]); + _cimg_mp_constant(*list_norm[p1]); + } + _cimg_mp_scalar1(mp_list_norm,arg1); + } + + switch (*ss1) { + case 'm' : arg2 = 0; break; // im#ind + case 'M' : arg2 = 1; break; // iM#ind + case 'a' : arg2 = 2; break; // ia#ind + case 'v' : arg2 = 3; break; // iv#ind + case 's' : arg2 = 12; break; // is#ind + case 'p' : arg2 = 13; break; // ip#ind + } + } else if (*ss1=='m') switch (*ss) { + case 'x' : arg2 = 4; break; // xm#ind + case 'y' : arg2 = 5; break; // ym#ind + case 'z' : arg2 = 6; break; // zm#ind + case 'c' : arg2 = 7; break; // cm#ind + } else if (*ss1=='M') switch (*ss) { + case 'x' : arg2 = 8; break; // xM#ind + case 'y' : arg2 = 9; break; // yM#ind + case 'z' : arg2 = 10; break; // zM#ind + case 'c' : arg2 = 11; break; // cM#ind + } + if (arg2!=~0U) { + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_stats) list_stats.assign(listin._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); + _cimg_mp_constant(list_stats(p1,arg2)); + } + _cimg_mp_scalar2(mp_list_stats,arg1,arg2); + } + } + + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4 error. + c1 = *se1; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + if (is_sth) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s1 = std::strchr(ss,'('); + s_op = s1 && c1==')'?"function call":"item"; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + s_op,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Evaluation procedure. + double operator()(const double x, const double y, const double z, const double c) { + mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; + for (p_code = code; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + return *result; + } + + // Evaluation procedure (return output values in vector 'output'). + template + void operator()(const double x, const double y, const double z, const double c, t *const output) { + mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; + for (p_code = code; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + if (result_dim) { + const double *ptrs = result + 1; + t *ptrd = output; + for (unsigned int k = 0; k_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + p_code_end = code.end(); + } + + // Evaluation procedure for end_t() bloc. + void end_t() { + if (!code_end_t) return; + if (imgin) { + mem[_cimg_mp_slot_x] = imgin._width - 1.; + mem[_cimg_mp_slot_y] = imgin._height - 1.; + mem[_cimg_mp_slot_z] = imgin._depth - 1.; + mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; + } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_end_t.end(); + for (p_code = code_end_t; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Evaluation procedure the end() bloc. + void end() { + if (!code_end) return; + if (imgin) { + mem[_cimg_mp_slot_x] = imgin._width - 1.; + mem[_cimg_mp_slot_y] = imgin._height - 1.; + mem[_cimg_mp_slot_z] = imgin._depth - 1.; + mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; + } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_end.end(); + for (p_code = code_end; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Merge inter-thread variables. + // (argument 'mp' is the master instance). + void merge(_cimg_math_parser& mp) { + if (&mp==this) return; + cimg_rofY(mp.memmerge,k) { + const unsigned int + pos = (unsigned int)mp.memmerge(0,k), + siz = (unsigned int)mp.memmerge(1,k), + iop = (unsigned int)mp.memmerge(2,k); + if (!siz) switch (iop) { // Scalar value + case 0 : mp.mem[pos] = mem[pos]; break; // Assignment + case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+ + case 2 : mp.mem[pos]-=mem[pos]; break; // Operator- + case 3 : mp.mem[pos]*=mem[pos]; break; // Operator* + case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/ + case 5 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min' + case 6 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max' + } else switch (iop) { // Vector value + case 0 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true) = CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 1 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 2 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 3 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 4 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 5 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg(&mem[pos + 1],siz,1,1,1,true)); + break; + case 6 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg(&mem[pos + 1],siz,1,1,1,true)); + break; + } + } + } + + // Return specified argument number as a string. + static const char *s_argth(const unsigned int n_arg) { + const char + *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth", + "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", + "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" }; + return _s_arg[n_arg s_calling_function() const { + CImg res; + const unsigned int + l1 = calling_function?(unsigned int)std::strlen(calling_function):0U, + l2 = user_macro?(unsigned int)std::strlen(user_macro):0U; + if (l2) { + res.assign(l1 + l2 + 48); + cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro); + } else { + res.assign(l1 + l2 + 4); + cimg_snprintf(res,res._width,"%s()",calling_function); + } + return res; + } + + // Return type of a memory element as a string. + CImg s_type(const unsigned int arg) const { + CImg res; + if (_cimg_mp_is_vector(arg)) { // Vector + CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); + cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg)); + } else if (_cimg_mp_is_constant(arg)) CImg::string("const scalar").move_to(res); // Const scalar + else CImg::string("scalar").move_to(res); // Scalar + return res; + } + + // Count parentheses/brackets level of each character of the expression. + CImg get_level(CImg& _expr) const { + bool is_escaped = false, next_is_escaped = false; + unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string + CImg res(_expr._width - 1); + unsigned int *pd = res._data; + int _level = 0; + for (const char *ps = _expr._data; *ps && _level>=0; ++ps) { + if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true; + if (!is_escaped && *ps=='\'') { // Non-escaped character + if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string + else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string + else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string + } + *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1): + *ps=='(' || *ps=='['?_level++: + *ps==')' || *ps==']'?--_level: + _level); + mode = next_mode; + is_escaped = next_is_escaped; + next_is_escaped = false; + } + if (mode) { + cimg::strellipsize(_expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unterminated string literal, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + _expr._data); + } + if (_level) { + cimg::strellipsize(_expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + _expr._data); + } + return res; + } + + // Find and return index of current image 'imgin' within image list 'listin'. + unsigned int get_mem_img_index() { + if (mem_img_index==~0U) { + if (&imgout>listout.data() && &imgout='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9 + else if (c2=='m') rp = 4; // im + else if (c2=='M') rp = 5; // iM + else if (c2=='a') rp = 6; // ia + else if (c2=='v') rp = 7; // iv + else if (c2=='s') rp = 8; // is + else if (c2=='p') rp = 9; // ip + else if (c2=='c') rp = 10; // ic + else if (c2=='n') rp = 11; // in + } else if (c2=='m') { + if (c1=='x') rp = 12; // xm + else if (c1=='y') rp = 13; // ym + else if (c1=='z') rp = 14; // zm + else if (c1=='c') rp = 15; // cm + } else if (c2=='M') { + if (c1=='x') rp = 16; // xM + else if (c1=='y') rp = 17; // yM + else if (c1=='z') rp = 18; // zM + else if (c1=='c') rp = 19; // cM + } + } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd + } else if (variable_name[1] && variable_name[2] && variable_name[3] && + !variable_name[4]) { // Four-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + c4 = variable_name[3]; + if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds + } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation + else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary + + if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels + + // Multi-char variable name : check for existing variable with same name + cimglist_for(variable_def,i) + if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; } + } + + // Tell for each character of an expression if it is inside a string or not. + CImg is_inside_string(CImg& _expr) const { + bool is_escaped = false, next_is_escaped = false; + unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string + CImg res = CImg::string(_expr); + bool *pd = res._data; + for (const char *ps = _expr._data; *ps; ++ps) { + if (!next_is_escaped && *ps=='\\') next_is_escaped = true; + if (!is_escaped && *ps=='\'') { // Non-escaped character + if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string + else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string + else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string + } + *(pd++) = mode>=1 || is_escaped; + mode = next_mode; + is_escaped = next_is_escaped; + next_is_escaped = false; + } + return res; + } + + // Return true if specified argument can be a part of an allowed variable name. + bool is_varchar(const char c) const { + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + } + + // Return true if specified argument can be considered as a variable name. + bool is_varname(const char *const str, const unsigned int length=~0U) const { + if (*str>='0' && *str<='9') return false; + for (unsigned int l = 0; l128) return false; + const int *ptr = memtype.data(arg + 1); + bool is_tmp = true; + while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } + return is_tmp; + } + + // Check if a memory slot is a positive integer constant scalar value. + // 'mode' can be: + // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant } + void check_constant(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, + char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,1,0); + if (!(_cimg_mp_is_constant(arg) && + (!mode || (double)(int)mem[arg]==mem[arg]) && + (mode<2 || mem[arg]>=(mode==3)))) { + const char *const s_arg = s_argth(n_arg); + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_arg?" argument":" Argument",s_type(arg)._data, + !mode?"":mode==1?"n integer": + mode==2?" positive integer":" strictly positive integer", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check if an image index is a constant value. + void check_constant_index(const unsigned int arg, + char *const ss, char *const se, const char saved_char) { + if (arg!=~0U && !_cimg_mp_is_constant(arg)) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Specified image index is not a constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check a matrix is square. + void check_matrix_square(const unsigned int arg, const unsigned int n_arg, + char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,2,0); + const unsigned int + siz = _cimg_mp_size(arg), + n = (unsigned int)cimg::round(std::sqrt((float)siz)); + if (n*n!=siz) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand"; + else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One"; + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') " + "cannot be considered as a square matrix, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), + s_type(arg)._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check type compatibility for one argument. + // Bits of 'mode' tells what types are allowed: + // { 1 = scalar | 2 = vectorN }. + // If 'N' is not zero, it also restricts the vectors to be of size N only. + void check_type(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, const unsigned int N, + char *const ss, char *const se, const char saved_char) { + const bool + is_scalar = _cimg_mp_is_scalar(arg), + is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N); + bool cond = false; + if (mode&1) cond|=is_scalar; + if (mode&2) cond|=is_vector; + if (!cond) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand"; + else s_arg = s_argth(n_arg); + CImg sb_type(32); + if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'"); + else if (mode==2) { + if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'vector'"); + } else { + if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'"); + } + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), + s_type(arg)._data,sb_type._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check that listin or listout are not empty. + void check_list(const bool is_out, + char *const ss, char *const se, const char saved_char) { + if ((!is_out && !listin) || (is_out && !listout)) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Invalid call with an empty image list, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Insert constant value in memory. + unsigned int constant(const double val) { + + // Search for built-in constant. + if (cimg::type::is_nan(val)) return _cimg_mp_slot_nan; + if (val==(double)(int)val) { + if (val>=0 && val<=10) return (unsigned int)val; + if (val<0 && val>=-5) return (unsigned int)(10 - val); + } + if (val==0.5) return 16; + + // Search for constant already requested before (in const cache). + unsigned int ind = ~0U; + if (constcache_size<1024) { + if (!constcache_size) { + constcache_vals.assign(16,1,1,1,0); + constcache_inds.assign(16,1,1,1,0); + *constcache_vals = val; + constcache_size = 1; + ind = 0; + } else { // Dichotomic search + const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1]; + if (val_beg>=val) ind = 0; + else if (val_end==val) ind = constcache_size - 1; + else if (val_end=constcache_size || constcache_vals[ind]!=val) { + ++constcache_size; + if (constcache_size>constcache_vals._width) { + constcache_vals.resize(-200,1,1,1,0); + constcache_inds.resize(-200,1,1,1,0); + } + const int l = constcache_size - (int)ind - 1; + if (l>0) { + std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double)); + std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int)); + } + constcache_vals[ind] = val; + constcache_inds[ind] = 0; + } + } + if (constcache_inds[ind]) return constcache_inds[ind]; + } + + // Insert new constant in memory if necessary. + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); } + const unsigned int pos = mempos++; + mem[pos] = val; + memtype[pos] = 1; // Set constant property + if (ind!=~0U) constcache_inds[ind] = pos; + return pos; + } + + // Insert new scalar in memory. + unsigned int scalar() { + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); } + return mempos++; + } + + // Insert new vector of specified size in memory. + unsigned int vector(const unsigned int siz) { + if (mempos + siz>=mem._width) { + mem.resize(2*mem._width + siz,1,1,1,0); + memtype.resize(mem._width,1,1,1,0); + } + const unsigned int pos = mempos++; + mem[pos] = cimg::type::nan(); + memtype[pos] = siz + 1; + mempos+=siz; + return pos; + } + + // Insert new initialized vector. + unsigned int vector(const unsigned int siz, const double value) { + const unsigned int pos = vector(siz); + double *ptr = &mem[pos] + 1; + for (unsigned int i = 0; i::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code); + return pos; + } + + // Set reserved status to all values of a vector. + void set_reserved_vector(const unsigned int arg) { + unsigned int siz = _cimg_mp_size(arg); + int *ptr = memtype.data(arg + 1); + while (siz-->0) *(ptr++) = -1; + } + + unsigned int scalar0(const mp_func op) { + const unsigned int pos = scalar(); + CImg::vector((ulongT)op,pos).move_to(code); + return_new_comp = true; + return pos; + } + + unsigned int scalar1(const mp_func op, const unsigned int arg1) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1).move_to(code); + return pos; + } + + unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2).move_to(code); + return pos; + } + + unsigned int scalar3(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code); + return pos; + } + + unsigned int scalar4(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code); + return pos; + } + + unsigned int scalar5(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); + return pos; + } + + unsigned int scalar6(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); + return pos; + } + + unsigned int scalar7(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6, + const unsigned int arg7) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); + return pos; + } + + void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) { + const unsigned int siz = _cimg_mp_size(pos); + if (siz>24) CImg::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]); + } + } + + void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) { + const unsigned int siz = _cimg_mp_size(pos); + if (siz>24) CImg::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); + } + } + + unsigned int vector1_v(const mp_func op, const unsigned int arg1) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg2), + pos = is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + // Evaluation functions, known by the parser. + // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT), + // so we can store pointers to them directly in the opcode vectors. +#ifdef _mp_arg +#undef _mp_arg +#endif +#define _mp_arg(x) mp.mem[mp.opcode[x]] + + static double mp_abs(_cimg_math_parser& mp) { + return cimg::abs(_mp_arg(2)); + } + + static double mp_add(_cimg_math_parser& mp) { + return _mp_arg(2) + _mp_arg(3); + } + + static double mp_acos(_cimg_math_parser& mp) { + return std::acos(_mp_arg(2)); + } + + static double mp_acosh(_cimg_math_parser& mp) { + return cimg::acosh(_mp_arg(2)); + } + + static double mp_asinh(_cimg_math_parser& mp) { + return cimg::asinh(_mp_arg(2)); + } + + static double mp_atanh(_cimg_math_parser& mp) { + return cimg::atanh(_mp_arg(2)); + } + + static double mp_arg(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(4); + const unsigned int + nb_args = (unsigned int)mp.opcode[2] - 4, + ind = _ind<0?_ind + nb_args:(unsigned int)_ind, + siz = (unsigned int)mp.opcode[3]; + if (siz>0) { + if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); + else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); + return cimg::type::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + static double mp_arg0(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(4); + const unsigned int + nb_args = (unsigned int)mp.opcode[2] - 4, + ind = _ind<0?_ind + nb_args:_ind + 1U, + siz = (unsigned int)mp.opcode[3]; + if (siz>0) { + if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); + else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); + return cimg::type::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + static double mp_argkth(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = mp_kth(mp); + for (unsigned int i = 4; ival) { val = _val; argval = i - 3; } + } + return (double)argval; + } + + static double mp_argmaxabs(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3), absval = cimg::abs(val); + unsigned int argval = 0; + for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; argval = i - 3; } + } + return (double)argval; + } + + static double mp_asin(_cimg_math_parser& mp) { + return std::asin(_mp_arg(2)); + } + + static double mp_atan(_cimg_math_parser& mp) { + return std::atan(_mp_arg(2)); + } + + static double mp_atan2(_cimg_math_parser& mp) { + return std::atan2(_mp_arg(2),_mp_arg(3)); + } + + static double mp_avg(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i>(unsigned int)_mp_arg(3)); + } + + static double mp_bitwise_xor(_cimg_math_parser& mp) { + return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3)); + } + + static double mp_bool(_cimg_math_parser& mp) { + return (double)(bool)_mp_arg(2); + } + + static double mp_break(_cimg_math_parser& mp) { + mp.break_type = 1; + mp.p_code = mp.p_break - 1; + return cimg::type::nan(); + } + + static double mp_breakpoint(_cimg_math_parser& mp) { + cimg_abort_init; + cimg_abort_test; + cimg::unused(mp); + return cimg::type::nan(); + } + +#ifdef cimg_mp_func_run + static double mp_run(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + CImg str = _str>'x'; + cimg_mp_func_run(str._data); + return cimg::type::nan(); + } +#endif + + static double mp_cbrt(_cimg_math_parser& mp) { + return cimg::cbrt(_mp_arg(2)); + } + + static double mp_ceil(_cimg_math_parser& mp) { + return std::ceil(_mp_arg(2)); + } + + static double mp_complex_abs(_cimg_math_parser& mp) { + return cimg::_hypot(_mp_arg(2),_mp_arg(3)); + } + + static double mp_complex_conj(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = real; + ptrd[1] = -imag; + return cimg::type::nan(); + } + + static double mp_complex_div_sv(_cimg_math_parser& mp) { + const double + *ptr2 = &_mp_arg(3) + 1, + r1 = _mp_arg(2), + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = r1*r2/denom; + *ptrd = -r1*i2/denom; + return cimg::type::nan(); + } + + static double mp_complex_div_vv(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = (r1*r2 + i1*i2)/denom; + *ptrd = (r2*i1 - r1*i2)/denom; + return cimg::type::nan(); + } + + static double mp_complex_exp(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = exp_real*std::cos(imag); + ptrd[1] = exp_real*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_log(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = 0.5*std::log(real*real + imag*imag); + ptrd[1] = std::atan2(imag,real); + return cimg::type::nan(); + } + + static double mp_complex_mul(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + *(ptrd++) = r1*r2 - i1*i2; + *(ptrd++) = r1*i2 + r2*i1; + return cimg::type::nan(); + } + + static void _mp_complex_pow(const double r1, const double i1, + const double r2, const double i2, + double *ptrd) { + double ro, io; + if (cimg::abs(i2)<1e-15) { // Exponent is real + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) { + if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; } + else ro = io = 0; + } else { + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2), + phio = r2*phi1; + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + } else { // Exponent is complex + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0; + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1), + phio = r2*phi1 + 0.5*i2*std::log(mod1_2); + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + *(ptrd++) = ro; + *ptrd = io; + } + + static double mp_complex_pow_ss(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_sv(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vs(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vv(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_cos(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::cos(real)*std::cosh(imag); + ptrd[1] = -std::sin(real)*std::sinh(imag); + return cimg::type::nan(); + } + + static double mp_complex_sin(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sin(real)*std::cosh(imag); + ptrd[1] = std::cos(real)*std::sinh(imag); + return cimg::type::nan(); + } + + static double mp_complex_tan(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sin(2*real)/denom; + ptrd[1] = std::sinh(2*imag)/denom; + return cimg::type::nan(); + } + + static double mp_complex_cosh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::cosh(real)*std::cos(imag); + ptrd[1] = std::sinh(real)*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_sinh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sinh(real)*std::cos(imag); + ptrd[1] = std::cosh(real)*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_tanh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sinh(2*real)/denom; + ptrd[1] = std::sin(2*imag)/denom; + return cimg::type::nan(); + } + + static double mp_continue(_cimg_math_parser& mp) { + mp.break_type = 2; + mp.p_code = mp.p_break - 1; + return cimg::type::nan(); + } + + static double mp_convolve(_cimg_math_parser &mp) { + return _mp_correlate(mp,true); + } + + static double mp_copy(_cimg_math_parser& mp) { + return _mp_arg(2); + } + + static double mp_correlate(_cimg_math_parser &mp) { + return _mp_correlate(mp,false); + } + + static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) { + double *ptrd = &_mp_arg(1) + 1; + const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1; + const unsigned int + wA = (unsigned int)mp.opcode[3], + hA = (unsigned int)mp.opcode[4], + dA = (unsigned int)mp.opcode[5], + sA = (unsigned int)mp.opcode[6], + wM = (unsigned int)mp.opcode[8], + hM = (unsigned int)mp.opcode[9], + dM = (unsigned int)mp.opcode[10], + sM = (unsigned int)mp.opcode[11], + boundary_conditions = (unsigned int)_mp_arg(12), + channel_mode = (unsigned int)mp.opcode[14], + xcenter = (unsigned int)_mp_arg(15), + ycenter = (unsigned int)_mp_arg(16), + zcenter = (unsigned int)_mp_arg(17), + xstart = (unsigned int)mp.opcode[18], + ystart = (unsigned int)mp.opcode[19], + zstart = (unsigned int)mp.opcode[20], + xend = (unsigned int)mp.opcode[21], + yend = (unsigned int)mp.opcode[22], + zend = (unsigned int)mp.opcode[23]; + const bool + is_normalized = (bool)_mp_arg(13); + const float + xstride = (float)_mp_arg(24), + ystride = (float)_mp_arg(25), + zstride = (float)_mp_arg(26), + xdilation = (float)_mp_arg(27), + ydilation = (float)_mp_arg(28), + zdilation = (float)_mp_arg(29); + CImg res; + if (is_convolve) res = CImg(ptrA,wA,hA,dA,sA,true). + get_convolve(CImg(ptrM,wM,hM,dM,sM,true), + boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter, + xstart,ystart,zstart, + xend,yend,zend, + xstride,ystride,zstride, + xdilation,ydilation,zdilation); + else res = CImg(ptrA,wA,hA,dA,sA,true). + get_correlate(CImg(ptrM,wM,hM,dM,sM,true), + boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter, + xstart,ystart,zstart, + xend,yend,zend, + xstride,ystride,zstride, + xdilation,ydilation,zdilation); + CImg(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res; + return cimg::type::nan(); + } + + static double mp_cos(_cimg_math_parser& mp) { + return std::cos(_mp_arg(2)); + } + + static double mp_cosh(_cimg_math_parser& mp) { + return std::cosh(_mp_arg(2)); + } + + static double mp_critical(_cimg_math_parser& mp) { + const ulongT g_target = mp.opcode[1]; + cimg_pragma_openmp(critical(mp_critical)) + { + for (const CImg *const p_end = ++mp.p_code + mp.opcode[2]; + mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + } + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const unsigned int + dx = (unsigned int)mp.opcode[7], + dy = (unsigned int)mp.opcode[8], + dz = (unsigned int)mp.opcode[9], + dc = (unsigned int)mp.opcode[10]; + const unsigned int boundary_conditions = (unsigned int)_mp_arg(11); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); + else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, + x + dx - 1,y + dy - 1, + z + dz - 1,c + dc - 1, + boundary_conditions); + return cimg::type::nan(); + } + + static double mp_cross(_cimg_math_parser& mp) { + CImg + vout(&_mp_arg(1) + 1,1,3,1,1,true), + v1(&_mp_arg(2) + 1,1,3,1,1,true), + v2(&_mp_arg(3) + 1,1,3,1,1,true); + (vout = v1).cross(v2); + return cimg::type::nan(); + } + + static double mp_cut(_cimg_math_parser& mp) { + double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); + return valcmax?cmax:val; + } + + static double mp_date(_cimg_math_parser& mp) { + const unsigned int + siz_out = (unsigned int)mp.opcode[2], + siz_arg1 = (unsigned int)mp.opcode[4], + siz_arg2 = (unsigned int)mp.opcode[6]; + double *ptr_out = &_mp_arg(1) + (siz_out?1:0); + const double + *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0), + *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1; + + if (!ptr_arg2) { // No filename specified + if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1); + if (siz_arg1==~0U) for (unsigned int k = 0; k::nan(); + } + + // Filename specified. + CImg ss(siz_arg2 + 1); + cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i]; + ss.back() = 0; + if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1); + for (unsigned int k = 0; k::nan(); + } + + static double mp_debug(_cimg_math_parser& mp) { + CImg expr(mp.opcode[2] - 4); + { + const ulongT *ptrs = mp.opcode._data + 4; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + } + cimg::strellipsize(expr); + const ulongT g_target = mp.opcode[1]; + +#if cimg_use_openmp==0 + const unsigned int n_thread = 0; +#else + const unsigned int n_thread = omp_get_thread_num(); +#endif + cimg_pragma_openmp(critical(mp_debug)) + { + std::fprintf(cimg::output(), + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" + "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width); + std::fflush(cimg::output()); + mp.debug_indent+=3; + } + const CImg *const p_end = ++mp.p_code + mp.opcode[3]; + CImg _op; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; + + _op.assign(1,op._height - 1); + const ulongT *ptrs = op._data + 1; + for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd mem[%u] = %.17g", + (void*)&mp,n_thread,mp.debug_indent,' ', + (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), + (unsigned int)target,mp.mem[target]); + std::fflush(cimg::output()); + } + } + cimg_pragma_openmp(critical(mp_debug)) + { + mp.debug_indent-=3; + std::fprintf(cimg::output(), + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" + "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); + std::fflush(cimg::output()); + } + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_decrement(_cimg_math_parser& mp) { + return _mp_arg(2) - 1; + } + + static double mp_det(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + return CImg(ptrs,k,k,1,1,true).det(); + } + + static double mp_diag(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3; + double *ptrd = &_mp_arg(1) + 1; + std::memset(ptrd,0,siz*siz*sizeof(double)); + for (unsigned int i = 3; i::nan(); + } + + static double mp_display_memory(_cimg_math_parser& mp) { + cimg::unused(mp); + std::fputc('\n',cimg::output()); + CImg title(128); + cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width); + mp.mem.display(title); + return cimg::type::nan(); + } + + static double mp_display(_cimg_math_parser& mp) { + const unsigned int + _siz = (unsigned int)mp.opcode[3], + siz = _siz?_siz:1; + const double *const ptr = &_mp_arg(1) + (_siz?1:0); + const int + w = (int)_mp_arg(4), + h = (int)_mp_arg(5), + d = (int)_mp_arg(6), + s = (int)_mp_arg(7); + CImg img; + if (w>0 && h>0 && d>0 && s>0) { + if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true); + else img.assign(ptr,siz).resize(w,h,d,s,-1); + } else img.assign(ptr,1,siz,1,1,true); + + CImg expr(mp.opcode[2] - 8); + const ulongT *ptrs = mp.opcode._data + 8; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + ((CImg::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr); + cimg::strellipsize(expr); + std::fputc('\n',cimg::output()); + img.display(expr._data); + return cimg::type::nan(); + } + + static double mp_div(_cimg_math_parser& mp) { + return _mp_arg(2)/_mp_arg(3); + } + + static double mp_dot(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[4]; + return CImg(&_mp_arg(2) + 1,1,siz,1,1,true). + dot(CImg(&_mp_arg(3) + 1,1,siz,1,1,true)); + } + + static double mp_do(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_body = ++mp.p_code, + *const p_cond = p_body + mp.opcode[3], + *const p_end = p_cond + mp.opcode[4]; + const unsigned int vsiz = (unsigned int)mp.opcode[5]; + if (mp.opcode[6]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[7]) mp.mem[mem_cond] = 0; + + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + do { + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } while (mp.mem[mem_cond]); + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_draw(_cimg_math_parser& mp) { + const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + unsigned int + dx = (unsigned int)mp.opcode[8], + dy = (unsigned int)mp.opcode[9], + dz = (unsigned int)mp.opcode[10], + dc = (unsigned int)mp.opcode[11]; + dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); + dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); + dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); + dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); + + const ulongT sizS = mp.opcode[2]; + if (sizS<(ulongT)dx*dy*dz*dc) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); + const float opacity = (float)_mp_arg(12); + + if (img._data) { + if (mp.opcode[13]!=~0U) { // Opacity mask specified + const ulongT sizM = mp.opcode[14]; + if (sizM<(ulongT)dx*dy*dz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); + img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); + } else img.draw_image(x,y,z,c,S,opacity); + } + return cimg::type::nan(); + } + + static double mp_echo(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type::nan(); } // No arguments + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + const CImg str = _str>'x'; + std::fprintf(cimg::output(),"\n%s",str._data); + return cimg::type::nan(); + } + + static double mp_ellipse(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + CImg color(img._spectrum,1,1,1,0); + bool is_invalid_arguments = false, is_outlined = false; + float r1 = 0, r2 = 0, angle = 0, opacity = 1; + unsigned int i = 4, pattern = ~0U; + int x0 = 0, y0 = 0; + if (i>=i_end) is_invalid_arguments = true; + else { + x0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + y0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + r1 = (float)_mp_arg(i++); + if (i>=i_end) r2 = r1; + else { + r2 = (float)_mp_arg(i++); + if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + + static double mp_eq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)==_mp_arg(3)); + } + + static double mp_erf(_cimg_math_parser& mp) { + return std::erf(_mp_arg(2)); + } + + static double mp_erfinv(_cimg_math_parser& mp) { + return cimg::erfinv(_mp_arg(2)); + } + + static double mp_exp(_cimg_math_parser& mp) { + return std::exp(_mp_arg(2)); + } + + static double mp_expr(_cimg_math_parser& mp) { + const unsigned int + sizs = (unsigned int)mp.opcode[3], + w = (unsigned int)mp.opcode[4], + h = (unsigned int)mp.opcode[5], + d = (unsigned int)mp.opcode[6], + s = (unsigned int)mp.opcode[7], + sizd = w*h*d*s; + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1); + CImg ss(sizs + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + if (!sizd) return CImg(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result + CImg(++ptrd,w,h,d,s,true) = CImg(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout); + return cimg::type::nan(); + } + + static double mp_eye(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int k = (unsigned int)mp.opcode[2]; + CImg(ptrd,k,k,1,1,true).identity_matrix(); + return cimg::type::nan(); + } + + static double mp_f2ui(_cimg_math_parser& mp) { + return (double)cimg::float2uint((float)_mp_arg(2)); + } + + static double mp_factorial(_cimg_math_parser& mp) { + return cimg::factorial((int)_mp_arg(2)); + } + + static double mp_fibonacci(_cimg_math_parser& mp) { + return cimg::fibonacci((int)_mp_arg(2)); + } + + static double mp_fill(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double + *ptrd = &_mp_arg(1), + *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0, + *const ptrs = &_mp_arg(4); + if (siz) ++ptrd; else ++siz; // Fill vector-valued slot + const CImg + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[5]; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + unsigned int it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + *ptrc = (double)it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return *ptrd; + } + + static double mp_find(_cimg_math_parser& mp) { + const int _step = (int)_mp_arg(6), step = _step?_step:-1; + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1); + if (ind<0 || ind>=(longT)siz) return -1.; + const double + *const ptrb = &_mp_arg(2) + 1, + *const ptre = ptrb + siz, + val = _mp_arg(4), + *ptr = ptrb + ind; + + // Forward search + if (step>0) { + while (ptr=ptre?-1.:(double)(ptr - ptrb); + } + + // Backward search. + while (ptr>=ptrb && *ptr!=val) ptr+=step; + return ptr0?0:siz1 - 1); + if (ind<0 || ind>=(longT)siz1) return -1.; + const double + *const ptr1b = &_mp_arg(2) + 1, + *const ptr1e = ptr1b + siz1, + *const ptr2b = &_mp_arg(4) + 1, + *const ptr2e = ptr2b + siz2, + *ptr1 = ptr1b + ind, + *p1 = 0, + *p2 = 0; + + // Forward search. + if (step>0) { + do { + while (ptr1=ptr1e) return -1.; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b && *ptr1!=*ptr2b) ptr1+=step; + if (ptr1=ptr1b); + return p2 + *const p_init = ++mp.p_code, + *const p_cond = p_init + mp.opcode[4], + *const p_body = p_cond + mp.opcode[5], + *const p_post = p_body + mp.opcode[6], + *const p_end = p_post + mp.opcode[7]; + const unsigned int vsiz = (unsigned int)mp.opcode[2]; + bool is_cond = false; + if (mp.opcode[8]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[9]) mp.mem[mem_cond] = 0; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + for (mp.p_code = p_init; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + + if (!mp.break_type) do { + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; + + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond && !mp.break_type) { + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + + for (mp.p_code = p_post; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } + } while (is_cond); + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_fsize(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const ulongT siz = (ulongT)mp.opcode[3]; + CImg ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::fsize(ss); + } + + static double mp_g(_cimg_math_parser& mp) { + cimg::unused(mp); + return cimg::grand(&mp.rng); + } + + static double mp_gauss(_cimg_math_parser& mp) { + const double x = _mp_arg(2), s = _mp_arg(3); + return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1); + } + +#ifdef cimg_mp_func_get + static double mp_get(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1); + const unsigned int + sizs = (unsigned int)mp.opcode[3], + sizd = (unsigned int)mp.opcode[4]; + const bool to_string = (bool)mp.opcode[5]; + CImg ss(sizs + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data); + else cimg_mp_func_get(ptrd,0,to_string,ss._data); + return cimg::type::nan(); + } +#endif + + static double mp_gcd(_cimg_math_parser& mp) { + return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3)); + } + +#ifdef cimg_mp_func_name + static double mp_name(_cimg_math_parser& mp) { + double *const ptr = &_mp_arg(1) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) std::memset(ptr,0,siz*sizeof(double)); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + cimg_mp_func_name(ind,ptr,siz); + } + return cimg::type::nan(); + } +#endif + + static double mp_gt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>_mp_arg(3)); + } + + static double mp_gte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>=_mp_arg(3)); + } + + static double mp_i(_cimg_math_parser& mp) { + return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y], + (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0); + } + + static double mp_if(_cimg_math_parser& mp) { + const bool is_cond = (bool)_mp_arg(2); + const ulongT + mem_left = mp.opcode[3], + mem_right = mp.opcode[4]; + const CImg + *const p_right = ++mp.p_code + mp.opcode[5], + *const p_end = p_right + mp.opcode[6]; + const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7]; + if (is_cond) for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + else for (mp.p_code = p_right; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.p_code==mp.p_break) --mp.p_code; + else mp.p_code = p_end - 1; + if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz); + return mp.mem[is_cond?mem_left:mem_right]; + } + + static double mp_image_d(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.depth(); + } + + static double mp_image_display(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + CImg title(256); + std::fputc('\n',cimg::output()); + cimg_snprintf(title,title._width,"[ Image #%u ]",ind); + img.display(title); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_h(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.height(); + } + + static double mp_image_median(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.median(); + } + + static double mp_image_norm(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.magnitude(); + } + + static double mp_image_print(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + CImg title(256); + std::fputc('\n',cimg::output()); + cimg_snprintf(title,title._width,"[ Image #%u ]",ind); + img.print(title); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_resize(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + const double + _w = mp.opcode[3]==~0U?-100:_mp_arg(3), + _h = mp.opcode[4]==~0U?-100:_mp_arg(4), + _d = mp.opcode[5]==~0U?-100:_mp_arg(5), + _s = mp.opcode[6]==~0U?-100:_mp_arg(6); + const unsigned int + w = (unsigned int)(_w>=0?_w:-_w*img.width()/100), + h = (unsigned int)(_h>=0?_h:-_h*img.height()/100), + d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100), + s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100), + interp = (int)_mp_arg(7); + if (mp.is_fill && img._data==mp.imgout._data) { + cimg::mutex(6,0); + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': " + "Cannot both fill and resize image (%u,%u,%u,%u) " + "to new dimensions (%u,%u,%u,%u).", + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s); + } + const unsigned int + boundary = (int)_mp_arg(8); + const float + cx = (float)_mp_arg(9), + cy = (float)_mp_arg(10), + cz = (float)_mp_arg(11), + cc = (float)_mp_arg(12); + img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_s(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.spectrum(); + } + + static double mp_image_sort(_cimg_math_parser& mp) { + const bool is_increasing = (bool)_mp_arg(3); + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()), + axis = (unsigned int)_mp_arg(4); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + img.sort(is_increasing, + axis==0 || axis=='x'?'x': + axis==1 || axis=='y'?'y': + axis==2 || axis=='z'?'z': + axis==3 || axis=='c'?'c':0); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_stats(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) CImg(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats(); + } + return cimg::type::nan(); + } + + static double mp_image_w(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width(); + } + + static double mp_image_wh(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height(); + } + + static double mp_image_whd(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height()*img.depth(); + } + + static double mp_image_whds(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height()*img.depth()*img.spectrum(); + } + + static double mp_increment(_cimg_math_parser& mp) { + return _mp_arg(2) + 1; + } + + static double mp_inrange(_cimg_math_parser& mp) { + const unsigned int sizd = (unsigned int)mp.opcode[2]; + const bool + include_m = (bool)_mp_arg(9), + include_M = (bool)_mp_arg(10); + if (!sizd) { // Scalar result + const double val = _mp_arg(3); + const double m = _mp_arg(5), M = _mp_arg(7); + if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val=M):(val>M)) && (include_m?(val<=m):(val=m) + ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val=M):(val>M)) && (include_m?(val<=m):(val::nan(); + } + + static double mp_int(_cimg_math_parser& mp) { + return (double)(longT)_mp_arg(2); + } + + static double mp_ioff(_cimg_math_parser& mp) { + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const CImg &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::is_directory(ss); + } + + static double mp_isin(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = _mp_arg(3); + for (unsigned int i = 4; i::is_inf(_mp_arg(2)); + } + + static double mp_isint(_cimg_math_parser& mp) { + return (double)((double)(longT)_mp_arg(2)==_mp_arg(2)); + } + + static double mp_isfile(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const ulongT siz = (ulongT)mp.opcode[3]; + CImg ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::is_file(ss); + } + + static double mp_isnan(_cimg_math_parser& mp) { + return (double)cimg::type::is_nan(_mp_arg(2)); + } + + static double mp_ixyzc(_cimg_math_parser& mp) { + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.imgin; + const double + x = _mp_arg(2), y = _mp_arg(3), + z = _mp_arg(4), c = _mp_arg(5); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx &img = mp.imgin; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), + z = oz + _mp_arg(4), c = oc + _mp_arg(5); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx vals(i_end - 4); + double *p = vals.data(); + for (unsigned int i = 4; i &img = mp.listin[indi]; + const int _step = (int)_mp_arg(5), step = _step?_step:-1; + const ulongT siz = (ulongT)img.size(); + longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1); + if (ind<0 || ind>=(longT)siz) return -1.; + const T + *const ptrb = img.data(), + *const ptre = img.end(), + *ptr = ptrb + ind; + const double val = _mp_arg(3); + + // Forward search + if (step>0) { + while (ptr=ptre?-1.:(double)(ptr - ptrb); + } + + // Backward search. + while (ptr>=ptrb && (double)*ptr!=val) ptr+=step; + return ptr &img = mp.listin[indi]; + const int _step = (bool)_mp_arg(6), step = _step?_step:-1; + const ulongT + siz1 = (ulongT)img.size(), + siz2 = (ulongT)mp.opcode[4]; + longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1); + if (ind<0 || ind>=(longT)siz1) return -1.; + const T + *const ptr1b = img.data(), + *const ptr1e = ptr1b + siz1, + *ptr1 = ptr1b + ind, + *p1 = 0; + const double + *const ptr2b = &_mp_arg(3) + 1, + *const ptr2e = ptr2b + siz2, + *p2 = 0; + + // Forward search. + if (step>0) { + do { + while (ptr1=ptr1e) return -1.; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b && *ptr1!=*ptr2b) ptr1+=step; + if (ptr1=ptr1b); + return p2 &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.listin[ind]; + const double + x = _mp_arg(3), y = _mp_arg(4), + z = _mp_arg(5), c = _mp_arg(6); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx &img = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), + z = oz + _mp_arg(5), c = oc + _mp_arg(6); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); + return *mp.list_median[ind]; + } + + static double mp_list_norm(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + if (!mp.list_norm) mp.list_norm.assign(mp.listin._width); + if (!mp.list_norm[ind]) CImg::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]); + return *mp.list_norm[ind]; + } + + static double mp_list_set_ioff(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), y = (int)_mp_arg(4), + z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; + const int + x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_set_Joff_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(3)), + y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(3)), + y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_spectrum(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._spectrum; + } + + static double mp_list_stats(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + k = (unsigned int)mp.opcode[3]; + if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); + if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); + return mp.list_stats(ind,k); + } + + static double mp_list_wh(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height; + } + + static double mp_list_whd(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; + } + + static double mp_list_whds(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; + } + + static double mp_list_width(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width; + } + + static double mp_list_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4), + vsiz = (unsigned int)mp.opcode[5]; + const CImg &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_list_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7), + vsiz = (unsigned int)mp.opcode[8]; + const CImg &img = mp.listin[ind]; + const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_list_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4), + vsiz = (unsigned int)mp.opcode[5]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z]; + const CImg &img = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_list_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7), + vsiz = (unsigned int)mp.opcode[8]; + const CImg &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_log(_cimg_math_parser& mp) { + return std::log(_mp_arg(2)); + } + + static double mp_log10(_cimg_math_parser& mp) { + return std::log10(_mp_arg(2)); + } + + static double mp_log2(_cimg_math_parser& mp) { + return cimg::log2(_mp_arg(2)); + } + + static double mp_logical_and(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (!val_left) { mp.p_code = p_end - 1; return 0; } + const ulongT mem_right = mp.opcode[3]; + for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_logical_not(_cimg_math_parser& mp) { + return (double)!_mp_arg(2); + } + + static double mp_logical_or(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (val_left) { mp.p_code = p_end - 1; return 1; } + const ulongT mem_right = mp.opcode[3]; + for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_lowercase(_cimg_math_parser& mp) { + return cimg::lowercase(_mp_arg(2)); + } + + static double mp_lt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<_mp_arg(3)); + } + + static double mp_lte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<=_mp_arg(3)); + } + + static double mp_matrix_eig(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + CImg val, vec; + CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); + CImg(ptrd,1,k,1,1,true) = val; + CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); + return cimg::type::nan(); + } + + static double mp_matrix_invert(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + const bool use_LU = (bool)_mp_arg(4); + CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(use_LU); + return cimg::type::nan(); + } + + static double mp_matrix_mul(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode[4], + l = (unsigned int)mp.opcode[5], + m = (unsigned int)mp.opcode[6]; + CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); + return cimg::type::nan(); + } + + static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + const bool use_LU = (bool)_mp_arg(5); + CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU); + return cimg::type::nan(); + } + + static double mp_matrix_svd(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + CImg U, S, V; + CImg(ptr1,k,l,1,1,true).SVD(U,S,V); + CImg(ptrd,k,l,1,1,true) = U; + CImg(ptrd + k*l,1,k,1,1,true) = S; + CImg(ptrd + k*l + k,k,k,1,1,true) = V; + return cimg::type::nan(); + } + + static double mp_max(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; } + } + return val; + } + + static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref, + const longT siz, const long inc) { + const longT + off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind, + eoff = off + (siz - 1)*inc; + if (off<0 || eoff>=mp.mem.width()) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " + "Out-of-bounds variable pointer " + "(length: %ld, increment: %ld, offset start: %ld, " + "offset end: %ld, offset max: %u).", + mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1); + return &mp.mem[off]; + } + + static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref, + const longT siz, const long inc, const bool is_out) { + const unsigned ind = (unsigned int)p_ref[1]; + const CImg &img = is_out? + (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]): + (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]); + const bool is_relative = (bool)p_ref[2]; + int ox, oy, oz, oc; + longT off = 0; + if (is_relative) { + ox = (int)mp.mem[_cimg_mp_slot_x]; + oy = (int)mp.mem[_cimg_mp_slot_y]; + oz = (int)mp.mem[_cimg_mp_slot_z]; + oc = (int)mp.mem[_cimg_mp_slot_c]; + off = img.offset(ox,oy,oz,oc); + } + if ((*p_ref)%2) { + const int + x = (int)mp.mem[p_ref[3]], + y = (int)mp.mem[p_ref[4]], + z = (int)mp.mem[p_ref[5]], + c = *p_ref==5?0:(int)mp.mem[p_ref[6]]; + off+=img.offset(x,y,z,c); + } else off+=(longT)mp.mem[p_ref[3]]; + const longT eoff = off + (siz - 1)*inc; + if (off<0 || eoff>=(longT)img.size()) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " + "Out-of-bounds image pointer " + "(length: %ld, increment: %ld, offset start: %ld, " + "offset end: %ld, offset max: %lu).", + mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1); + return (float*)&img[off]; + } + + static double mp_memcopy(_cimg_math_parser& mp) { + longT siz = (longT)_mp_arg(4); + const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6); + const float + _opacity = (float)_mp_arg(7), + opacity = (float)cimg::abs(_opacity), + omopacity = 1 - std::max(_opacity,0.f); + if (siz>0) { + const bool + is_doubled = mp.opcode[8]<=1, + is_doubles = mp.opcode[15]<=1; + if (is_doubled && is_doubles) { // (double*) <- (double*) + double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); + const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); + if (inc_d==1 && inc_s==1 && _opacity>=1) { + if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double)); + else std::memmove(ptrd,ptrs,siz*sizeof(double)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) { + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else { // Overlapping buffers + CImg buf((unsigned int)siz); + cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; } + ptrs = buf; + if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } + } + } + } else if (is_doubled && !is_doubles) { // (double*) <- (float*) + double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); + const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false); + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else if (!is_doubled && is_doubles) { // (float*) <- (double*) + float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true); + const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); + if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; } + } else { // (float*) <- (float*) + float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true); + const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false); + if (inc_d==1 && inc_s==1 && _opacity>=1) { + if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float)); + else std::memmove(ptrd,ptrs,siz*sizeof(float)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) { + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else { // Overlapping buffers + CImg buf((unsigned int)siz); + cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; } + ptrs = buf; + if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } + } + } + } + } + return _mp_arg(1); + } + + static double mp_min(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i(ptrd,wS,wD,1,1,true) = CImg(ptrS,wS,hS,1,1,false). + project_matrix(CImg(ptrD,wD,hS,1,1,true),method,max_iter,max_residual); + return cimg::type::nan(); + } + + static double mp_mul(_cimg_math_parser& mp) { + return _mp_arg(2)*_mp_arg(3); + } + + static double mp_mul2(_cimg_math_parser& mp) { + return _mp_arg(2)*_mp_arg(3)*_mp_arg(4); + } + + static double mp_neq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)!=_mp_arg(3)); + } + + static double mp_norm0(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + switch (i_end - 3) { + case 1 : return _mp_arg(3)!=0; + case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0); + } + double res = 0; + for (unsigned int i = 3; ires) res = val; + } + return res; + } + + static double mp_normp(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + if (i_end==4) return cimg::abs(_mp_arg(3)); + const double p = (double)mp.opcode[3]; + double res = 0; + for (unsigned int i = 4; i0?res:0.; + } + + static double mp_permutations(_cimg_math_parser& mp) { + return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4)); + } + + static double mp_polygon(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + bool is_invalid_arguments = i_end<=4, is_outlined = false; + if (!is_invalid_arguments) { + int nbv = (int)_mp_arg(4); + if (!nbv) is_invalid_arguments = true; + else { + if (nbv<0) { nbv = -nbv; is_outlined = true; } + CImg points(nbv,2,1,1,0); + CImg color(img._spectrum,1,1,1,0); + float opacity = 1; + unsigned int i = 5, pattern=~0U; + cimg_foroff(points,k) if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + + static double mp_pow(_cimg_math_parser& mp) { + const double v = _mp_arg(2), p = _mp_arg(3); + return std::pow(v,p); + } + + static double mp_pow0_25(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return std::sqrt(std::sqrt(val)); + } + + static double mp_pow3(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val; + } + + static double mp_pow4(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val*val; + } + + static double mp_print(_cimg_math_parser& mp) { + const double val = _mp_arg(1); + const bool print_char = (bool)mp.opcode[3]; + cimg_pragma_openmp(critical(mp_print)) + { + CImg _expr(mp.opcode[2] - 4); + const ulongT *ptrs = mp.opcode._data + 4; + cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(_expr); + cimg::mutex(6); + if (print_char) + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'", + _expr._data,val,(int)val); + else + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g", + _expr._data,val); + std::fflush(cimg::output()); + cimg::mutex(6,0); + } + return val; + } + + static double mp_prod(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[4]; + + if (nb_it>0) { + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + double it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + *ptrc = it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + mp.break_type = _break_type; + } + + mp.p_code = p_end - 1; + return *ptrs; + } + + static double mp_rol(_cimg_math_parser& mp) { + return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3)); + } + + static double mp_ror(_cimg_math_parser& mp) { + return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3)); + } + + static double mp_rot2d(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float + theta = (float)_mp_arg(2)*cimg::PI/180, + ca = std::cos(theta), + sa = std::sin(theta); + *(ptrd++) = ca; + *(ptrd++) = -sa; + *(ptrd++) = sa; + *ptrd = ca; + return cimg::type::nan(); + } + + static double mp_rot3d(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); + CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + return cimg::type::nan(); + } + + static double mp_round(_cimg_math_parser& mp) { + return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); + } + + static double mp_self_add(_cimg_math_parser& mp) { + return _mp_arg(1)+=_mp_arg(2); + } + + static double mp_self_bitwise_and(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val & (longT)_mp_arg(2)); + } + + static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val<<(unsigned int)_mp_arg(2)); + } + + static double mp_self_bitwise_or(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val | (longT)_mp_arg(2)); + } + + static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val>>(unsigned int)_mp_arg(2)); + } + + static double mp_self_decrement(_cimg_math_parser& mp) { + return --_mp_arg(1); + } + + static double mp_self_increment(_cimg_math_parser& mp) { + return ++_mp_arg(1); + } + + static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2]; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode[2] = mp.opcode[4]; // Scalar argument + l_opcode.swap(mp.opcode); + ulongT &target = mp.opcode[1]; + while (siz-->0) { target = ptrd++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + ulongT &target = mp.opcode[1], &argument = mp.opcode[2]; + while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_self_mul(_cimg_math_parser& mp) { + return _mp_arg(1)*=_mp_arg(2); + } + + static double mp_self_div(_cimg_math_parser& mp) { + return _mp_arg(1)/=_mp_arg(2); + } + + static double mp_self_modulo(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = cimg::mod(val,_mp_arg(2)); + } + + static double mp_self_pow(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = std::pow(val,_mp_arg(2)); + } + + static double mp_self_sub(_cimg_math_parser& mp) { + return _mp_arg(1)-=_mp_arg(2); + } + + static double mp_set_ioff(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + x = (int)_mp_arg(2), y = (int)_mp_arg(3), + z = (int)_mp_arg(4), c = (int)_mp_arg(5); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; + const int + x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Ixyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + x = (int)_mp_arg(2), + y = (int)_mp_arg(3), + z = (int)_mp_arg(4); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const int + x = (int)_mp_arg(2), + y = (int)_mp_arg(3), + z = (int)_mp_arg(4); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_set_Joff_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Jxyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(2)), + y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(2)), + y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_shift(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + const int + shift = (int)_mp_arg(4), + boundary_conditions = (int)_mp_arg(5); + CImg(ptrd,siz,1,1,1,true) = CImg(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions); + return cimg::type::nan(); + } + + static double mp_sign(_cimg_math_parser& mp) { + return cimg::sign(_mp_arg(2)); + } + + static double mp_sin(_cimg_math_parser& mp) { + return std::sin(_mp_arg(2)); + } + + static double mp_sinc(_cimg_math_parser& mp) { + return cimg::sinc(_mp_arg(2)); + } + + static double mp_sinh(_cimg_math_parser& mp) { + return std::sinh(_mp_arg(2)); + } + + static double mp_solve(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode[4], + l = (unsigned int)mp.opcode[5], + m = (unsigned int)mp.opcode[6]; + CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,false).solve(CImg(ptr1,k,l,1,1,true)); + return cimg::type::nan(); + } + + static double mp_sort(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const bool is_increasing = (bool)_mp_arg(4); + const unsigned int + siz = (unsigned int)mp.opcode[3], + nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5), + siz_elt = (unsigned int)_mp_arg(6); + const ulongT sn = siz_elt*nb_elts; + if (sn>siz || siz_elt<1) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': " + "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid " + "for sorting a vector of size %u.", + mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz); + CImg(ptrd,siz_elt,nb_elts,1,1,true) = CImg(ptrs,siz_elt,nb_elts,1,1,true). + get_sort(is_increasing,siz_elt>1?'y':0); + if (sn(ptrd + sn,siz - sn,1,1,1,true) = CImg(ptrs + sn,siz - sn,1,1,1,true); + return cimg::type::nan(); + } + + static double mp_sqr(_cimg_math_parser& mp) { + return cimg::sqr(_mp_arg(2)); + } + + static double mp_sqrt(_cimg_math_parser& mp) { + return std::sqrt(_mp_arg(2)); + } + + static double mp_srand(_cimg_math_parser& mp) { + mp.rng = (cimg_uint64)_mp_arg(2); + return cimg::type::nan(); + } + + static double mp_srand0(_cimg_math_parser& mp) { + cimg::srand(&mp.rng); + +#if cimg_use_openmp!=0 + mp.rng+=omp_get_thread_num(); +#endif + return cimg::type::nan(); + } + + static double mp_std(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i0) mp.mem[ptrd++] = (double)*(ptrs++); + return cimg::type::nan(); + } + +#ifdef cimg_mp_func_store + static double mp_store(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2), + *ptr2 = &_mp_arg(4) + 1; + const unsigned int + siz1 = (unsigned int)mp.opcode[3], + siz2 = (unsigned int)mp.opcode[5]; + const int + w = (int)_mp_arg(6), + h = (int)_mp_arg(7), + d = (int)_mp_arg(8), + s = (int)_mp_arg(9); + + const bool is_compressed = (bool)_mp_arg(10); + if (w<0 || h<0 || d<0 || s<0) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': " + "Specified image dimensions (%d,%d,%d,%d) are invalid.", + pixel_type(),w,h,d,s); + CImg ss(siz2 + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i]; + ss.back() = 0; + if (siz1) cimg_mp_func_store(ptr1 + 1,siz1, + (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); + else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); + return cimg::type::nan(); + } +#endif + + static double mp_stov(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2); + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)_mp_arg(4); + const bool is_strict = (bool)_mp_arg(5); + double val = cimg::type::nan(); + if (ind<0 || ind>=(longT)siz) return val; + if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val; + + CImg ss(siz + 1 - ind); + ptrs+=1 + ind; + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + + const char *s = ss._data; + while (*s && *s<=32) ++s; + const bool is_negative = *s=='-'; + if (is_negative || *s=='+') ++s; + int err = 0; + char sep; + + if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number + unsigned int ival; + err = cimg_sscanf(s + 2,"%x%c",&ival,&sep); + if (err>0) val = (double)ival; + } else if (*s>32) { // Decimal number + err = cimg_sscanf(s,"%lf%c",&val,&sep); +#if cimg_OS==2 + // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able + // to read those particular values. + if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) { + if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type::inf(); err = 1 + (s[3]!=0); } + else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type::nan(); err = 1 + (s[3]!=0); } + } +#endif + } + if (err<=0 || (is_strict && err!=1)) return cimg::type::nan(); + if (is_negative) val = -val; + return val; + } + + static double mp_string(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(4 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + const CImg str = _str>'x'; + const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]); + std::memset(ptrd,0,mp.opcode[2]*sizeof(double)); + for (unsigned int k = 0; k::nan(); + } + + static double mp_sub(_cimg_math_parser& mp) { + return _mp_arg(2) - _mp_arg(3); + } + + static double mp_sum(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i(ptrs,k,k,1,1,true).trace(); + } + + static double mp_transpose(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + CImg(ptrd,l,k,1,1,true) = CImg(ptrs,k,l,1,1,true).get_transpose(); + return cimg::type::nan(); + } + + static double mp_u(_cimg_math_parser& mp) { + return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng); + } + + static double mp_ui2f(_cimg_math_parser& mp) { + return (double)cimg::uint2float((unsigned int)_mp_arg(2)); + } + + static double mp_uppercase(_cimg_math_parser& mp) { + return cimg::uppercase(_mp_arg(2)); + } + + static double mp_var(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i::nan(); + } + + static double mp_vector_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const longT + length = (longT)mp.opcode[3], + start = (longT)_mp_arg(4), + sublength = (longT)mp.opcode[5], + step = (longT)_mp_arg(6); + if (start<0 || start + step*(sublength-1)>=length) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': " + "Out-of-bounds sub-vector request " + "(length: %ld, start: %ld, sub-length: %ld, step: %ld).", + mp.imgin.pixel_type(),length,start,sublength,step); + ptrs+=start; + if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double)); + else for (longT k = 0; k::nan(); + } + + static double mp_vector_init(_cimg_math_parser& mp) { + unsigned int + ptrs = 4U, + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[3]; + switch (mp.opcode[2] - 4) { + case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given + case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break; + default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; } + } + return cimg::type::nan(); + } + + static double mp_vector_eq(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(4) + 1; + unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n; + const int N = (int)_mp_arg(6); + const bool case_sensitive = (bool)_mp_arg(7); + bool still_equal = true; + double value; + if (!N) return true; + + // Compare all values. + if (N<0) { + if (p1>0 && p2>0) { // Vector == vector + if (p1!=p2) return false; + if (case_sensitive) + while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++); + else + while (still_equal && p1--) + still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); + return still_equal; + } else if (p1>0 && !p2) { // Vector == scalar + value = _mp_arg(4); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && p1--) still_equal = *(ptr1++)==value; + return still_equal; + } else if (!p1 && p2>0) { // Scalar == vector + value = _mp_arg(2); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && p2--) still_equal = *(ptr2++)==value; + return still_equal; + } else { // Scalar == scalar + if (case_sensitive) return _mp_arg(2)==_mp_arg(4); + else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); + } + } + + // Compare only first N values. + if (p1>0 && p2>0) { // Vector == vector + n = cimg::min((unsigned int)N,p1,p2); + if (case_sensitive) + while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++); + else + while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); + return still_equal; + } else if (p1>0 && !p2) { // Vector == scalar + n = std::min((unsigned int)N,p1); + value = _mp_arg(4); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && n--) still_equal = *(ptr1++)==value; + return still_equal; + } else if (!p1 && p2>0) { // Scalar == vector + n = std::min((unsigned int)N,p2); + value = _mp_arg(2); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && n--) still_equal = *(ptr2++)==value; + return still_equal; + } // Scalar == scalar + if (case_sensitive) return _mp_arg(2)==_mp_arg(4); + return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); + } + + static double mp_vector_lerp(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double *ptrd = &_mp_arg(1) + 1; + const double + *ptrs1 = &_mp_arg(3) + 1, + *ptrs2 = &_mp_arg(4) + 1, + t = _mp_arg(5); + for (unsigned int k = 0; k::nan(); + } + + static double mp_vector_off(_cimg_math_parser& mp) { + const unsigned int + ptr = (unsigned int)mp.opcode[2] + 1, + siz = (unsigned int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type::nan(); + } + + static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(4); + l_opcode[2] = mp.opcode[4]; // Scalar argument1 + l_opcode.swap(mp.opcode); + ulongT &argument2 = mp.opcode[3]; + while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode.swap(mp.opcode); + ulongT &argument = mp.opcode[2]; + while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,5); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode[4] = mp.opcode[6]; // Scalar argument3 + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs1 = (unsigned int)mp.opcode[4] + 1, + ptrs2 = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; + while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_neq(_cimg_math_parser& mp) { + return !mp_vector_eq(mp); + } + + static double mp_vector_print(_cimg_math_parser& mp) { + const bool print_string = (bool)mp.opcode[4]; + cimg_pragma_openmp(critical(mp_vector_print)) + { + CImg _expr(mp.opcode[2] - 5); + const ulongT *ptrs = mp.opcode._data + 5; + cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(_expr); + unsigned int + ptr = (unsigned int)mp.opcode[1] + 1, + siz0 = (unsigned int)mp.opcode[3], + siz = siz0; + cimg::mutex(6); + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data); + unsigned int count = 0; + while (siz-->0) { + if (count>=64 && siz>=64) { + std::fprintf(cimg::output(),"...,"); + ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64; + siz = 64; + } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":""); + ++count; + } + if (print_string) { + CImg str(siz0 + 1); + ptr = (unsigned int)mp.opcode[1] + 1; + for (unsigned int k = 0; k::nan(); + } + + static double mp_vector_resize(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4]; + const int + interpolation = (int)_mp_arg(5), + boundary_conditions = (int)_mp_arg(6); + if (p2) { // Resize vector + const double *const ptrs = &_mp_arg(3) + 1; + CImg(ptrd,p1,1,1,1,true) = CImg(ptrs,p2,1,1,1,true). + get_resize(p1,1,1,1,interpolation,boundary_conditions); + } else { // Resize scalar + const double value = _mp_arg(3); + CImg(ptrd,p1,1,1,1,true) = CImg(1,1,1,1,value).resize(p1,1,1,1,interpolation, + boundary_conditions); + } + return cimg::type::nan(); + } + + static double mp_vector_reverse(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int p1 = (unsigned int)mp.opcode[3]; + CImg(ptrd,p1,1,1,1,true) = CImg(ptrs,p1,1,1,1,true).get_mirror('x'); + return cimg::type::nan(); + } + + static double mp_vector_set_off(_cimg_math_parser& mp) { + const unsigned int + ptr = (unsigned int)mp.opcode[2] + 1, + siz = (unsigned int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1); + return _mp_arg(1); + } + +#define _cimg_mp_vfunc(func) \ + const longT sizd = (longT)mp.opcode[2];\ + const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \ + double *const ptrd = &_mp_arg(1) + (sizd?1:0); \ + cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \ + { CImg vec(nbargs); double res; \ + cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \ + cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \ + func; ptrd[k] = res; \ + }} \ + return sizd?cimg::type::nan():*ptrd; + + static double _mp_vargkth(CImg& vec) { + const double val = (+vec).get_shared_points(1,vec.width() - 1). + kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)); + cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.; + return 1.; + } + + static double mp_vargkth(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = _mp_vargkth(vec)); + } + + static double mp_vargmax(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data())); + } + + static double mp_vargmaxabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data())); + } + + static double mp_vargmin(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data())); + } + + static double mp_vargminabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data())); + } + + static double mp_vavg(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.mean()); + } + + static double mp_vkth(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1). + kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2))); + } + + static double mp_vmax(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.max()); + } + + static double mp_vmaxabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.maxabs()); + } + + static double mp_vmedian(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.median()); + } + + static double mp_vmin(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.min()); + } + + static double mp_vminabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.minabs()); + } + + static double mp_vprod(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.product()); + } + + static double mp_vstd(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3])); + } + + static double mp_vsum(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.sum()); + } + + static double mp_vvar(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.get_stats()[3]); + } + + static double mp_vtos(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + sizd = (unsigned int)mp.opcode[2], + sizs = (unsigned int)mp.opcode[4]; + std::memset(ptrd,0,sizd*sizeof(double)); + const int nb_digits = (int)_mp_arg(5); + CImg format(8); + switch (nb_digits) { + case -1 : std::strcpy(format,"%g"); break; + case 0 : std::strcpy(format,"%.17g"); break; + default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits); + } + CImg str; + if (sizs) { // Vector expression + const double *ptrs = &_mp_arg(3) + 1; + CImg(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str); + } else { // Scalar expression + str.assign(sizd + 1); + cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); + } + const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1); + CImg(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); + return cimg::type::nan(); + } + + static double mp_while(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_cond = ++mp.p_code, + *const p_body = p_cond + mp.opcode[3], + *const p_end = p_body + mp.opcode[4]; + const unsigned int vsiz = (unsigned int)mp.opcode[5]; + bool is_cond = false; + if (mp.opcode[6]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[7]) mp.mem[mem_cond] = 0; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + do { + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond && !mp.break_type) // Evaluate body + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } while (is_cond); + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3), + vsiz = (unsigned int)mp.opcode[4]; + const CImg &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6), + vsiz = (unsigned int)mp.opcode[7]; + const CImg &img = mp.imgin; + const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3), + vsiz = (unsigned int)mp.opcode[4]; + const CImg &img = mp.imgin; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], + oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z]; + const longT + off = img.offset(ox,oy,oz) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6), + vsiz = (unsigned int)mp.opcode[7]; + const CImg &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + +#undef _mp_arg + + }; // struct _cimg_math_parser {} + +#define _cimg_create_pointwise_functions(name,func,min_size) \ + CImg& name() { \ + if (is_empty()) return *this; \ + cimg_openmp_for(*this,func((double)*ptr),min_size); \ + return *this; \ + } \ + CImg get_##name() const { \ + return CImg(*this,false).name(); \ + } + + //! Compute the square value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqr().normalize(0,255)).display(); + \endcode + \image html ref_sqr.jpg + **/ + _cimg_create_pointwise_functions(sqr,cimg::sqr,524288) + + //! Compute the square root of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqrt().normalize(0,255)).display(); + \endcode + \image html ref_sqrt.jpg + **/ + _cimg_create_pointwise_functions(sqrt,std::sqrt,8192) + + //! Compute the exponential of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(exp,std::exp,4096) + + //! Compute the logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm + \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log,std::log,262144) + + //! Compute the base-2 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm + \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log2,cimg::log2,4096) + + //! Compute the base-10 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm + \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log10,std::log10,4096) + + //! Compute the absolute value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(abs,cimg::abs,524288) + + //! Compute the sign of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign + \f$\mathrm{sign}(I_{(x,y,z,c)})\f$. + \note + - The sign is set to: + - \c 1 if pixel value is strictly positive. + - \c -1 if pixel value is strictly negative. + - \c 0 if pixel value is equal to \c 0. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sign,cimg::sign,32768) + + //! Compute the cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(cos,std::cos,8192) + + //! Compute the sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sin,std::sin,8192) + + //! Compute the sinc of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc + \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sinc,cimg::sinc,2048) + + //! Compute the tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(tan,std::tan,2048) + + //! Compute the hyperbolic cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine + \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(cosh,std::cosh,2048) + + //! Compute the hyperbolic sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine + \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sinh,std::sinh,2048) + + //! Compute the hyperbolic tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent + \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(tanh,std::tanh,2048) + + //! Compute the arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine + \f$\mathrm{acos}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(acos,std::acos,8192) + + //! Compute the arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine + \f$\mathrm{asin}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(asin,std::asin,8192) + + //! Compute the arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent + \f$\mathrm{atan}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(atan,std::atan,8192) + + //! Compute the arctangent2 of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 + \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$. + \param img Image whose pixel values specify the second argument of the \c atan2() function. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2' + img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2' + img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value + (img_x,img_y,img_atan2).display(); + \endcode + **/ + template + CImg& atan2(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return atan2(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_atan2(const CImg& img) const { + return CImg(*this,false).atan2(img); + } + + //! Compute the hyperbolic arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh + \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(acosh,cimg::acosh,8192) + + //! Compute the hyperbolic arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine + \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(asinh,cimg::asinh,8192) + + //! Compute the hyperbolic arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent + \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(atanh,cimg::atanh,8192) + + //! In-place pointwise multiplication. + /** + Compute the pointwise multiplication between the image instance and the specified input image \c img. + \param img Input image, as the second operand of the multiplication. + \note + - Similar to operator+=(const CImg&), except that it performs a pointwise multiplication + instead of an addition. + - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg&) instead. + \par Example + \code + CImg + img("reference.jpg"), + shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false); + shade.normalize(0,1); + (img,shade,img.get_mul(shade)).display(); + \endcode + **/ + template + CImg& mul(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return mul(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_mul(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).mul(img); + } + + //! In-place pointwise division. + /** + Similar to mul(const CImg&), except that it performs a pointwise division instead of a multiplication. + **/ + template + CImg& div(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return div(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_div(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).div(img); + } + + //! Raise each pixel value to a specified power. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$. + \param p Exponent value. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img0("reference.jpg"), // Load reference color image + img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8 + img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5 + (img0,img1,img2).display(); + \endcode + **/ + CImg& pow(const double p) { + if (is_empty()) return *this; + if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; } + if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; } + if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; } + if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; } + if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; } + if (p==0) return fill((T)1); + if (p==0.5) return sqrt(); + if (p==1) return *this; + if (p==2) return sqr(); + if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; } + if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; } + cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024); + return *this; + } + + //! Raise each pixel value to a specified power \newinstance. + CImg get_pow(const double p) const { + return CImg(*this,false).pow(p); + } + + //! Raise each pixel value to a power, specified from an expression. + /** + Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. + **/ + CImg& pow(const char *const expression) { + return pow((+*this)._fill(expression,true,1,0,0,"pow",this)); + } + + //! Raise each pixel value to a power, specified from an expression \newinstance. + CImg get_pow(const char *const expression) const { + return CImg(*this,false).pow(expression); + } + + //! Raise each pixel value to a power, pointwisely specified from another image. + /** + Similar to operator+=(const CImg& img), except that it performs an exponentiation instead of an addition. + **/ + template + CImg& pow(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return pow(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_pow(const CImg& img) const { + return CImg(*this,false).pow(img); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const unsigned int n=1) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::rol(*ptr,n),32768); + return *this; + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const unsigned int n=1) const { + return (+*this).rol(n); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const char *const expression) { + return rol((+*this)._fill(expression,true,1,0,0,"rol",this)); + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const char *const expression) const { + return (+*this).rol(expression); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const CImg&), except that it performs a left rotation instead of a left shift. + **/ + template + CImg& rol(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return rol(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_rol(const CImg& img) const { + return (+*this).rol(img); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const unsigned int n=1) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::ror(*ptr,n),32768); + return *this; + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const unsigned int n=1) const { + return (+*this).ror(n); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const char *const expression) { + return ror((+*this)._fill(expression,true,1,0,0,"ror",this)); + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const char *const expression) const { + return (+*this).ror(expression); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const CImg&), except that it performs a right rotation instead of a right shift. + **/ + template + CImg& ror(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return ror(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_ror(const CImg& img) const { + return (+*this).ror(img); + } + + //! Pointwise min operator between instance image and a value. + /** + \param val Value used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& min(const T& value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,std::min(*ptr,value),65536); + return *this; + } + + //! Pointwise min operator between instance image and a value \newinstance. + CImg get_min(const T& value) const { + return (+*this).min(value); + } + + //! Pointwise min operator between two images. + /** + \param img Image used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& min(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return min(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_min(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).min(img); + } + + //! Pointwise min operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& min(const char *const expression) { + return min((+*this)._fill(expression,true,1,0,0,"min",this)); + } + + //! Pointwise min operator between an image and an expression \newinstance. + CImg get_min(const char *const expression) const { + return CImg(*this,false).min(expression); + } + + //! Pointwise max operator between instance image and a value. + /** + \param val Value used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& max(const T& value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,std::max(*ptr,value),65536); + return *this; + } + + //! Pointwise max operator between instance image and a value \newinstance. + CImg get_max(const T& value) const { + return (+*this).max(value); + } + + //! Pointwise max operator between two images. + /** + \param img Image used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& max(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return max(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_max(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).max(img); + } + + //! Pointwise max operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& max(const char *const expression) { + return max((+*this)._fill(expression,true,1,0,0,"max",this)); + } + + //! Pointwise max operator between an image and an expression \newinstance. + CImg get_max(const char *const expression) const { + return CImg(*this,false).max(expression); + } + + //! Pointwise minabs operator between instance image and a value. + /** + \param val Value used as the reference argument of the minabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& minabs(const T& value) { + if (is_empty()) return *this; + const T absvalue = cimg::abs(value); + cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536); + return *this; + } + + //! Pointwise minabs operator between instance image and a value \newinstance. + CImg get_minabs(const T& value) const { + return (+*this).minabs(value); + } + + //! Pointwise minabs operator between two images. + /** + \param img Image used as the reference argument of the minabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& minabs(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return minabs(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_minabs(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).minabs(img); + } + + //! Pointwise minabs operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& minabs(const char *const expression) { + return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this)); + } + + //! Pointwise minabs operator between an image and an expression \newinstance. + CImg get_minabs(const char *const expression) const { + return CImg(*this,false).minabs(expression); + } + + //! Pointwise maxabs operator between instance image and a value. + /** + \param val Value used as the reference argument of the maxabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& maxabs(const T& value) { + if (is_empty()) return *this; + const T absvalue = cimg::abs(value); + cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536); + return *this; + } + + //! Pointwise maxabs operator between instance image and a value \newinstance. + CImg get_maxabs(const T& value) const { + return (+*this).maxabs(value); + } + + //! Pointwise maxabs operator between two images. + /** + \param img Image used as the reference argument of the maxabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& maxabs(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return maxabs(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_maxabs(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).maxabs(img); + } + + //! Pointwise maxabs operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& maxabs(const char *const expression) { + return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this)); + } + + //! Pointwise maxabs operator between an image and an expression \newinstance. + CImg get_maxabs(const char *const expression) const { + return CImg(*this,false).maxabs(expression); + } + + //! Return a reference to the minimum pixel value. + /** + **/ + T& min() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min; + cimg_for(*this,ptrs,T) if (*ptrsmax_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value \const. + const T& max() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max; + cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value in absolute value. + /** + **/ + T& maxabs() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "maxabs(): Empty instance.", + cimg_instance); + T *ptr_maxabs = _data; + T maxabs_value = *ptr_maxabs; + cimg_for(*this,ptrs,T) { + const T ma = cimg::abs(*ptrs); + if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; } + } + return *ptr_maxabs; + } + + //! Return a reference to the maximum pixel value in absolute value \const. + const T& maxabs() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "maxabs(): Empty instance.", + cimg_instance); + const T *ptr_maxabs = _data; + T maxabs_value = *ptr_maxabs; + cimg_for(*this,ptrs,T) { + const T ma = cimg::abs(*ptrs); + if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; } + } + return *ptr_maxabs; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value. + /** + \param[out] max_val Maximum pixel value. + **/ + template + T& min_max(t& max_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value \const. + template + const T& min_max(t& max_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + const T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the maximum pixel value as well as the minimum pixel value. + /** + \param[out] min_val Minimum pixel value. + **/ + template + T& max_min(t& min_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val=size()) return max(); + CImg arr(*this,false); + ulongT l = 0, ir = size() - 1; + for ( ; ; ) { + if (ir<=l + 1) { + if (ir==l + 1 && arr[ir]>1; + cimg::swap(arr[mid],arr[l + 1]); + if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); + if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]); + if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]); + ulongT i = l + 1, j = ir; + const T pivot = arr[l + 1]; + for ( ; ; ) { + do ++i; while (arr[i]pivot); + if (j=k) ir = j - 1; + if (j<=k) l = i; + } + } + } + + //! Return the median pixel value. + /** + **/ + T median() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "median(): Empty instance.", + cimg_instance); + const ulongT s = size(); + switch (s) { + case 1 : return _data[0]; + case 2 : return cimg::median(_data[0],_data[1]); + case 3 : return cimg::median(_data[0],_data[1],_data[2]); + case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]); + case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]); + case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]); + case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8], + _data[9],_data[10],_data[11],_data[12]); + } + const T res = kth_smallest(s>>1); + return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2); + } + + //! Return the product of all the pixel values. + /** + **/ + double product() const { + if (is_empty()) return 0; + double res = 1; + cimg_for(*this,ptrs,T) res*=(double)*ptrs; + return res; + } + + //! Return the sum of all the pixel values. + /** + **/ + double sum() const { + double res = 0; + cimg_for(*this,ptrs,T) res+=(double)*ptrs; + return res; + } + + //! Return the average pixel value. + /** + **/ + double mean() const { + double res = 0; + cimg_for(*this,ptrs,T) res+=(double)*ptrs; + return res/size(); + } + + //! Return the variance of the pixel values. + /** + \param variance_method Method used to estimate the variance. Can be: + - \c 0: Second moment, computed as + \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = + 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$ + with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$. + - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$. + - \c 2: Least median of squares. + - \c 3: Least trimmed of squares. + **/ + double variance(const unsigned int variance_method=1) const { + double foo; + return variance_mean(variance_method,foo); + } + + //! Return the variance as well as the average of the pixel values. + /** + \param variance_method Method used to estimate the variance (see variance(const unsigned int) const). + \param[out] mean Average pixel value. + **/ + template + double variance_mean(const unsigned int variance_method, t& mean) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_mean(): Empty instance.", + cimg_instance); + + double variance = 0, average = 0; + const ulongT siz = size(); + switch (variance_method) { + case 0 : { // Least mean square (standard definition) + double S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } + variance = (S2 - S*S/siz)/siz; + average = S; + } break; + case 1 : { // Least mean square (robust definition) + double S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } + variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + average = S; + } break; + case 2 : { // Least Median of Squares (MAD) + CImg buf(*this,false); + buf.sort(); + const ulongT siz2 = siz>>1; + const double med_i = (double)buf[siz2]; + cimg_for(buf,ptrs,Tfloat) { + const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; + } + buf.sort(); + const double sig = (double)(1.4828*buf[siz2]); + variance = sig*sig; + } break; + default : { // Least trimmed of Squares + CImg buf(*this,false); + const ulongT siz2 = siz>>1; + cimg_for(buf,ptrs,Tfloat) { + const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; + } + buf.sort(); + double a = 0; + const Tfloat *ptrs = buf._data; + for (ulongT j = 0; j0?variance:0; + } + + //! Return estimated variance of the noise. + /** + \param variance_method Method used to compute the variance (see variance(const unsigned int) const). + \note Because of structures such as edges in images it is + recommended to use a robust variance estimation. The variance of the + noise is estimated by computing the variance of the Laplacian \f$(\Delta + I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]= + \sigma^2\f$ where \f$\sigma\f$ is the noise variance. + **/ + double variance_noise(const unsigned int variance_method=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_noise(): Empty instance.", + cimg_instance); + + const ulongT siz = size(); + if (!siz || !_data) return 0; + if (variance_method>1) { // Compute a scaled version of the Laplacian + CImg tmp(*this,false); + if (_depth==1) { + const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3(I,T); + cimg_for3x3(*this,x,y,0,c,I,T) { + tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn + + (double)Icp - 4*(double)Icc); + } + } + } else { + const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,T); + cimg_for3x3x3(*this,x,y,z,c,I,T) { + tmp(x,y,z,c) = cste*( + (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); + } + } + } + return tmp.variance(variance_method); + } + + // Version that doesn't need intermediate images. + double variance = 0, S = 0, S2 = 0; + if (_depth==1) { + const double cste = 1./std::sqrt(20.); + CImg_3x3(I,T); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { + const double val = cste*((double)Inc + (double)Ipc + + (double)Icn + (double)Icp - 4*(double)Icc); + S+=val; S2+=val*val; + } + } else { + const double cste = 1./std::sqrt(42.); + CImg_3x3x3(I,T); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { + const double val = cste * + ((double)Incc + (double)Ipcc + (double)Icnc + + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); + S+=val; S2+=val*val; + } + } + if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + else variance = (S2 - S*S/siz)/siz; + return variance>0?variance:0; + } + + //! Compute the MSE (Mean-Squared Error) between two images. + /** + \param img Image used as the second argument of the MSE operator. + **/ + template + double MSE(const CImg& img) const { + if (img.size()!=size()) + throw CImgArgumentException(_cimg_instance + "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + double vMSE = 0; + const t* ptr2 = img._data; + cimg_for(*this,ptr1,T) { + const double diff = (double)*ptr1 - (double)*(ptr2++); + vMSE+=diff*diff; + } + const ulongT siz = img.size(); + if (siz) vMSE/=siz; + return vMSE; + } + + //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images. + /** + \param img Image used as the second argument of the PSNR operator. + \param max_value Maximum theoretical value of the signal. + **/ + template + double PSNR(const CImg& img, const double max_value=255) const { + const double vMSE = (double)std::sqrt(MSE(img)); + return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type::max()); + } + + //! Evaluate math formula. + /** + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); + } + + // Fast function to pre-evaluate common expressions. + // (return 'true' in case of success, and set value of 'res'). + template + bool __eval(const char *const expression, t &res) const { + if (!expression || !*expression) { res = (t)0; return true; } + const char c = *expression; + bool is_success = false; + char c1, end; + double val; + if (c>='0' && c<='9') { // Possible value + if (!expression[1]) { // Single digit + res = (t)(c - '0'); + is_success = true; + } else if (std::sscanf(expression,"%lf%c",&val,&end)==1) { // Single value + res = (t)val; + is_success = true; + } + } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value + (c1=expression[1])>='0' && c1<='0') { + if (!expression[2]) { // [+-!] + Single digit + const int ival = c1 - '0'; + res = (t)(c=='+'?ival:c=='-'?-ival:!ival); + is_success = true; + } else if (std::sscanf(expression + 1,"%lf%c",&val,&end)==1) { // [+-!] Single value + res = (t)(c=='+'?val:c=='-'?-val:(double)!val); + is_success = true; + } + } else if (!expression[1]) switch (*expression) { // Other common single-char expressions + case 'w' : res = (t)_width; is_success = true; break; + case 'h' : res = (t)_height; is_success = true; break; + case 'd' : res = (t)_depth; is_success = true; break; + case 's' : res = (t)_spectrum; is_success = true; break; + case 'r' : res = (t)_is_shared; is_success = true; break; + } + return is_success; + } + + double _eval(CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression || !*expression) return 0; + double _val = 0; + if (__eval(expression,_val)) return _val; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'),"eval", + *this,img_output,list_inputs,list_outputs,false); + mp.begin_t(); + const double val = mp(x,y,z,c); + mp.end_t(); + mp.end(); + return val; + } + + //! Evaluate math formula. + /** + \param[out] output Contains values of output vector returned by the evaluated expression + (or is empty if the returned type is scalar). + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + void eval(CImg &output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + template + void eval(CImg& output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); + } + + template + void _eval(CImg& output, CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression || !*expression) { output.assign(1); *output = 0; return; } + double _val = 0; + if (__eval(expression,_val)) { output.assign(1); *output = _val; return; } + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'),"eval", + *this,img_output,list_inputs,list_outputs,false); + output.assign(1,std::max(1U,mp.result_dim)); + mp.begin_t(); + mp(x,y,z,c,output._data); + mp.end_t(); + mp.end(); + } + + //! Evaluate math formula on a set of variables. + /** + \param expression Math formula, as a C-string. + \param xyzc Set of values (x,y,z,c) used for the evaluation. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,xyzc,list_inputs,list_outputs); + } + + //! Evaluate math formula on a set of variables \const. + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,xyzc,list_inputs,list_outputs); + } + + template + CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + CImg res(1,xyzc.size()/4); + if (!expression || !*expression) return res.fill(0); + _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false); + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel if (res._height>=512)) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_eval)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + cimg_pragma_openmp(for) + for (int i = 0; i[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]. + **/ + CImg get_stats(const unsigned int variance_method=1) const { + if (is_empty()) return CImg(); + const ulongT siz = size(); + const longT off_end = (longT)siz; + double S = 0, S2 = 0, P = 1; + longT offm = 0, offM = 0; + T m = *_data, M = m; + + cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) { + longT loffm = 0, loffM = 0; + T lm = *_data, lM = lm; + cimg_pragma_openmp(for) + for (longT off = 0; offlM) { lM = val; loffM = off; } + S+=_val; + S2+=_val*_val; + P*=_val; + } + cimg_pragma_openmp(critical(get_stats)) { + if (lmM || (lM==M && loffM1?(S2 - S*S/siz)/(siz - 1):0): + variance(variance_method)), + variance_value = _variance_value>0?_variance_value:0; + int + xm = 0, ym = 0, zm = 0, cm = 0, + xM = 0, yM = 0, zM = 0, cM = 0; + contains(_data[offm],xm,ym,zm,cm); + contains(_data[offM],xM,yM,zM,cM); + return CImg(1,14).fill((double)m,(double)M,mean_value,variance_value, + (double)xm,(double)ym,(double)zm,(double)cm, + (double)xM,(double)yM,(double)zM,(double)cM, + S,P); + } + + //! Compute statistics vector from the pixel values \inplace. + CImg& stats(const unsigned int variance_method=1) { + return get_stats(variance_method).move_to(*this); + } + + //@} + //------------------------------------- + // + //! \name Vector / Matrix Operations + //@{ + //------------------------------------- + + //! Compute norm of the image, viewed as a matrix. + /** + \param magnitude_type Norm type. Can be: + - \c -1: Linf-norm + - \c 0: L0-norm + - \c 1: L1-norm + - \c 2: L2-norm + **/ + double magnitude(const int magnitude_type=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "magnitude(): Empty instance.", + cimg_instance); + const ulongT siz = size(); + double res = 0; + switch (magnitude_type) { + case -1 : { + cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } + } break; + case 1 : { + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); + } break; + default : { + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]); + res = (double)std::sqrt(res); + } + } + return res; + } + + //! Compute the trace of the image, viewed as a matrix. + /** + **/ + double trace() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "trace(): Empty instance.", + cimg_instance); + double res = 0; + cimg_forX(*this,k) res+=(double)(*this)(k,k); + return res; + } + + //! Compute the determinant of the image, viewed as a matrix. + /** + **/ + double det() const { + if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "det(): Instance is not a square matrix.", + cimg_instance); + + switch (_width) { + case 1 : return (double)((*this)(0,0)); + case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0)); + case 3 : { + const double + a = (double)_data[0], d = (double)_data[1], g = (double)_data[2], + b = (double)_data[3], e = (double)_data[4], h = (double)_data[5], + c = (double)_data[6], f = (double)_data[7], i = (double)_data[8]; + return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; + } + default : { + CImg lu(*this,false); + CImg indx; + bool d; + lu._LU(indx,d); + double res = d?(double)1:(double)-1; + cimg_forX(lu,i) res*=lu(i,i); + return res; + } + } + } + + //! Compute the dot product between instance and argument, viewed as matrices. + /** + \param img Image used as a second argument of the dot product. + **/ + template + double dot(const CImg& img) const { + const ulongT nb = std::min(size(),img.size()); + double res = 0; + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192)) + for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off]; + return res; + } + + //! Get vector-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + CImg res; + if (res._height!=_spectrum) res.assign(1,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + const T *ptrs = data(x,y,z); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get (square) matrix-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \note - The spectrum() of the image must be a square. + **/ + CImg get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + const int n = (int)cimg::round(std::sqrt((double)_spectrum)); + const T *ptrs = data(x,y,z,0); + const ulongT whd = (ulongT)_width*_height*_depth; + CImg res(n,n); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get tensor-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + const T *ptrs = data(x,y,z,0); + const ulongT whd = (ulongT)_width*_height*_depth; + if (_spectrum==6) + return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd)); + if (_spectrum==3) + return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd)); + return tensor(*ptrs); + } + + //! Set vector-valued pixel at specified position. + /** + \param vec Vector to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_vector_at(const CImg& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { + if (x<_width && y<_height && z<_depth) { + const t *ptrs = vec._data; + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = data(x,y,z); + for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) { + *ptrd = (T)*(ptrs++); ptrd+=whd; + } + } + return *this; + } + + //! Set (square) matrix-valued pixel at specified position. + /** + \param mat Matrix to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_matrix_at(const CImg& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + return set_vector_at(mat,x,y,z); + } + + //! Set tensor-valued pixel at specified position. + /** + \param ten Tensor to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + T *ptrd = data(x,y,z,0); + const ulongT siz = (ulongT)_width*_height*_depth; + if (ten._height==2) { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[3]; + } + else { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[2]; ptrd+=siz; + *ptrd = (T)ten[4]; ptrd+=siz; + *ptrd = (T)ten[5]; ptrd+=siz; + *ptrd = (T)ten[8]; + } + return *this; + } + + //! Resize image to become a diagonal matrix. + /** + \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient. + **/ + CImg& diagonal() { + return get_diagonal().move_to(*this); + } + + //! Resize image to become a diagonal matrix \newinstance. + CImg get_diagonal() const { + if (is_empty()) return *this; + const unsigned int siz = (unsigned int)size(); + CImg res(siz,siz,1,1,0); + cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off]; + return res; + } + + //! Replace the image by an identity matrix. + /** + \note If the instance image is not square, it is resized to a square matrix using its maximum + dimension as a reference. + **/ + CImg& identity_matrix() { + return identity_matrix(std::max(_width,_height)).move_to(*this); + } + + //! Replace the image by an identity matrix \newinstance. + CImg get_identity_matrix() const { + return identity_matrix(std::max(_width,_height)); + } + + //! Fill image with a linear sequence of values. + /** + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + CImg& sequence(const T& a0, const T& a1) { + if (is_empty()) return *this; + const ulongT siz = size() - 1; + T* ptr = _data; + if (siz) { + const double delta = (double)a1 - (double)a0; + cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); + } else *ptr = a0; + return *this; + } + + //! Fill image with a linear sequence of values \newinstance. + CImg get_sequence(const T& a0, const T& a1) const { + return (+*this).sequence(a0,a1); + } + + //! Transpose the image, viewed as a matrix. + /** + \note Equivalent to \code permute_axes("yxzc"); \endcode. + **/ + CImg& transpose() { + if (_width==1) { _width = _height; _height = 1; return *this; } + if (_height==1) { _height = _width; _width = 1; return *this; } + if (_width==_height) { + cimg_forYZC(*this,y,z,c) for (int x = y; x get_transpose() const { + return get_permute_axes("yxzc"); + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors. + /** + \param img Image used as the second argument of the cross product. + \note The first argument of the cross product is \c *this. + **/ + template + CImg& cross(const CImg& img) { + if (_width!=1 || _height<3 || img._width!=1 || img._height<3) + throw CImgInstanceException(_cimg_instance + "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + + const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; + (*this)[0] = (T)(y*img[2] - z*img[1]); + (*this)[1] = (T)(z*img[0] - x*img[2]); + (*this)[2] = (T)(x*img[1] - y*img[0]); + return *this; + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance. + template + CImg<_cimg_Tt> get_cross(const CImg& img) const { + return CImg<_cimg_Tt>(*this).cross(img); + } + + //! Invert the instance image, viewed as a matrix. + /** + \param use_LU Choose the inverting algorithm. Can be: + - \c true: LU-based matrix inversion. + - \c false: SVD-based matrix inversion. + **/ + CImg& invert(const bool use_LU=true) { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "invert(): Instance is not a square matrix.", + cimg_instance); + const double dete = _width>3?-1.:det(); + if (dete!=0. && _width==2) { + const double + a = _data[0], c = _data[1], + b = _data[2], d = _data[3]; + _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete); + _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete); + } else if (dete!=0. && _width==3) { + const double + a = _data[0], d = _data[1], g = _data[2], + b = _data[3], e = _data[4], h = _data[5], + c = _data[6], f = _data[7], i = _data[8]; + _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete); + _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete); + _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete); + } else { + +#ifdef cimg_use_lapack + int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N]; + Tfloat + *const lapA = new Tfloat[N*N], + *const WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetri_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] WORK; +#else + if (use_LU) { // LU-based + CImg A(*this,false), indx; + bool d; + A._LU(indx,d); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16)) + cimg_forX(*this,j) { + CImg col(1,_width,1,1,0); + col(j) = 1; + col._solve(A,indx); + cimg_forX(*this,i) (*this)(j,i) = (T)col(i); + } + } else pseudoinvert(false); // SVD-based +#endif + } + return *this; + } + + //! Invert the instance image, viewed as a matrix \newinstance. + CImg get_invert(const bool use_LU=true) const { + return CImg(*this,false).invert(use_LU); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. + /** + **/ + CImg& pseudoinvert(const bool use_LU=false) { + return get_pseudoinvert(use_LU).move_to(*this); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. + CImg get_pseudoinvert(const bool use_LU=false) const { + + // LU-based method. + if (use_LU) { + CImg AtA(width(),width()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128)) + cimg_forY(AtA,i) + for (int j = 0; j<=i; ++j) { + double res = 0; + cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k); + AtA(j,i) = AtA(i,j) = (Tfloat)res; + } + AtA.invert(true); + return AtA*get_transpose(); + } + + // SVD-based method. + CImg U, S, V; + SVD(U,S,V,false); + const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max(); + cimg_forX(V,x) { + const Tfloat s = S(x), invs = s>epsilon?1/s:0; + cimg_forY(V,y) V(x,y)*=invs; + } + return V*U.transpose(); + } + + //! Solve a system of linear equations. + /** + \param A Matrix of the linear system. + \param use_LU In case of non square system (least-square solution), + choose between SVD-based (\c false) or LU-based (\c true) method. + LU method is faster for large matrices, but numerically less stable. + \note Solve \c AX = B where \c B=*this. + **/ + template + CImg& solve(const CImg& A, const bool use_LU=false) { + if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + + if (A.size()==1) return (*this)/=A[0]; + if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system + const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3], + fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d), + det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd); + if (fM==fa) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det; + (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y; + } else if (fM==fc) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det; + (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y; + } else if (fM==fb) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det; + (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det; + (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d); + } + return *this; + } + + if (A._width==A._height) { // Square linear system +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N]; + Ttfloat + *const lapA = new Ttfloat[N*N], + *const lapB = new Ttfloat[N], + *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l)); + cimg_forX(*this,i) { + cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrs_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0; + } + delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; +#else + CImg lu(A,false); + CImg indx; + bool d; + lu._LU(indx,d); + CImg res(_width,A._width); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16)) + cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx)); + res.move_to(*this); +#endif + } else { // Least-square solution for non-square systems + +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width; + Ttfloat WORK_QUERY; + Ttfloat + * const lapA = new Ttfloat[M*N], + * const lapB = new Ttfloat[M*NRHS]; + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO); + LWORK = (int) WORK_QUERY; + Ttfloat *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l)); + cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l)); + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO); + if (INFO != 0) + cimg::warn(_cimg_instance + "solve(): LAPACK library function sgels() returned error code %d.", + cimg_instance, + INFO); + assign(NRHS, N); + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l]; + else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); + delete[] lapA; delete[] lapB; delete[] WORK; +#else + (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); +#endif + } + return *this; + } + + //! Solve a system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve(const CImg& A, const bool use_LU=false) const { + typedef _cimg_Ttfloat Ttfloat; + return CImg(*this,false).solve(A,use_LU); + } + + template + CImg& _solve(const CImg& A, const CImg& indx) { + typedef _cimg_Ttfloat Ttfloat; + const int N = height(); + int ii = -1; + Ttfloat sum; + for (int i = 0; i=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j); + else if (sum!=0) ii = i; + (*this)(i) = (T)sum; + } + for (int i = N - 1; i>=0; --i) { + sum = (*this)(i); + for (int j = i + 1; j + CImg& solve_tridiagonal(const CImg& A) { + const unsigned int siz = (unsigned int)size(); + if (A._width!=3 || A._height!=siz) + throw CImgArgumentException(_cimg_instance + "solve_tridiagonal(): Instance and tridiagonal matrix " + "(%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = 1e-4f; + CImg B = A.get_column(1), V(*this,false); + for (int i = 1; i<(int)siz; ++i) { + const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon); + B[i] -= m*A(2,i - 1); + V[i] -= m*V[i - 1]; + } + (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon)); + for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i + 1])/(B[i]?B[i]:epsilon)); + return *this; + } + + //! Solve a tridiagonal system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg& A) const { + return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A); + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. + **/ + template + const CImg& eigen(CImg& val, CImg &vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + + if (val.size()<(ulongT)_width) val.assign(1,_width); + if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width); + switch (_width) { + case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break; + case 2 : { + const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d; + double f = e*e - 4*(a*d - b*c); + if (f<0) cimg::warn(_cimg_instance + "eigen(): Complex eigenvalues found.", + cimg_instance); + f = std::sqrt(f); + const double + l1 = 0.5*(e - f), + l2 = 0.5*(e + f), + b2 = b*b, + norm1 = std::sqrt(cimg::sqr(l2 - a) + b2), + norm2 = std::sqrt(cimg::sqr(l1 - a) + b2); + val[0] = (t)l2; + val[1] = (t)l1; + if (norm1>0) { vec(0,0) = (t)(b/norm1); vec(0,1) = (t)((l2 - a)/norm1); } else { vec(0,0) = 1; vec(0,1) = 0; } + if (norm2>0) { vec(1,0) = (t)(b/norm2); vec(1,1) = (t)((l1 - a)/norm2); } else { vec(1,0) = 1; vec(1,1) = 0; } + } break; + default : + throw CImgInstanceException(_cimg_instance + "eigen(): Eigenvalues computation of general matrices is limited " + "to 2x2 matrices.", + cimg_instance); + } + } + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \return A list of two images [val; vec], whose meaning is similar as in eigen(CImg&,CImg&) const. + **/ + CImgList get_eigen() const { + CImgList res(2); + eigen(res[0],res[1]); + return res; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. + **/ + template + const CImg& symmetric_eigen(CImg& val, CImg& vec) const { + if (is_empty()) { val.assign(); vec.assign(); return *this; } + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + val.assign(1,_width); + vec.assign(_width,_width); + + if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; } + if (_width==2) { + const double + a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], + e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)), + l1 = 0.5*(e - f), l2 = 0.5*(e + f), + n = std::sqrt(cimg::sqr(l2 - a) + b*b); + val[0] = (t)l2; + val[1] = (t)l1; + if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; } + vec[1] = -vec[2]; + vec[3] = vec[0]; + return *this; + } + +#ifdef cimg_use_lapack + char JOB = 'V', UPLO = 'U'; + int N = _width, LWORK = 4*N, INFO; + Tfloat + *const lapA = new Tfloat[N*N], + *const lapW = new Tfloat[N], + *const WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); + cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.", + cimg_instance, + INFO); + if (!INFO) { + cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i]; + cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]); + } else { val.fill(0); vec.fill(0); } + delete[] lapA; delete[] lapW; delete[] WORK; + +#else + CImg V(_width,_width); + Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M)); + (CImg(*this,false)/=maxabs).SVD(vec,val,V,false); + if (maxabs!=1) val*=maxabs; + + bool is_ambiguous = false; + float eig = 0; + cimg_forY(val,p) { // Check for ambiguous cases + if (val[p]>eig) eig = (float)val[p]; + t scal = 0; + cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); + if (cimg::abs(scal)<0.9f) is_ambiguous = true; + if (scal<0) val[p] = -val[p]; + } + if (is_ambiguous) { + ++(eig*=2); + SVD(vec,val,V,false,40,eig); + val-=eig; + } + + CImg permutations; // Sort eigenvalues in decreasing order + CImg tmp(_width); + val.sort(permutations,false); + cimg_forY(vec,k) { + cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k); + std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width); + } +#endif + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \return A list of two images [val; vec], whose meaning are similar as in + symmetric_eigen(CImg&,CImg&) const. + **/ + CImgList get_symmetric_eigen() const { + CImgList res(2); + symmetric_eigen(res[0],res[1]); + return res; + } + + //! Sort pixel values and get sorting permutations. + /** + \param[out] permutations Permutation map used for the sorting. + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + **/ + template + CImg& sort(CImg& permutations, const bool is_increasing=true) { + permutations.assign(_width,_height,_depth,_spectrum); + if (is_empty()) return *this; + cimg_foroff(permutations,off) permutations[off] = (t)off; + return _quicksort(0,size() - 1,permutations,is_increasing,true); + } + + //! Sort pixel values and get sorting permutations \newinstance. + template + CImg get_sort(CImg& permutations, const bool is_increasing=true) const { + return (+*this).sort(permutations,is_increasing); + } + + //! Sort pixel values. + /** + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + \param axis Tells if the value sorting must be done along a specific axis. Can be: + - \c 0: All pixel values are sorted, independently on their initial position. + - \c 'x': Image columns are sorted, according to the first value in each column. + - \c 'y': Image rows are sorted, according to the first value in each row. + - \c 'z': Image slices are sorted, according to the first value in each slice. + - \c 'c': Image channels are sorted, according to the first value in each channel. + **/ + CImg& sort(const bool is_increasing=true, const char axis=0) { + if (is_empty()) return *this; + CImg perm; + switch (cimg::lowercase(axis)) { + case 0 : + _quicksort(0,size() - 1,perm,is_increasing,false); + break; + case 'x' : { + perm.assign(_width); + get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c); + } break; + case 'y' : { + perm.assign(_height); + get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c); + } break; + case 'z' : { + perm.assign(_depth); + get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c); + } break; + case 'c' : { + perm.assign(_spectrum); + get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]); + } break; + default : + throw CImgArgumentException(_cimg_instance + "sort(): Invalid specified axis '%c' " + "(should be { x | y | z | c }).", + cimg_instance,axis); + } + return *this; + } + + //! Sort pixel values \newinstance. + CImg get_sort(const bool is_increasing=true, const char axis=0) const { + return (+*this).sort(is_increasing,axis); + } + + template + CImg& _quicksort(const long indm, const long indM, CImg& permutations, + const bool is_increasing, const bool is_permutations) { + if (indm(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]>(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]>(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } else { + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]<(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } + if (indM - indm>=3) { + const T pivot = (*this)[mid]; + long i = indm, j = indM; + if (is_increasing) { + do { + while ((*this)[i]pivot) --j; + if (i<=j) { + if (is_permutations) cimg::swap(permutations[i],permutations[j]); + cimg::swap((*this)[i++],(*this)[j--]); + } + } while (i<=j); + } else { + do { + while ((*this)[i]>pivot) ++i; + while ((*this)[j] A; // Input matrix (assumed to contain some values) + CImg<> U,S,V; + A.SVD(U,S,V) + \endcode + **/ + template + const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = (Ttfloat)1e-25; + + if (is_empty()) { U.assign(); S.assign(); V.assign(); } + else if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "SVD(): Instance has invalid dimensions (depth or channels different from 1).", + cimg_instance); + else { + U = *this; + if (lambda!=0) { + const unsigned int delta = std::min(U._width,U._height); + for (unsigned int i = 0; i rv1(_width); + Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0; + int l = 0; + + cimg_forX(U,i) { + l = i + 1; + rv1[i] = scale*g; + g = s = scale = 0; + if (i=0?-1:1)*std::sqrt(s)); + h = f*g - s; + U(i,i) = f - g; + for (int j = l; j=0?-1:1)*std::sqrt(s)); + h = f*g - s; + U(l,i) = f - g; + for (int k = l; k=0; --i) { + if (i=0; --i) { + l = i + 1; + g = S[i]; + for (int j = l; j=0; --k) { + int nm = 0; + for (unsigned int its = 0; its=1; --l) { + nm = l - 1; + if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; } + if ((cimg::abs(S[nm]) + anorm)==anorm) break; + } + if (flag) { + c = 0; + s = 1; + for (int i = l; i<=k; ++i) { + f = s*rv1[i]; + rv1[i] = c*rv1[i]; + if ((cimg::abs(f) + anorm)==anorm) break; + g = S[i]; + h = cimg::_hypot(f,g); + S[i] = h; + h = 1/h; + c = g*h; + s = -f*h; + cimg_forY(U,j) { + const t y = U(nm,j), z = U(i,j); + U(nm,j) = y*c + z*s; + U(i,j) = z*c - y*s; + } + } + } + + const t z = S[k]; + if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } + nm = k - 1; + t x = S[l], y = S[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y); + g = cimg::_hypot(f,(Ttfloat)1); + f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x); + c = s = 1; + for (int j = l; j<=nm; ++j) { + const int i = j + 1; + g = rv1[i]; + h = s*g; + g = c*g; + t y1 = S[i], z1 = cimg::_hypot(f,h); + rv1[j] = z1; + c = f/std::max(epsilon,(Ttfloat)z1); + s = h/std::max(epsilon,(Ttfloat)z1); + f = x*c + g*s; + g = g*c - x*s; + h = y1*s; + y1*=c; + cimg_forX(U,jj) { + const t x2 = V(j,jj), z2 = V(i,jj); + V(j,jj) = x2*c + z2*s; + V(i,jj) = z2*c - x2*s; + } + z1 = cimg::_hypot(f,h); + S[j] = z1; + if (z1) { + z1 = 1/std::max(epsilon,(Ttfloat)z1); + c = f*z1; + s = h*z1; + } + f = c*g + s*y1; + x = c*y1 - s*g; + cimg_forY(U,jj) { + const t y2 = U(j,jj), z2 = U(i,jj); + U(j,jj) = y2*c + z2*s; + U(i,jj) = z2*c - y2*s; + } + } + rv1[l] = 0; + rv1[k] = f; + S[k] = x; + } + } + + if (sorting) { + CImg permutations; + CImg tmp(_width); + S.sort(permutations,false); + cimg_forY(U,k) { + cimg_forY(permutations,y) tmp(y) = U(permutations(y),k); + std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width); + } + cimg_forY(V,k) { + cimg_forY(permutations,y) tmp(y) = V(permutations(y),k); + std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width); + } + } + } + return *this; + } + + //! Compute the SVD of the instance image, viewed as a general matrix. + /** + \return A list of three images [U; S; V], whose meaning is similar as in + SVD(CImg&,CImg&,CImg&,bool,unsigned int,float) const. + **/ + CImgList get_SVD(const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + CImgList res(3); + SVD(res[0],res[1],res[2],sorting,max_iteration,lambda); + return res; + } + + // [internal] Compute the LU decomposition of a permuted matrix. + template + CImg& _LU(CImg& indx, bool& d) { + const int N = width(); + int imax = 0; + CImg vv(N); + indx.assign(N); + d = true; + + bool return0 = false; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512)) + cimg_forX(*this,i) { + Tfloat vmax = 0; + cimg_forX(*this,j) { + const Tfloat tmp = cimg::abs((*this)(j,i)); + if (tmp>vmax) vmax = tmp; + } + if (vmax==0) return0 = true; else vv[i] = 1/vmax; + } + if (return0) { indx.fill(0); return fill(0); } + + cimg_forX(*this,j) { + for (int i = 0; i=vmax) { vmax = tmp; imax = i; } + } + if (j!=imax) { + cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); + d = !d; + vv[imax] = vv[j]; + } + indx[j] = (t)imax; + if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; + if (j=3 = orthogonal matching pursuit where an orthogonal projection step is performed + every 'method-2' iterations. + \param max_iter Sets the max number of iterations processed for each signal. + If set to '0' (default), 'max_iter' is set to the number of dictionary columns. + (only meaningful for matching pursuit and its variants). + \param max_residual Gives a stopping criterion on signal reconstruction accuracy. + (only meaningful for matching pursuit and its variants). + \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column. + Thus, the matrix product D*W is an approximation of the input matrix. + **/ + template + CImg& project_matrix(const CImg& dictionary, const unsigned int method=0, + const unsigned int max_iter=0, const double max_residual=1e-6) { + return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this); + } + + template + CImg get_project_matrix(const CImg& dictionary, const unsigned int method=0, + const unsigned int max_iter=0, const double max_residual=1e-6) const { + if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "project_matrix(): Instance image is not a matrix.", + cimg_instance); + if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.", + cimg_instance, + dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum); + + if (!method) return get_solve(dictionary,true); + CImg W(_width,dictionary._width,1,1,0); + + // Compute dictionary norm and normalize it. + CImg D(dictionary,false), Dnorm(D._width); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32)) + cimg_forX(Dnorm,d) { + Tfloat norm = 0; + cimg_forY(D,y) norm+=cimg::sqr(D(d,y)); + Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm)); + } + cimg_forXY(D,d,y) D(d,y)/=Dnorm[d]; + + // Matching pursuit. + const unsigned int proj_step = method<3?1:method - 2; + bool is_orthoproj = false; + + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32)) + cimg_forX(*this,x) { + CImg S = get_column(x); + const CImg S0 = method<2?CImg():S; + Tfloat residual = S.magnitude()/S._height; + const unsigned int nmax = max_iter?max_iter:D._width; + + for (unsigned int n = 0; nmax_residual; ++n) { + + // Find best matching column in D. + int dmax = 0; + Tfloat absdotmax = 0, dotmax = 0; + cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32)) + cimg_forX(D,d) { + Tfloat _dot = 0; + cimg_forY(D,y) _dot+=S[y]*D(d,y); + Tfloat absdot = cimg::abs(_dot); + cimg_pragma_openmp(critical(get_project_matrix)) { + if (absdot>absdotmax) { + absdotmax = absdot; + dotmax = _dot; + dmax = d; + } + } + } + + if (!n || method<3 || n%proj_step) { + // Matching Pursuit: Subtract component to signal. + W(x,dmax)+=dotmax; + residual = 0; + cimg_forY(S,y) { + S[y]-=dotmax*D(dmax,y); + residual+=cimg::sqr(S[y]); + } + residual = std::sqrt(residual)/S._height; + is_orthoproj = false; + + } else { + // Orthogonal Matching Pursuit: Orthogonal projection step. + W(x,dmax) = 1; // Used as a marker only. + unsigned int nbW = 0; + cimg_forY(W,d) if (W(x,d)) ++nbW; + CImg sD(nbW,D._height); + CImg inds(nbW); + int sd = 0; + cimg_forY(W,d) if (W(x,d)) { + cimg_forY(sD,y) sD(sd,y) = D(d,y); + inds[sd++] = d; + } + S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights + + // Recompute residual signal. + S = S0; + cimg_forY(sD,k) { + const Tfloat weight = sD[k]; + const unsigned int ind = inds[k]; + W(x,ind) = weight; + cimg_forY(S,y) S[y]-=weight*D(ind,y); + } + residual = S.magnitude()/S._height; + is_orthoproj = true; + } + } + + // Perform last orthoprojection step if needed. + if (method>=2 && !is_orthoproj) { + unsigned int nbW = 0; + cimg_forY(W,d) if (W(x,d)) ++nbW; + if (nbW) { // Avoid degenerated case where 0 coefs are used + CImg sD(nbW,D._height); + CImg inds(nbW); + int sd = 0; + cimg_forY(W,d) if (W(x,d)) { + cimg_forY(sD,y) sD(sd,y) = D(d,y); + inds[sd++] = d; + } + S0.get_solve(sD,true).move_to(sD); + cimg_forY(sD,k) W(x,inds[k]) = sD[k]; + } + } + } + + // Normalize resulting coefficients according to initial (non-normalized) dictionary. + cimg_forXY(W,x,y) W(x,y)/=Dnorm[y]; + return W; + } + + //! Compute minimal path in a graph, using the Dijkstra algorithm. + /** + \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance + between two nodes (i,j). + \param nb_nodes Number of graph nodes. + \param starting_node Index of the starting node. + \param ending_node Index of the ending node (set to ~0U to ignore ending node). + \param previous_node Array that gives the previous node index in the path to the starting node + (optional parameter). + \return Array of distances of each node to the starting node. + **/ + template + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + if (starting_node>=nb_nodes) + throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher " + "than number of nodes %u.", + pixel_type(),starting_node,nb_nodes); + CImg dist(1,nb_nodes,1,1,cimg::type::max()); + dist(starting_node) = 0; + previous_node.assign(1,nb_nodes,1,1,(t)-1); + previous_node(starting_node) = (t)starting_node; + CImg Q(nb_nodes); + cimg_forX(Q,u) Q(u) = (unsigned int)u; + cimg::swap(Q(starting_node),Q(0)); + unsigned int sizeQ = nb_nodes; + while (sizeQ) { + // Update neighbors from minimal vertex + const unsigned int umin = Q(0); + if (umin==ending_node) sizeQ = 0; + else { + const T dmin = dist(umin); + const T infty = cimg::type::max(); + for (unsigned int q = 1; qdist(Q(left))) || + (rightdist(Q(right)));) { + if (right + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node=~0U) { + CImg foo; + return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + /** + \param starting_node Index of the starting node. + \param ending_node Index of the ending node. + \param previous_node Array that gives the previous node index in the path to the starting node + (optional parameter). + \return Array of distances of each node to the starting node. + \note image instance corresponds to the adjacency matrix of the graph. + **/ + template + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + template + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) const { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "dijkstra(): Instance is not a graph adjacency matrix.", + cimg_instance); + + return dijkstra(*this,_width,starting_node,ending_node,previous_node); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { + return get_dijkstra(starting_node,ending_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { + CImg foo; + return get_dijkstra(starting_node,ending_node,foo); + } + + //! Return an image containing the character codes of specified string. + /** + \param str input C-string to encode as an image. + \param is_last_zero Tells if the ending \c '0' character appear in the resulting image. + \param is_shared Return result that shares its buffer with \p str. + **/ + static CImg string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) { + if (!str) return CImg(); + return CImg(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared); + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg row_vector(const T& a0) { + return vector(a0); + } + + //! Return a \c 2x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg row_vector(const T& a0, const T& a1) { + CImg r(2,1); + r[0] = a0; r[1] = a1; + return r; + } + + //! Return a \c 3x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg row_vector(const T& a0, const T& a1, const T& a2) { + CImg r(3,1); + r[0] = a0; r[1] = a1; r[2] = a2; + return r; + } + + //! Return a \c 4x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3) { + CImg r(4,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; + return r; + } + + //! Return a \c 5x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + CImg r(5,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; + return r; + } + + //! Return a \c 6x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + CImg r(6,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; + return r; + } + + //! Return a \c 7x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + CImg r(7,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; + return r; + } + + //! Return a \c 8x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + CImg r(8,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; + return r; + } + + //! Return a \c 9x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + CImg r(9,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; + return r; + } + + //! Return a \c 10x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + CImg r(10,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + return r; + } + + //! Return a \c 11x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + CImg r(11,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; + return r; + } + + //! Return a \c 12x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + CImg r(12,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; + return r; + } + + //! Return a \c 13x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + CImg r(13,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; + return r; + } + + //! Return a \c 14x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + CImg r(14,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; + return r; + } + + //! Return a \c 15x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + CImg r(15,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; + return r; + } + + //! Return a \c 16x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(16,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; + return r; + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg vector(const T& a0) { + CImg r(1,1); + r[0] = a0; + return r; + } + + //! Return a \c 1x2 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg vector(const T& a0, const T& a1) { + CImg r(1,2); + r[0] = a0; r[1] = a1; + return r; + } + + //! Return a \c 1x3 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2) { + CImg r(1,3); + r[0] = a0; r[1] = a1; r[2] = a2; + return r; + } + + //! Return a \c 1x4 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { + CImg r(1,4); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; + return r; + } + + //! Return a \c 1x5 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + CImg r(1,5); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; + return r; + } + + //! Return a \c 1x6 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + CImg r(1,6); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; + return r; + } + + //! Return a \c 1x7 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + CImg r(1,7); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; + return r; + } + + //! Return a \c 1x8 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + CImg r(1,8); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; + return r; + } + + //! Return a \c 1x9 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + CImg r(1,9); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; + return r; + } + + //! Return a \c 1x10 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + CImg r(1,10); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + return r; + } + + //! Return a \c 1x11 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + CImg r(1,11); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; + return r; + } + + //! Return a \c 1x12 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + CImg r(1,12); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; + return r; + } + + //! Return a \c 1x13 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + CImg r(1,13); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; + return r; + } + + //! Return a \c 1x14 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + CImg r(1,14); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; + return r; + } + + //! Return a \c 1x15 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + CImg r(1,15); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; + return r; + } + + //! Return a \c 1x16 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(1,16); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; + return r; + } + + //! Return a 1x1 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg matrix(const T& a0) { + return vector(a0); + } + + //! Return a 2x2 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, + const T& a2, const T& a3) { + CImg r(2,2); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; + *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a 3x3 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + \param a4 Fifth matrix value. + \param a5 Sixth matrix value. + \param a6 Seventh matrix value. + \param a7 Eighth matrix value. + \param a8 Ninth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, const T& a2, + const T& a3, const T& a4, const T& a5, + const T& a6, const T& a7, const T& a8) { + CImg r(3,3); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; + return r; + } + + //! Return a 4x4 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(4,4); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 5x5 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, + const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, + const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, + const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, + const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { + CImg r(5,5); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; + *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; + *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; + return r; + } + + //! Return a 1x1 symmetric matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg tensor(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 symmetric matrix tensor containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2) { + return matrix(a0,a1,a1,a2); + } + + //! Return a 3x3 symmetric matrix containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5); + } + + //! Return a 1x1 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1) { + return matrix(a0,0,0,a1); + } + + //! Return a 3x3 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2) { + return matrix(a0,0,0,0,a1,0,0,0,a2); + } + + //! Return a 4x4 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { + return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); + } + + //! Return a 5x5 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); + } + + //! Return a NxN identity matrix. + /** + \param N Dimension of the matrix. + **/ + static CImg identity_matrix(const unsigned int N) { + CImg res(N,N,1,1,0); + cimg_forX(res,x) res(x,x) = 1; + return res; + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1. + /** + \param N Size of the resulting vector. + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + static CImg sequence(const unsigned int N, const T& a0, const T& a1) { + if (N) return CImg(1,N).sequence(a0,a1); + return CImg(); + } + + //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or third quaternion coordinate. + \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). + **/ + static CImg rotation_matrix(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) { + double X, Y, Z, W, N; + if (is_quaternion) { + N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w); + if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; } + else { X = Y = Z = 0; W = 1; } + return CImg::matrix((T)(X*X + Y*Y - Z*Z - W*W),(T)(2*Y*Z - 2*X*W),(T)(2*X*Z + 2*Y*W), + (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y), + (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W)); + } + N = cimg::hypot((double)x,(double)y,(double)z); + if (N>0) { X = x/N; Y = y/N; Z = z/N; } + else { X = Y = 0; Z = 1; } + const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang); + return CImg::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s), + (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s), + (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c)); + } + + //@} + //----------------------------------- + // + //! \name Value Manipulation + //@{ + //----------------------------------- + + //! Fill all pixel values with specified value. + /** + \param val Fill value. + **/ + CImg& fill(const T& val) { + if (is_empty()) return *this; + if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val; + else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*) + return *this; + } + + //! Fill all pixel values with specified value \newinstance. + CImg get_fill(const T& val) const { + return CImg(_width,_height,_depth,_spectrum).fill(val); + } + + //! Fill sequentially all pixel values with specified values. + /** + \param val0 First fill value. + \param val1 Second fill value. + **/ + CImg& fill(const T& val0, const T& val1) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 1; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 2; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 3; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 4; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 5; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 6; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 7; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 8; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 9; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 10; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 11; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 12; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 13; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 14; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14, const T& val15) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 15; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14, const T& val15) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14,val15); + } + + //! Fill sequentially pixel values according to a given expression. + /** + \param expression C-string describing a math formula, or a sequence of values. + \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling. + \param allow_formula Tells that mathematical formulas are authorized for the filling. + \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. + \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression. + **/ + CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0); + } + + // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula | + // 2 = allow formula and do not fill image values }. + CImg& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode, + const CImgList *const list_inputs, CImgList *const list_outputs, + const char *const calling_function, const CImg *provides_copy) { + if (is_empty() || !expression || !*expression) return *this; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + CImg is_error; + bool is_value_sequence = false; + cimg_abort_init; + + if (formula_mode) { + + // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser. + double value; + char sep; + const int err = cimg_sscanf(expression,"%lf %c",&value,&sep); + if (err==1 || (err==2 && sep==',')) { + if (err==1) { if (formula_mode==2) return *this; return fill((T)value); } + else is_value_sequence = true; + } + + // Try to fill values according to a formula. + _cimg_abort_init_openmp; + if (!is_value_sequence) try { + CImg base = provides_copy?provides_copy->get_shared():get_shared(); + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'), + calling_function,base,this,list_inputs,list_outputs,true); + if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && + mp.need_input_copy) + base.assign().assign(*this,false); // Needs input copy + + // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation). + unsigned int M; + if (mp.result_dim) { + M = cimg::max(_width,_height,_depth); + M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height); + } else { + M = cimg::max(_width,_height,_depth,_spectrum); + M = M==_width?cimg::max(_height,_depth,_spectrum): + M==_height?cimg::max(_width,_depth,_spectrum): + M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth); + } + + bool do_in_parallel = false; +#if cimg_use_openmp!=0 + cimg_openmp_if(*expression=='*' || *expression==':' || + (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2)) + do_in_parallel = true; +#endif + if (mp.result_dim) { // Vector-valued expression + const unsigned int N = std::min(mp.result_dim,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; + if (*expression=='<') { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_rofYZ(*this,y,z) { + cimg_abort_test; + if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0); + else cimg_rofX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); + + } else if (*expression=='>' || !do_in_parallel) { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_forYZ(*this,y,z) { + cimg_abort_test; + if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0); + else cimg_forX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); + + } else { + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + lmp.is_fill = true; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + +#define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \ + cimg_pragma_openmp(for cimg_openmp_collapse(2)) \ + cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \ + else { \ + CImg res(1,lmp.result_dim); \ + T *__ptrd = data(_sx,_sy,_sz,0); \ + const ulongT off = (ulongT)_off; \ + cimg_for##_X(*this,_x) { \ + lmp(x,y,z,0,res._data); \ + const double *ptrs = res._data; \ + T *_ptrd = __ptrd; \ + for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \ + __ptrd+=off; \ + } \ + } \ + } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp + + if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } + else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } + else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) } + + lmp.end_t(); + cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); } + if (&lmp!=&mp) delete &lmp; + } +#endif + } + + } else { // Scalar-valued expression + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') { + mp.begin_t(); + if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); } + else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } + mp.end_t(); + + } else if (*expression=='>' || !do_in_parallel) { + mp.begin_t(); + if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } + else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } + mp.end_t(); + + } else { + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + lmp.is_fill = true; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + +#define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \ + cimg_pragma_openmp(for cimg_openmp_collapse(3)) \ + cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \ + else { \ + T *_ptrd = data(_sx,_sy,_sz,_sc); \ + const ulongT off = (ulongT)_off; \ + cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \ + } \ + } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp + + if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } + else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } + else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } + else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) } + + lmp.end_t(); + cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); } + if (&lmp!=&mp) delete &lmp; + } +#endif + } + } + mp.end(); + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + } + + // Try to fill values according to a value sequence. + if (!formula_mode || is_value_sequence || is_error) { + CImg item(256); + char sep = 0; + const char *nexpression = expression; + ulongT nb = 0; + const ulongT siz = size(); + T *ptrd = _data; + for (double val = 0; *nexpression && nb0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { + nexpression+=std::strlen(item) + (err>1); + *(ptrd++) = (T)val; + } else break; + } + cimg::exception_mode(omode); + if (nb get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs); + } + + //! Fill sequentially pixel values according to the values found in another image. + /** + \param values Image containing the values used for the filling. + \param repeat_values In case there are less values than necessary in \c values, tells if these values must be + repeated for the filling. + **/ + template + CImg& fill(const CImg& values, const bool repeat_values=true) { + if (is_empty() || !values) return *this; + T *ptrd = _data, *ptre = ptrd + size(); + for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs + CImg get_fill(const CImg& values, const bool repeat_values=true) const { + return repeat_values?CImg(_width,_height,_depth,_spectrum).fill(values,repeat_values): + (+*this).fill(values,repeat_values); + } + + //! Fill pixel values along the X-axis at a specified pixel position. + /** + \param y Y-coordinate of the filled column. + \param z Z-coordinate of the filled column. + \param c C-coordinate of the filled column. + \param a0 First fill value. + **/ + CImg& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) { +#define _cimg_fill1(x,y,z,c,off,siz,t) { \ + va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \ + for (unsigned int k = 1; k& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) { + if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position. + /** + \param x X-coordinate of the filled row. + \param z Z-coordinate of the filled row. + \param c C-coordinate of the filled row. + \param a0 First fill value. + **/ + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position \overloading. + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position. + /** + \param x X-coordinate of the filled slice. + \param y Y-coordinate of the filled slice. + \param c C-coordinate of the filled slice. + \param a0 First fill value. + **/ + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) { + const ulongT wh = (ulongT)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position \overloading. + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) { + const ulongT wh = (ulongT)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position. + /** + \param x X-coordinate of the filled channel. + \param y Y-coordinate of the filled channel. + \param z Z-coordinate of the filled channel. + \param a0 First filling value. + **/ + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { + const ulongT whd = (ulongT)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position \overloading. + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { + const ulongT whd = (ulongT)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double); + return *this; + } + + //! Discard specified sequence of values in the image buffer, along a specific axis. + /** + \param values Sequence of values to discard. + \param axis Axis along which the values are discarded. If set to \c 0 (default value) + the method does it for all the buffer values and returns a one-column vector. + \note Discarded values will change the image geometry, so the resulting image + is returned as a one-column vector. + **/ + template + CImg& discard(const CImg& values, const char axis=0) { + if (is_empty() || !values) return *this; + return get_discard(values,axis).move_to(*this); + } + + template + CImg get_discard(const CImg& values, const char axis=0) const { + if (!values) return +*this; + CImg res; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + ulongT j = 0; + unsigned int k = 0; + int i0 = 0; + res.assign(width(),height(),depth(),spectrum()); + switch (_axis) { + case 'x' : { + cimg_forX(*this,i) { + if ((*this)(i)!=(T)values[j]) { + if (j) --i; + res.draw_image(k,get_columns(i0,i)); + k+=i - i0 + 1; i0 = i + 1; j = 0; + } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = (int)i + 1; }} + } + if ((ulongT)i0& discard(const char axis=0) { + return get_discard(axis).move_to(*this); + } + + //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance. + CImg get_discard(const char axis=0) const { + CImg res; + if (is_empty()) return res; + const char _axis = cimg::lowercase(axis); + T current = *_data?(T)0:(T)1; + int j = 0; + res.assign(width(),height(),depth(),spectrum()); + switch (_axis) { + case 'x' : { + cimg_forX(*this,i) + if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); } + res.resize(j,-100,-100,-100,0); + } break; + case 'y' : { + cimg_forY(*this,i) + if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); } + res.resize(-100,j,-100,-100,0); + } break; + case 'z' : { + cimg_forZ(*this,i) + if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); } + res.resize(-100,-100,j,-100,0); + } break; + case 'c' : { + cimg_forC(*this,i) + if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); } + res.resize(-100,-100,-100,j,0); + } break; + default : { + res.unroll('y'); + cimg_foroff(*this,i) { + const T val = (*this)[i]; + if (val!=current) res[j++] = current = val; + } + res.resize(-100,j,-100,-100,0); + } + } + return res; + } + + //! Invert endianness of all pixel values. + /** + **/ + CImg& invert_endianness() { + cimg::invert_endianness(_data,size()); + return *this; + } + + //! Invert endianness of all pixel values \newinstance. + CImg get_invert_endianness() const { + return (+*this).invert_endianness(); + } + + //! Fill image with random values in specified range. + /** + \param val_min Minimal authorized random value. + \param val_max Maximal authorized random value. + \note Random variables are uniformly distributed in [val_min,val_max]. + **/ + CImg& rand(const T& val_min, const T& val_max) { + const float delta = (float)val_max - (float)val_min + (cimg::type::is_float()?0:1); + if (cimg::type::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng)); + cimg::srand(rng); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng))); + cimg::srand(rng); + } + return *this; + } + + //! Fill image with random values in specified range \newinstance. + CImg get_rand(const T& val_min, const T& val_max) const { + return (+*this).rand(val_min,val_max); + } + + //! Round pixel values. + /** + \param y Rounding precision. + \param rounding_type Rounding type. Can be: + - \c -1: Backward. + - \c 0: Nearest. + - \c 1: Forward. + **/ + CImg& round(const double y=1, const int rounding_type=0) { + if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192); + return *this; + } + + //! Round pixel values \newinstance. + CImg get_round(const double y=1, const unsigned int rounding_type=0) const { + return (+*this).round(y,rounding_type); + } + + //! Add random noise to pixel values. + /** + \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the + global value range. + \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, + \p 3=Poisson or \p 4=Rician). + \return A reference to the modified image instance. + \note + - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on + the image value itself. + - Function \p CImg::get_noise() is also defined. It returns a non-shared modified copy of the image instance. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_noise(40); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_noise.jpg + **/ + CImg& noise(const double sigma, const unsigned int noise_type=0) { + if (is_empty()) return *this; + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0; + if (nsigma==0 && noise_type!=3) return *this; + if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M); + if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.); + switch (noise_type) { + case 0 : { // Gaussian noise + cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) { + Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng)); + if (val>vmax) val = vmax; + if (valvmax) val = vmax; + if (val::is_float()) { --m; ++M; } + else { m = (Tfloat)cimg::type::min(); M = (Tfloat)cimg::type::max(); } + } + cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) if (cimg::rand(100,&rng)vmax) val = vmax; + if (val get_noise(const double sigma, const unsigned int noise_type=0) const { + return (+*this).noise(sigma,noise_type); + } + + //! Linearly normalize pixel values. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \param constant_case_ratio In case of instance image having a constant value, tell what ratio + of [min_value,max_value] is used to fill the normalized image + (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(160,220); + (img,res).display(); + \endcode + \image html ref_normalize2.jpg + **/ + CImg& normalize(const T& min_value, const T& max_value, + const float constant_case_ratio=0) { + if (is_empty()) return *this; + const T a = min_value get_normalize(const T& min_value, const T& max_value, + const float ratio_if_constant_image=0) const { + return CImg(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image); + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm. + /** + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_normalize.jpg + **/ + CImg& normalize() { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + T *ptrd = data(0,y,z,0); + cimg_forX(*this,x) { + const T *ptrs = ptrd; + float n = 0; + cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; } + n = (float)std::sqrt(n); + T *_ptrd = ptrd++; + if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; } + else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; } + } + } + return *this; + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance. + CImg get_normalize() const { + return CImg(*this,false).normalize(); + } + + //! Compute Lp-norm of each multi-valued pixel of the image instance. + /** + \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_norm(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_norm.jpg + **/ + CImg& norm(const int norm_type=2) { + if (_spectrum==1 && norm_type) return abs(); + return get_norm(norm_type).move_to(*this); + } + + //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance. + CImg get_norm(const int norm_type=2) const { + if (is_empty()) return *this; + if (_spectrum==1 && norm_type) return get_abs(); + const ulongT whd = (ulongT)_width*_height*_depth; + CImg res(_width,_height,_depth); + switch (norm_type) { + case -1 : { // Linf-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; } + *(ptrd++) = n; + } + } + } break; + case 0 : { // L0-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + unsigned int n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; } + *(ptrd++) = (Tfloat)n; + } + } + } break; + case 1 : { // L1-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } + *(ptrd++) = n; + } + } + } break; + case 2 : { // L2-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); + } + } + } break; + default : { // Linf-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type); + } + } + } + } + return res; + } + + //! Cut pixel values in specified range. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_cut(160,220); + (img,res).display(); + \endcode + \image html ref_cut.jpg + **/ + CImg& cut(const T& min_value, const T& max_value) { + if (is_empty()) return *this; + const T a = min_value get_cut(const T& min_value, const T& max_value) const { + return (+*this).cut(min_value,max_value); + } + + //! Uniformly quantize pixel values. + /** + \param nb_levels Number of quantization levels. + \param keep_range Tells if resulting values keep the same range as the original ones. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_quantize(4); + (img,res).display(); + \endcode + \image html ref_quantize.jpg + **/ + CImg& quantize(const unsigned int nb_levels, const bool keep_range=true) { + if (!nb_levels) + throw CImgArgumentException(_cimg_instance + "quantize(): Invalid quantization request with 0 values.", + cimg_instance); + + if (is_empty()) return *this; + Tfloat m, M = (Tfloat)max_min(m), range = M - m; + if (range>0) { + if (keep_range) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); + _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); + _data[off] = (T)std::min(val,nb_levels - 1); + } + } + return *this; + } + + //! Uniformly quantize pixel values \newinstance. + CImg get_quantize(const unsigned int n, const bool keep_range=true) const { + return (+*this).quantize(n,keep_range); + } + + //! Threshold pixel values. + /** + \param value Threshold value + \param soft_threshold Tells if soft thresholding must be applied (instead of hard one). + \param strict_threshold Tells if threshold value is strict. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_threshold(128); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_threshold.jpg + **/ + CImg& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) { + if (is_empty()) return *this; + if (strict_threshold) { + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const T v = _data[off]; + _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) + cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0; + } else { + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const T v = _data[off]; + _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) + cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0; + } + return *this; + } + + //! Threshold pixel values \newinstance. + CImg get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const { + return (+*this).threshold(value,soft_threshold,strict_threshold); + } + + //! Compute the histogram of pixel values. + /** + \param nb_levels Number of desired histogram levels. + \param min_value Minimum pixel value considered for the histogram computation. + All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. + All pixel values higher than \p max_value will not be counted. + \note + - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x + in the image I. + - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional. + \par Example + \code + const CImg img = CImg("reference.jpg").histogram(256); + img.display_graph(0,3); + \endcode + \image html ref_histogram.jpg + **/ + CImg& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) { + return get_histogram(nb_levels,min_value,max_value).move_to(*this); + } + + //! Compute the histogram of pixel values \overloading. + CImg& histogram(const unsigned int nb_levels) { + return get_histogram(nb_levels).move_to(*this); + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const { + if (!nb_levels || is_empty()) return CImg(); + const double + vmin = (double)(min_value res(nb_levels,1,1,1,0); + cimg_rof(*this,ptrs,T) { + const T val = *ptrs; + if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))]; + } + return res; + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels) const { + if (!nb_levels || is_empty()) return CImg(); + T vmax = 0, vmin = min_max(vmax); + return get_histogram(nb_levels,vmin,vmax); + } + + //! Equalize histogram of pixel values. + /** + \param nb_levels Number of histogram levels used for the equalization. + \param min_value Minimum pixel value considered for the histogram computation. + All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. + All pixel values higher than \p max_value will not be counted. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_equalize(256); + (img,res).display(); + \endcode + \image html ref_equalize.jpg + **/ + CImg& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) { + if (!nb_levels || is_empty()) return *this; + const T + vmin = min_value hist = get_histogram(nb_levels,vmin,vmax); + ulongT cumul = 0; + cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } + if (!cumul) cumul = 1; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576)) + cimg_rofoff(*this,off) { + const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin)); + if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul); + } + return *this; + } + + //! Equalize histogram of pixel values \overloading. + CImg& equalize(const unsigned int nb_levels) { + if (!nb_levels || is_empty()) return *this; + T vmax = 0, vmin = min_max(vmax); + return equalize(nb_levels,vmin,vmax); + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const { + return (+*this).equalize(nblevels,val_min,val_max); + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels) const { + return (+*this).equalize(nblevels); + } + + //! Index multi-valued pixels regarding to a specified colormap. + /** + \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing. + \param dithering Level of dithering (0=disable, 1=standard level). + \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors. + \note + - \p img.index(colormap,dithering,1) is equivalent to img.index(colormap,dithering,0).map(colormap). + \par Example + \code + const CImg img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); + const CImg res = img.get_index(colormap,1,true); + (img,res).display(); + \endcode + \image html ref_index.jpg + **/ + template + CImg& index(const CImg& colormap, const float dithering=1, const bool map_indexes=false) { + return get_index(colormap,dithering,map_indexes).move_to(*this); + } + + //! Index multi-valued pixels regarding to a specified colormap \newinstance. + template + CImg::Tuint> + get_index(const CImg& colormap, const float dithering=1, const bool map_indexes=true) const { + if (colormap._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "index(): Instance and specified colormap (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + typedef typename CImg::Tuint tuint; + if (is_empty()) return CImg(); + const ulongT + whd = (ulongT)_width*_height*_depth, + pwhd = (ulongT)colormap._width*colormap._height*colormap._depth; + CImg res(_width,_height,_depth,map_indexes?_spectrum:1); + if (dithering>0) { // Dithered versions + tuint *ptrd = res._data; + const float ndithering = cimg::cut(dithering,0,1)/16; + Tfloat valm = 0, valM = (Tfloat)max_min(valm); + if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; } + CImg cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1); + Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0); + const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth; + switch (_spectrum) { + case 1 : { // Optimized for scalars + cimg_forYZ(*this,y,z) { + if (yvalM?valM:_val0; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1, + _val2 = (Tfloat)*ptrs2, val2 = _val2valM?valM:_val2; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrpvalM?valM:_val; + dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd; + } + if (dist=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z); + for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd; + for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd; + for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, + *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z); + for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp img("reference.jpg"), + colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), + colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), + res = img.get_index(colormap1,0).map(colormap2); + (img,res).display(); + \endcode + \image html ref_map.jpg + **/ + template + CImg& map(const CImg& colormap, const unsigned int boundary_conditions=0) { + return get_map(colormap,boundary_conditions).move_to(*this); + } + + //! Map predefined colormap on the scalar (indexed) image instance \newinstance. + template + CImg get_map(const CImg& colormap, const unsigned int boundary_conditions=0) const { + const ulongT + whd = (ulongT)_width*_height*_depth, siz = size(), + cwhd = (ulongT)colormap._width*colormap._height*colormap._depth, + cwhd2 = 2*cwhd; + CImg res(_width,_height,_depth,_spectrum*colormap._spectrum); + switch (colormap._spectrum) { + + case 1 : { // Optimized for scalars + switch (boundary_conditions) { + case 3 : // Mirror + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256)) + for (longT off = 0; off<(longT)siz; ++off) { + const ulongT ind = ((ulongT)_data[off])%cwhd2; + res[off] = colormap[ind& label(const bool is_high_connectivity=false, const Tfloat tolerance=0, + const bool is_L2_norm=true) { + if (is_empty()) return *this; + return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this); + } + + //! Label connected components \newinstance. + CImg get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0, + const bool is_L2_norm=true) const { + if (is_empty()) return CImg(); + + // Create neighborhood tables. + int dx[13], dy[13], dz[13], nb = 0; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0; + dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0; + if (is_high_connectivity) { + dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0; + } + if (_depth>1) { // 3D version + dx[nb] = 0; dy[nb] = 0; dz[nb++]=1; + if (is_high_connectivity) { + dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1; + dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1; + + dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1; + } + } + return _label(nb,dx,dy,dz,tolerance,is_L2_norm); + } + + //! Label connected components \overloading. + /** + \param connectivity_mask Mask of the neighboring pixels. + \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region. + \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used. + **/ + template + CImg& label(const CImg& connectivity_mask, const Tfloat tolerance=0, + const bool is_L2_norm=true) { + if (is_empty()) return *this; + return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this); + } + + //! Label connected components \newinstance. + template + CImg get_label(const CImg& connectivity_mask, const Tfloat tolerance=0, + const bool is_L2_norm=true) const { + if (is_empty()) return CImg(); + int nb = 0; + cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb; + CImg dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0); + nb = 0; + cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) && + connectivity_mask(x,y,z)) { + dx[nb] = x; dy[nb] = y; dz[nb++] = z; + } + return _label(nb,dx,dy,dz,tolerance,is_L2_norm); + } + + CImg _label(const unsigned int nb, const int *const dx, + const int *const dy, const int *const dz, + const Tfloat tolerance, const bool is_L2_norm) const { + CImg res(_width,_height,_depth); + const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance; + + // Init label numbers. + ulongT *ptr = res.data(); + cimg_foroff(res,p) *(ptr++) = p; + + // For each neighbour-direction, label. + for (unsigned int n = 0; n& _system_strescape() { +#define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\ + move_to(list); \ + CImg(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break + CImgList list; + const T *ptrs = _data; + cimg_for(*this,p,T) switch ((int)*p) { + cimg_system_strescape('\\',"\\\\"); + cimg_system_strescape('\"',"\\\""); + cimg_system_strescape('!',"\"\\!\""); + cimg_system_strescape('`',"\\`"); + cimg_system_strescape('$',"\\$"); + } + if (ptrs(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list); + return (list>'x').move_to(*this); + } + + //@} + //--------------------------------- + // + //! \name Color Base Management + //@{ + //--------------------------------- + + //! Return colormap \e "default", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_default.jpg + **/ + static const CImg& default_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,256,1,3); + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + colormap(0,index,0) = (Tuchar)r; + colormap(0,index,1) = (Tuchar)g; + colormap(0,index++,2) = (Tuchar)b; + } + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "HSV", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hsv.jpg + **/ + static const CImg& HSV_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + CImg tmp(1,256,1,3,1); + tmp.get_shared_channel(0).sequence(0,359); + colormap = tmp.HSVtoRGB(); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "lines", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_lines.jpg + **/ + static const CImg& lines_LUT256() { + static const unsigned char pal[] = { + 0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255, + 53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125, + 206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138, + 101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125, + 16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0, + 97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121, + 227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85, + 210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255, + 20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49, + 210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121, + 166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190, + 85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81, + 247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0, + 170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81, + 215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121, + 0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125, + 0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219, + 251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239, + 89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57, + 202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125, + 32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142, + 8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65, + 194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210, + 154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85, + 146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125, + 0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49, + 89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125, + 69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69, + 40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194, + 125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0, + 125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93, + 16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85, + 0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97, + 255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49, + 162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130, + 36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125, + 219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125, + 202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210, + 85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255, + 97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255, + 85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239, + 89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166, + 0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0, + 174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69, + 162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81, + 158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97, + 57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4, + 73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 }; + static const CImg colormap(pal,1,256,1,3,false); + return colormap; + } + + //! Return colormap \e "hot", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hot.jpg + **/ + static const CImg& hot_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "cool", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cool.jpg + **/ + static const CImg& cool_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3); + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "jet", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_jet.jpg + **/ + static const CImg& jet_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "flag", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_flag.jpg + **/ + static const CImg& flag_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255; + colormap.resize(1,256,1,3,0,2); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "cube", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cube.jpg + **/ + static const CImg& cube_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,8,1,3,(T)0); + colormap[1] = colormap[3] = colormap[5] = colormap[7] = + colormap[10] = colormap[11] = colormap[12] = colormap[13] = + colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Convert pixel values from sRGB to RGB color spaces. + CImg& sRGBtoRGB() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) + cimg_rofoff(*this,off) { + const Tfloat + sval = (Tfloat)_data[off]/255, + val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f)); + _data[off] = (T)cimg::cut(val*255,0,255); + } + return *this; + } + + //! Convert pixel values from sRGB to RGB color spaces \newinstance. + CImg get_sRGBtoRGB() const { + return CImg(*this,false).sRGBtoRGB(); + } + + //! Convert pixel values from RGB to sRGB color spaces. + CImg& RGBtosRGB() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) + cimg_rofoff(*this,off) { + const Tfloat + val = (Tfloat)_data[off]/255, + sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f); + _data[off] = (T)cimg::cut(sval*255,0,255); + } + return *this; + } + + //! Convert pixel values from RGB to sRGB color spaces \newinstance. + CImg get_RGBtosRGB() const { + return CImg(*this,false).RGBtosRGB(); + } + + //! Convert pixel values from RGB to HSI color spaces. + CImg& RGBtoHSI() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSI(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSI() const { + return CImg(*this,false).RGBtoHSI(); + } + + //! Convert pixel values from HSI to RGB color spaces. + CImg& HSItoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSItoRGB(): Instance is not a HSI image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSItoRGB() const { + return CImg< Tuchar>(*this,false).HSItoRGB(); + } + + //! Convert pixel values from RGB to HSL color spaces. + CImg& RGBtoHSL() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSL(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSL() const { + return CImg(*this,false).RGBtoHSL(); + } + + //! Convert pixel values from HSL to RGB color spaces. + CImg& HSLtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSLtoRGB(): Instance is not a HSL image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSLtoRGB() const { + return CImg(*this,false).HSLtoRGB(); + } + + //! Convert pixel values from RGB to HSV color spaces. + CImg& RGBtoHSV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSV() const { + return CImg(*this,false).RGBtoHSV(); + } + + //! Convert pixel values from HSV to RGB color spaces. + CImg& HSVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSVtoRGB(): Instance is not a HSV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSVtoRGB() const { + return CImg(*this,false).HSVtoRGB(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& RGBtoYCbCr() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYCbCr(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) + for (longT N = 0; N get_RGBtoYCbCr() const { + return CImg(*this,false).RGBtoYCbCr(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& YCbCrtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YCbCrtoRGB(): Instance is not a YCbCr image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) + for (longT N = 0; N get_YCbCrtoRGB() const { + return CImg(*this,false).YCbCrtoRGB(); + } + + //! Convert pixel values from RGB to YUV color spaces. + CImg& RGBtoYUV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYUV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) + for (longT N = 0; N get_RGBtoYUV() const { + return CImg(*this,false).RGBtoYUV(); + } + + //! Convert pixel values from YUV to RGB color spaces. + CImg& YUVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YUVtoRGB(): Instance is not a YUV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) + for (longT N = 0; N get_YUVtoRGB() const { + return CImg< Tuchar>(*this,false).YUVtoRGB(); + } + + //! Convert pixel values from RGB to CMY color spaces. + CImg& RGBtoCMY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoCMY(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_RGBtoCMY() const { + return CImg(*this,false).RGBtoCMY(); + } + + //! Convert pixel values from CMY to RGB color spaces. + CImg& CMYtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoRGB(): Instance is not a CMY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_CMYtoRGB() const { + return CImg(*this,false).CMYtoRGB(); + } + + //! Convert pixel values from CMY to CMYK color spaces. + CImg& CMYtoCMYK() { + return get_CMYtoCMYK().move_to(*this); + } + + //! Convert pixel values from CMY to CMYK color spaces \newinstance. + CImg get_CMYtoCMYK() const { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoCMYK(): Instance is not a CMY image.", + cimg_instance); + + CImg res(_width,_height,_depth,4); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) + for (longT N = 0; N=255) C = M = Y = 0; + else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } + pd1[N] = (Tfloat)cimg::cut(C,0,255), + pd2[N] = (Tfloat)cimg::cut(M,0,255), + pd3[N] = (Tfloat)cimg::cut(Y,0,255), + pd4[N] = (Tfloat)cimg::cut(K,0,255); + } + return res; + } + + //! Convert pixel values from CMYK to CMY color spaces. + CImg& CMYKtoCMY() { + return get_CMYKtoCMY().move_to(*this); + } + + //! Convert pixel values from CMYK to CMY color spaces \newinstance. + CImg get_CMYKtoCMY() const { + if (_spectrum!=4) + throw CImgInstanceException(_cimg_instance + "CMYKtoCMY(): Instance is not a CMYK image.", + cimg_instance); + + CImg res(_width,_height,_depth,3); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) + for (longT N = 0; N& RGBtoXYZ(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoXYZ(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_RGBtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).RGBtoXYZ(use_D65); + } + + //! Convert pixel values from XYZ to RGB color spaces. + /** + \param use_D65 Tell to use the D65 illuminant (D50 otherwise). + **/ + CImg& XYZtoRGB(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoRGB(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_XYZtoRGB(const bool use_D65=true) const { + return CImg(*this,false).XYZtoRGB(use_D65); + } + + //! Convert pixel values from XYZ to Lab color spaces. + CImg& XYZtoLab(const bool use_D65=true) { +#define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116) + + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoLab(): Instance is not a XYZ image.", + cimg_instance); + const CImg white = CImg(1,1,1,3,255).RGBtoXYZ(use_D65); + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) + for (longT N = 0; N get_XYZtoLab(const bool use_D65=true) const { + return CImg(*this,false).XYZtoLab(use_D65); + } + + //! Convert pixel values from Lab to XYZ color spaces. + CImg& LabtoXYZ(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "LabtoXYZ(): Instance is not a Lab image.", + cimg_instance); + const CImg white = CImg(1,1,1,3,255).RGBtoXYZ(use_D65); + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) + for (longT N = 0; N216?cX*cX*cX:(116*cX - 16)*27/24389), + Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389), + Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389); + p1[N] = (T)(X*white[0]); + p2[N] = (T)(Y*white[1]); + p3[N] = (T)(Z*white[2]); + } + return *this; + } + + //! Convert pixel values from Lab to XYZ color spaces \newinstance. + CImg get_LabtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).LabtoXYZ(use_D65); + } + + //! Convert pixel values from XYZ to xyY color spaces. + CImg& XYZtoxyY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoxyY(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) + for (longT N = 0; N0?sum:1; + p1[N] = (T)(X/nsum); + p2[N] = (T)(Y/nsum); + p3[N] = (T)Y; + } + return *this; + } + + //! Convert pixel values from XYZ to xyY color spaces \newinstance. + CImg get_XYZtoxyY() const { + return CImg(*this,false).XYZtoxyY(); + } + + //! Convert pixel values from xyY pixels to XYZ color spaces. + CImg& xyYtoXYZ() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "xyYtoXYZ(): Instance is not a xyY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) + for (longT N = 0; N0?py:1; + p1[N] = (T)(px*Y/ny); + p2[N] = (T)Y; + p3[N] = (T)((1 - px - py)*Y/ny); + } + return *this; + } + + //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance. + CImg get_xyYtoXYZ() const { + return CImg(*this,false).xyYtoXYZ(); + } + + //! Convert pixel values from RGB to Lab color spaces. + CImg& RGBtoLab(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoLab(use_D65); + } + + //! Convert pixel values from RGB to Lab color spaces \newinstance. + CImg get_RGBtoLab(const bool use_D65=true) const { + return CImg(*this,false).RGBtoLab(use_D65); + } + + //! Convert pixel values from Lab to RGB color spaces. + CImg& LabtoRGB(const bool use_D65=true) { + return LabtoXYZ().XYZtoRGB(use_D65); + } + + //! Convert pixel values from Lab to RGB color spaces \newinstance. + CImg get_LabtoRGB(const bool use_D65=true) const { + return CImg(*this,false).LabtoRGB(use_D65); + } + + //! Convert pixel values from RGB to xyY color spaces. + CImg& RGBtoxyY(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoxyY(); + } + + //! Convert pixel values from RGB to xyY color spaces \newinstance. + CImg get_RGBtoxyY(const bool use_D65=true) const { + return CImg(*this,false).RGBtoxyY(use_D65); + } + + //! Convert pixel values from xyY to RGB color spaces. + CImg& xyYtoRGB(const bool use_D65=true) { + return xyYtoXYZ().XYZtoRGB(use_D65); + } + + //! Convert pixel values from xyY to RGB color spaces \newinstance. + CImg get_xyYtoRGB(const bool use_D65=true) const { + return CImg(*this,false).xyYtoRGB(use_D65); + } + + //! Convert pixel values from RGB to CMYK color spaces. + CImg& RGBtoCMYK() { + return RGBtoCMY().CMYtoCMYK(); + } + + //! Convert pixel values from RGB to CMYK color spaces \newinstance. + CImg get_RGBtoCMYK() const { + return CImg(*this,false).RGBtoCMYK(); + } + + //! Convert pixel values from CMYK to RGB color spaces. + CImg& CMYKtoRGB() { + return CMYKtoCMY().CMYtoRGB(); + } + + //! Convert pixel values from CMYK to RGB color spaces \newinstance. + CImg get_CMYKtoRGB() const { + return CImg(*this,false).CMYKtoRGB(); + } + + //@} + //------------------------------------------ + // + //! \name Geometric / Spatial Manipulation + //@{ + //------------------------------------------ + + static float _cimg_lanczos(const float x) { + if (x<=-2 || x>=2) return 0; + const float a = (float)cimg::PI*x, b = 0.5f*a; + return (float)(x?std::sin(a)*std::sin(b)/(a*b):1); + } + + //! Resize image to new dimensions. + /** + \param size_x Number of columns (new size along the X-axis). + \param size_y Number of rows (new size along the Y-axis). + \param size_z Number of slices (new size along the Z-axis). + \param size_c Number of vector-channels (new size along the C-axis). + \param interpolation_type Method of interpolation: + - -1 = no interpolation: raw memory resizing. + - 0 = no interpolation: additional space is filled according to \p boundary_conditions. + - 1 = nearest-neighbor interpolation. + - 2 = moving average interpolation. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = cubic interpolation. + - 6 = lanczos interpolation. + \param boundary_conditions Type of boundary conditions used if necessary. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const int size_x, const int size_y=-100, + const int size_z=-100, const int size_c=-100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + if (!size_x || !size_y || !size_z || !size_c) return assign(); + const unsigned int + _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x), + _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y), + _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z), + _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c), + sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1; + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this; + if (is_empty()) return assign(sx,sy,sz,sc,(T)0); + if (interpolation_type==-1 && sx*sy*sz*sc==size()) { + _width = sx; _height = sy; _depth = sz; _spectrum = sc; + return *this; + } + return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c).move_to(*this); + } + + //! Resize image to new dimensions \newinstance. + CImg get_resize(const int size_x, const int size_y = -100, + const int size_z = -100, const int size_c = -100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 || + centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1) + throw CImgArgumentException(_cimg_instance + "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].", + cimg_instance, + centering_x,centering_y,centering_z,centering_c); + + if (!size_x || !size_y || !size_z || !size_c) return CImg(); + const unsigned int + sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)), + sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)), + sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)), + sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100)); + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this; + if (is_empty()) return CImg(sx,sy,sz,sc,(T)0); + CImg res; + switch (interpolation_type) { + + // Raw resizing. + // + case -1 : + std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc)); + break; + + // No interpolation. + // + case 0 : { + const int + xc = (int)(centering_x*((int)sx - width())), + yc = (int)(centering_y*((int)sy - height())), + zc = (int)(centering_z*((int)sz - depth())), + cc = (int)(centering_c*((int)sc - spectrum())); + + switch (boundary_conditions) { + case 3 : { // Mirror + res.assign(sx,sy,sz,sc); + const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024)) + cimg_forXYZC(res,x,y,z,c) { + const int + mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2), + mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2); + res(x,y,z,c) = (*this)(mx sprite; + if (xc>0) { // X-backward + res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite); + } + if (xc + width()<(int)sx) { // X-forward + res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1, + zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite); + } + if (yc>0) { // Y-backward + res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite); + } + if (yc + height()<(int)sy) { // Y-forward + res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1, + zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite); + } + if (zc>0) { // Z-backward + res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite); + for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite); + } + if (zc + depth()<(int)sz) { // Z-forward + res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite); + } + if (cc>0) { // C-backward + res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite); + for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite); + } + if (cc + spectrum()<(int)sc) { // C-forward + res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite); + for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); + } + } break; + default : // Dirichlet + res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this); + } + break; + } break; + + // Nearest neighbor interpolation. + // + case 1 : { + res.assign(sx,sy,sz,sc); + CImg off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1); + const ulongT + wh = (ulongT)_width*_height, + whd = (ulongT)_width*_height*_depth, + sxy = (ulongT)sx*sy, + sxyz = (ulongT)sx*sy*sz, + one = (ulongT)1; + if (sx==_width) off_x.fill(1); + else { + ulongT *poff_x = off_x._data, curr = 0; + cimg_forX(res,x) { + const ulongT old = curr; + curr = (x + one)*_width/sx; + *(poff_x++) = curr - old; + } + } + if (sy==_height) off_y.fill(_width); + else { + ulongT *poff_y = off_y._data, curr = 0; + cimg_forY(res,y) { + const ulongT old = curr; + curr = (y + one)*_height/sy; + *(poff_y++) = _width*(curr - old); + } + *poff_y = 0; + } + if (sz==_depth) off_z.fill(wh); + else { + ulongT *poff_z = off_z._data, curr = 0; + cimg_forZ(res,z) { + const ulongT old = curr; + curr = (z + one)*_depth/sz; + *(poff_z++) = wh*(curr - old); + } + *poff_z = 0; + } + if (sc==_spectrum) off_c.fill(whd); + else { + ulongT *poff_c = off_c._data, curr = 0; + cimg_forC(res,c) { + const ulongT old = curr; + curr = (c + one)*_spectrum/sc; + *(poff_c++) = whd*(curr - old); + } + *poff_c = 0; + } + + T *ptrd = res._data; + const T* ptrc = _data; + const ulongT *poff_c = off_c._data; + for (unsigned int c = 0; c_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,_height,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256)) + cimg_forYZC(tmp,y,z,v) { + for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; + if (!b) { tmp(t++,y,z,v)/=_width; b = _width; } + if (!c) { ++s; c = sx; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sy!=_height) { + if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256)) + cimg_forXZC(tmp,x,z,v) { + for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; + else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; + if (!b) { tmp(x,t++,z,v)/=_height; b = _height; } + if (!c) { ++s; c = sy; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sz!=_depth) { + if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,sz,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256)) + cimg_forXYC(tmp,x,y,v) { + for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; + else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; + if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; } + if (!c) { ++s; c = sz; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sc!=_spectrum) { + if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res); + else { + CImg tmp(sx,sy,sz,sc,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sc>=256 && _width*_height*_depth>=256)) + cimg_forXYZ(tmp,x,y,z) { + for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; + else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; + if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; } + if (!c) { ++s; c = sc; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + } break; + + // Linear interpolation. + // + case 3 : { + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1; + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrs resx, resy, resz, resc; + if (sx!=_width) { + if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + resx.assign(sx,_height,_depth,_spectrum,(T)0); + const int dx = (int)(2*sx), dy = 2*width(); + int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0; + cimg_forX(resx,x) if ((err-=dy)<=0) { + cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c); + ++xs; + err+=dx; + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + resy.assign(sx,sy,_depth,_spectrum,(T)0); + const int dx = (int)(2*sy), dy = 2*height(); + int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0; + cimg_forY(resy,y) if ((err-=dy)<=0) { + cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c); + ++ys; + err+=dx; + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + resz.assign(sx,sy,sz,_spectrum,(T)0); + const int dx = (int)(2*sz), dy = 2*depth(); + int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0; + cimg_forZ(resz,z) if ((err-=dy)<=0) { + cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c); + ++zs; + err+=dx; + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + resc.assign(sx,sy,sz,sc,(T)0); + const int dx = (int)(2*sc), dy = 2*spectrum(); + int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0; + cimg_forC(resc,c) if ((err-=dy)<=0) { + cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs); + ++cs; + err+=dx; + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Cubic interpolation. + // + case 5 : { + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2); + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1, + val3 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Lanczos interpolation. + // + case 6 : { + const double vmin = (double)cimg::type::min(), vmax = (double)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, + *const ptrsmax = ptrs0 + (_width - 2); + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2, + val4 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, + *const ptrsmax = ptrs0 + (_height - 2)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, + *const ptrsmax = ptrs0 + (_depth - 2)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, + *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Unknown interpolation. + // + default : + throw CImgArgumentException(_cimg_instance + "resize(): Invalid specified interpolation %d " + "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | " + "5=cubic | 6=lanczos }).", + cimg_instance, + interpolation_type); + } + return res; + } + + //! Resize image to dimensions of another image. + /** + \param src Reference image used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + template + CImg& resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of another image \newinstance. + template + CImg get_resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window. + /** + \param disp Reference display window used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + CImg& resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window \newinstance. + CImg get_resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to half-size along XY axes, using an optimized filter. + CImg& resize_halfXY() { + return get_resize_halfXY().move_to(*this); + } + + //! Resize image to half-size along XY axes, using an optimized filter \newinstance. + CImg get_resize_halfXY() const { + if (is_empty()) return *this; + static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + CImg I(9), res(_width/2,_height/2,_depth,_spectrum); + T *ptrd = res._data; + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) + if (x%2 && y%2) *(ptrd++) = (T) + (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] + + I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] + + I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]); + return res; + } + + //! Resize image to double-size, using the Scale2X algorithm. + /** + \note Use anisotropic upscaling algorithm + described here. + **/ + CImg& resize_doubleXY() { + return get_resize_doubleXY().move_to(*this); + } + + //! Resize image to double-size, using the Scale2X algorithm \newinstance. + CImg get_resize_doubleXY() const { +#define _cimg_gs2x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width) + +#define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs2x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(_width<<1,_height<<1,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width; + _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) { + if (Icp!=Icn && Ipc!=Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = Ipc==Icn?Ipc:Icc; + *(ptrd2++) = Icn==Inc?Inc:Icc; + } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; } + } + } + return res; + } + + //! Resize image to triple-size, using the Scale3X algorithm. + /** + \note Use anisotropic upscaling algorithm + described here. + **/ + CImg& resize_tripleXY() { + return get_resize_tripleXY().move_to(*this); + } + + //! Resize image to triple-size, using the Scale3X algorithm \newinstance. + CImg get_resize_tripleXY() const { +#define _cimg_gs3x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width) + +#define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs3x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(3*_width,3*_height,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width, + *ptrd3 = ptrd2 + res._width; + _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) { + if (Icp != Icn && Ipc != Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc; + *(ptrd2++) = Icc; + *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc; + *(ptrd3++) = Ipc==Icn?Ipc:Icc; + *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc; + *(ptrd3++) = Icn==Inc?Inc:Icc; + } else { + *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc; + *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; + *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; + } + } + } + return res; + } + + //! Mirror image content along specified axis. + /** + \param axis Mirror axis + **/ + CImg& mirror(const char axis) { + if (is_empty()) return *this; + T *pf, *pb, *buf = 0; + switch (cimg::lowercase(axis)) { + case 'x' : { + pf = _data; pb = data(_width - 1); + const unsigned int width2 = _width/2; + for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) { + for (unsigned int x = 0; x get_mirror(const char axis) const { + return (+*this).mirror(axis); + } + + //! Mirror image content along specified axes. + /** + \param axes Mirror axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" + **/ + CImg& mirror(const char *const axes) { + for (const char *s = axes; *s; ++s) mirror(*s); + return *this; + } + + //! Mirror image content along specified axes \newinstance. + CImg get_mirror(const char *const axes) const { + return (+*this).mirror(axes); + } + + //! Shift image content. + /** + \param delta_x Amount of displacement along the X-axis. + \param delta_y Amount of displacement along the Y-axis. + \param delta_z Amount of displacement along the Z-axis. + \param delta_c Amount of displacement along the C-axis. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const unsigned int boundary_conditions=0) { + if (is_empty()) return *this; + if (boundary_conditions==3) + return get_crop(-delta_x,-delta_y,-delta_z,-delta_c, + width() - delta_x - 1, + height() - delta_y - 1, + depth() - delta_z - 1, + spectrum() - delta_c - 1,3).move_to(*this); + if (delta_x) // Shift along X-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width()); + if (!ndelta_x) return *this; + CImg buf(cimg::abs(ndelta_x)); + if (ndelta_x>0) cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T)); + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T)); + std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T)); + std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_x<0) { + const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(_width - 1,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width())?width() - 1:delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(0,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width()) return fill((T)0); + if (delta_x<0) cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T)); + std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T)); + std::memset(data(0,y,z,c),0,delta_x*sizeof(T)); + } + } + + if (delta_y) // Shift along Y-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height()); + if (!ndelta_y) return *this; + CImg buf(width(),cimg::abs(ndelta_y)); + if (ndelta_y>0) cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T)); + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T)); + std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T)); + std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_y<0) { + const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c); + for (int l = 0; l=height())?height() - 1:delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c); + for (int l = 0; l=height()) return fill((T)0); + if (delta_y<0) cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T)); + std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T)); + std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T)); + } + } + + if (delta_z) // Shift along Z-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth()); + if (!ndelta_z) return *this; + CImg buf(width(),height(),cimg::abs(ndelta_z)); + if (ndelta_z>0) cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T)); + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T)); + } else cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T)); + std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_z<0) { + const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c); + for (int l = 0; l=depth())?depth() - 1:delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c); + for (int l = 0; l=depth()) return fill((T)0); + if (delta_z<0) cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T)); + std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T)); + } else cimg_forC(*this,c) { + std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T)); + std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T)); + } + } + + if (delta_c) // Shift along C-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum()); + if (!ndelta_c) return *this; + CImg buf(width(),height(),depth(),cimg::abs(ndelta_c)); + if (ndelta_c>0) { + std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T)); + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T)); + } else { + std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T)); + std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T)); + std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_c<0) { + const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c; + if (!ndelta_c) return *this; + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1); + for (int l = 0; l=spectrum())?spectrum() - 1:delta_c; + if (!ndelta_c) return *this; + std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,1); + for (int l = 0; l=spectrum()) return fill((T)0); + if (delta_c<0) { + std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T)); + std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T)); + } else { + std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T)); + std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T)); + } + } + return *this; + } + + //! Shift image content \newinstance. + CImg get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const unsigned int boundary_conditions=0) const { + return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions); + } + + //! Permute axes order. + /** + \param axes_order Axes permutations, as a C-string of 4 characters. + This function permutes image content regarding the specified axes permutation. + **/ + CImg& permute_axes(const char *const axes_order) { + return get_permute_axes(axes_order).move_to(*this); + } + + //! Permute axes order \newinstance. + CImg get_permute_axes(const char *const axes_order) const { + const T foo = (T)0; + return _permute_axes(axes_order,foo); + } + + template + CImg _permute_axes(const char *const axes_order, const t&) const { + if (is_empty() || !axes_order) return CImg(*this,false); + CImg res; + const T* ptrs = _data; + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } + else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; } + } + if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { + const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); + ulongT wh, whd; + switch (code) { + case 0x0123 : // xyzc + return +*this; + case 0x0132 : // xycz + res.assign(_width,_height,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0213 : // xzyc + res.assign(_width,_depth,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x0231 : // xzcy + res.assign(_width,_depth,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x0312 : // xcyz + res.assign(_width,_spectrum,_height,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0321 : // xczy + res.assign(_width,_spectrum,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x1023 : // yxzc + res.assign(_height,_width,_depth,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1032 : // yxcz + res.assign(_height,_width,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1203 : // yzxc + res.assign(_height,_depth,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1230 : // yzcx + res.assign(_height,_depth,_spectrum,_width); + switch (_width) { + case 1 : { + t *ptr_r = res.data(0,0,0,0); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); + } + } break; + case 2 : { + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + ptrs+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + ptrs+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA + t + *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), + *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + *(ptr_a++) = (t)ptrs[3]; + ptrs+=4; + } + } break; + default : { + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); + return res; + } + } + break; + case 0x1302 : // ycxz + res.assign(_height,_spectrum,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1320 : // yczx + res.assign(_height,_spectrum,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2013 : // zxyc + res.assign(_depth,_width,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2031 : // zxcy + res.assign(_depth,_width,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2103 : // zyxc + res.assign(_depth,_height,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2130 : // zycx + res.assign(_depth,_height,_spectrum,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2301 : // zcxy + res.assign(_depth,_spectrum,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2310 : // zcyx + res.assign(_depth,_spectrum,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3012 : // cxyz + res.assign(_spectrum,_width,_height,_depth); + switch (_spectrum) { + case 1 : { + const T *ptr_r = data(0,0,0,0); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); + } break; + case 2 : { + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd[3] = (t)*(ptr_a++); + ptrd+=4; + } + } break; + default : { + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); + } + } + break; + case 0x3021 : // cxzy + res.assign(_spectrum,_width,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3102 : // cyxz + res.assign(_spectrum,_height,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x3120 : // cyzx + res.assign(_spectrum,_height,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3201 : // czxy + res.assign(_spectrum,_depth,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3210 : // czyx + res.assign(_spectrum,_depth,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); + break; + } + } + if (!res) + throw CImgArgumentException(_cimg_instance + "permute_axes(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + return res; + } + + //! Unroll pixel values along specified axis. + /** + \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c'). + **/ + CImg& unroll(const char axis) { + const unsigned int siz = (unsigned int)size(); + if (siz) switch (cimg::lowercase(axis)) { + case 'x' : _width = siz; _height = _depth = _spectrum = 1; break; + case 'y' : _height = siz; _width = _depth = _spectrum = 1; break; + case 'z' : _depth = siz; _width = _height = _spectrum = 1; break; + case 'c' : _spectrum = siz; _width = _height = _depth = 1; break; + } + return *this; + } + + //! Unroll pixel values along specified axis \newinstance. + CImg get_unroll(const char axis) const { + return (+*this).unroll(axis); + } + + //! Rotate image with arbitrary angle. + /** + \param angle Rotation angle, in degrees. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note The size of the image is modified. + **/ + CImg& rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate image with arbitrary angle \newinstance. + CImg get_rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res; + const float nangle = cimg::mod(angle,360.f); + if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles + const int wm1 = width() - 1, hm1 = height() - 1; + const int iangle = (int)nangle/90; + switch (iangle) { + case 1 : { // 90 deg + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c); + } break; + case 2 : { // 180 deg + res.assign(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c); + } break; + case 3 : { // 270 deg + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c); + } break; + default : // 0 deg + return *this; + } + } else { // Generic angle + const float + rad = (float)(nangle*cimg::PI/180.), + ca = (float)std::cos(rad), sa = (float)std::sin(rad), + ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa), + vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca), + w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1); + res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum); + const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1); + _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2); + } + return res; + } + + //! Rotate image with arbitrary angle, around a center point. + /** + \param angle Rotation angle, in degrees. + \param cx X-coordinate of the rotation center. + \param cy Y-coordinate of the rotation center. + \param interpolation Type of interpolation, { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions, { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& rotate(const float angle, const float cx, const float cy, + const unsigned int interpolation, const unsigned int boundary_conditions=0) { + return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate image with arbitrary angle, around a center point \newinstance. + CImg get_rotate(const float angle, const float cx, const float cy, + const unsigned int interpolation, const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy); + return res; + } + + // [internal] Perform 2D rotation with arbitrary angle. + void _rotate(CImg& res, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions, + const float w2, const float h2, + const float rw2, const float rh2) const { + const float + rad = (float)(angle*cimg::PI/180.), + ca = (float)std::cos(rad), sa = (float)std::sin(rad); + + switch (boundary_conditions) { + case 3 : { // Mirror + + switch (interpolation) { + case 2 : { // Cubic interpolation + const float ww = 2.f*width(), hh = 2.f*height(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048)) + cimg_forXYZC(res,x,y,z,c) { + const float xc = x - rw2, yc = y - rh2, + mx = cimg::mod(w2 + xc*ca + yc*sa,ww), + my = cimg::mod(h2 - xc*sa + yc*ca,hh); + res(x,y,z,c) = _cubic_atXY_c(mx{ 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note Most of the time, size of the image is modified. + **/ + CImg rotate(const float u, const float v, const float w, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate volumetric image with arbitrary angle and axis \newinstance. + CImg get_rotate(const float u, const float v, const float w, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions) const { + if (is_empty()) return *this; + CImg res; + const float + w1 = _width - 1, h1 = _height - 1, d1 = _depth -1, + w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1; + CImg R = CImg::rotation_matrix(u,v,w,angle); + const CImg + X = R*CImg(8,3,1,1, + 0.f,w1,w1,0.f,0.f,w1,w1,0.f, + 0.f,0.f,h1,h1,0.f,0.f,h1,h1, + 0.f,0.f,0.f,0.f,d1,d1,d1,d1); + float + xm, xM = X.get_shared_row(0).max_min(xm), + ym, yM = X.get_shared_row(1).max_min(ym), + zm, zM = X.get_shared_row(2).max_min(zm); + const int + dx = (int)cimg::round(xM - xm), + dy = (int)cimg::round(yM - ym), + dz = (int)cimg::round(zM - zm); + R.transpose(); + res.assign(1 + dx,1 + dy,1 + dz,_spectrum); + const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz; + _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2); + return res; + } + + //! Rotate volumetric image with arbitrary angle and axis, around a center point. + /** + \param u X-coordinate of the 3D rotation axis. + \param v Y-coordinate of the 3D rotation axis. + \param w Z-coordinate of the 3D rotation axis. + \param angle Rotation angle, in degrees. + \param cx X-coordinate of the rotation center. + \param cy Y-coordinate of the rotation center. + \param cz Z-coordinate of the rotation center. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic }. + \note Most of the time, size of the image is modified. + **/ + CImg rotate(const float u, const float v, const float w, const float angle, + const float cx, const float cy, const float cz, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance. + CImg get_rotate(const float u, const float v, const float w, const float angle, + const float cx, const float cy, const float cz, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + CImg R = CImg::rotation_matrix(u,v,w,-angle); + _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz); + return res; + } + + // [internal] Perform 3D rotation with arbitrary axis and angle. + void _rotate(CImg& res, const CImg& R, + const unsigned int interpolation, const unsigned int boundary_conditions, + const float w2, const float h2, const float d2, + const float rw2, const float rh2, const float rd2) const { + switch (boundary_conditions) { + case 3 : // Mirror + switch (interpolation) { + case 2 : { // Cubic interpolation + const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048)) + cimg_forXYZ(res,x,y,z) { + const float + xc = x - rw2, yc = y - rh2, zc = z - rd2, + X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), + Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), + Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); + cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X{ 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + template + CImg& warp(const CImg& p_warp, const unsigned int mode=0, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this); + } + + //! Warp image content by a warping field \newinstance + template + CImg get_warp(const CImg& p_warp, const unsigned int mode=0, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty() || !p_warp) return *this; + if (mode && !is_sameXYZ(p_warp)) + throw CImgArgumentException(_cimg_instance + "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) " + "have different XYZ dimensions.", + cimg_instance, + p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data); + + CImg res(p_warp._width,p_warp._height,p_warp._depth,_spectrum); + + if (p_warp._spectrum==1) { // 1D warping + if (mode>=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = x + (int)cimg::round(*(ptrs0++)); + if (X>=0 && X=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = (int)cimg::round(*(ptrs0++)); + if (X>=0 && X=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++)); + if (X>=0 && X=0 && Y=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++)); + if (X>=0 && X=0 && Y=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++), + z + (float)*(ptrs2++),c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int + X = x + (int)cimg::round(*(ptrs0++)), + Y = y + (int)cimg::round(*(ptrs1++)), + Z = z + (int)cimg::round(*(ptrs2++)); + if (X>=0 && X=0 && Y=0 && Z=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int + X = (int)cimg::round(*(ptrs0++)), + Y = (int)cimg::round(*(ptrs1++)), + Z = (int)cimg::round(*(ptrs2++)); + if (X>=0 && X=0 && Y=0 && Z get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const { + if (is_empty() || _depth<2) return +*this; + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + const CImg + img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1), + img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc"). + resize(_depth,_height,1,-100,-1), + img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1); + return CImg(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())). + draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy). + draw_image(0,img_xy._height,img_xz); + } + + //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace. + CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) { + if (_depth<2) return *this; + return get_projections2d(x0,y0,z0).move_to(*this); + } + + //! Crop image region. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param z0 = Z-coordinate of the upper-left crop rectangle corner. + \param c0 = C-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param z1 = Z-coordinate of the lower-right crop rectangle corner. + \param c1 = C-coordinate of the lower-right crop rectangle corner. + \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const unsigned int boundary_conditions=0) { + return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const unsigned int boundary_conditions=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "crop(): Empty instance.", + cimg_instance); + const int + nx0 = x0=0 && nx1=0 && ny1=0 && nz1=0 && nc1 res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0); + if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) + switch (_boundary_conditions) { + case 3 : { // Mirror + const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) { + const int + mx = cimg::mod(nx0 + x,w2), + my = cimg::mod(ny0 + y,h2), + mz = cimg::mod(nz0 + z,d2), + mc = cimg::mod(nc0 + c,s2); + res(x,y,z,c) = (*this)(mx=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) { + res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()), + cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum())); + } + } break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c); + break; + default : // Dirichlet + res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this); + } + else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this); + return res; + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const unsigned int boundary_conditions=0) { + return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, + const int x1, const int y1, + const unsigned int boundary_conditions=0) { + return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, + const int x1, const int y1, + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) { + return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const { + return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Autocrop image region, regarding the specified background value. + CImg& autocrop(const T& value, const char *const axes="czyx") { + if (is_empty()) return *this; + for (const char *s = axes; *s; ++s) { + const char axis = cimg::lowercase(*s); + const CImg coords = _autocrop(value,axis); + if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels + else switch (axis) { + case 'x' : { + const int x0 = coords[0], x1 = coords[1]; + if (x0>=0 && x1>=0) crop(x0,x1); + } break; + case 'y' : { + const int y0 = coords[0], y1 = coords[1]; + if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1); + } break; + case 'z' : { + const int z0 = coords[0], z1 = coords[1]; + if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1); + } break; + default : { + const int c0 = coords[0], c1 = coords[1]; + if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background value \newinstance. + CImg get_autocrop(const T& value, const char *const axes="czyx") const { + return (+*this).autocrop(value,axes); + } + + //! Autocrop image region, regarding the specified background color. + /** + \param color Color used for the crop. If \c 0, color is guessed. + \param axes Axes used for the crop. + **/ + CImg& autocrop(const T *const color=0, const char *const axes="zyx") { + if (is_empty()) return *this; + if (!color) { // Guess color + const CImg col1 = get_vector_at(0,0,0); + const unsigned int w = _width, h = _height, d = _depth, s = _spectrum; + autocrop(col1,axes); + if (_width==w && _height==h && _depth==d && _spectrum==s) { + const CImg col2 = get_vector_at(w - 1,h - 1,d - 1); + autocrop(col2,axes); + } + return *this; + } + for (const char *s = axes; *s; ++s) { + const char axis = cimg::lowercase(*s); + switch (axis) { + case 'x' : { + int x0 = width(), x1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'x'); + const int nx0 = coords[0], nx1 = coords[1]; + if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); } + } + if (x0==width() && x1==-1) return assign(); else crop(x0,x1); + } break; + case 'y' : { + int y0 = height(), y1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'y'); + const int ny0 = coords[0], ny1 = coords[1]; + if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); } + } + if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1); + } break; + default : { + int z0 = depth(), z1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'z'); + const int nz0 = coords[0], nz1 = coords[1]; + if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); } + } + if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background color \newinstance. + CImg get_autocrop(const T *const color=0, const char *const axes="zyx") const { + return (+*this).autocrop(color,axes); + } + + CImg _autocrop(const T& value, const char axis) const { + CImg res; + switch (cimg::lowercase(axis)) { + case 'x' : { + int x0 = -1, x1 = -1; + cimg_forX(*this,x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); } + if (x0>=0) { + for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); } + } + res = CImg::vector(x0,x1); + } break; + case 'y' : { + int y0 = -1, y1 = -1; + cimg_forY(*this,y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); } + if (y0>=0) { + for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); } + } + res = CImg::vector(y0,y1); + } break; + case 'z' : { + int z0 = -1, z1 = -1; + cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); } + if (z0>=0) { + for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); } + } + res = CImg::vector(z0,z1); + } break; + default : { + int c0 = -1, c1 = -1; + cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); } + if (c0>=0) { + for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; } + } + res = CImg::vector(c0,c1); + } + } + return res; + } + + //! Return specified image column. + /** + \param x0 Image column. + **/ + CImg get_column(const int x0) const { + return get_columns(x0,x0); + } + + //! Return specified image column \inplace. + CImg& column(const int x0) { + return columns(x0,x0); + } + + //! Return specified range of image columns. + /** + \param x0 Starting image column. + \param x1 Ending image column. + **/ + CImg& columns(const int x0, const int x1) { + return get_columns(x0,x1).move_to(*this); + } + + //! Return specified range of image columns \inplace. + CImg get_columns(const int x0, const int x1) const { + return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1); + } + + //! Return specified image row. + CImg get_row(const int y0) const { + return get_rows(y0,y0); + } + + //! Return specified image row \inplace. + /** + \param y0 Image row. + **/ + CImg& row(const int y0) { + return rows(y0,y0); + } + + //! Return specified range of image rows. + /** + \param y0 Starting image row. + \param y1 Ending image row. + **/ + CImg get_rows(const int y0, const int y1) const { + return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1); + } + + //! Return specified range of image rows \inplace. + CImg& rows(const int y0, const int y1) { + return get_rows(y0,y1).move_to(*this); + } + + //! Return specified image slice. + /** + \param z0 Image slice. + **/ + CImg get_slice(const int z0) const { + return get_slices(z0,z0); + } + + //! Return specified image slice \inplace. + CImg& slice(const int z0) { + return slices(z0,z0); + } + + //! Return specified range of image slices. + /** + \param z0 Starting image slice. + \param z1 Ending image slice. + **/ + CImg get_slices(const int z0, const int z1) const { + return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1); + } + + //! Return specified range of image slices \inplace. + CImg& slices(const int z0, const int z1) { + return get_slices(z0,z1).move_to(*this); + } + + //! Return specified image channel. + /** + \param c0 Image channel. + **/ + CImg get_channel(const int c0) const { + return get_channels(c0,c0); + } + + //! Return specified image channel \inplace. + CImg& channel(const int c0) { + return channels(c0,c0); + } + + //! Return specified range of image channels. + /** + \param c0 Starting image channel. + \param c1 Ending image channel. + **/ + CImg get_channels(const int c0, const int c1) const { + return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1); + } + + //! Return specified range of image channels \inplace. + CImg& channels(const int c0, const int c1) { + return get_channels(c0,c1).move_to(*this); + } + + //! Return stream line of a 2D or 3D vector field. + CImg get_streamline(const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false) const { + if (_spectrum!=2 && _spectrum!=3) + throw CImgInstanceException(_cimg_instance + "streamline(): Instance is not a 2D or 3D vector field.", + cimg_instance); + if (_spectrum==2) { + if (is_oriented_only) { + typename CImg::_functor4d_streamline2d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.f,_height - 1.f,0.f); + } else { + typename CImg::_functor4d_streamline2d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.f,_height - 1.f,0.f); + } + } + if (is_oriented_only) { + typename CImg::_functor4d_streamline3d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); + } + typename CImg::_functor4d_streamline3d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); + } + + //! Return stream line of a 3D vector field. + /** + \param func Vector field function. + \param x X-coordinate of the starting point of the streamline. + \param y Y-coordinate of the starting point of the streamline. + \param z Z-coordinate of the starting point of the streamline. + \param L Streamline length. + \param dl Streamline length increment. + \param interpolation_type Type of interpolation. + Can be { 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }. + \param is_backward_tracking Tells if the streamline is estimated forward or backward. + \param is_oriented_only Tells if the direction of the vectors must be ignored. + \param x0 X-coordinate of the first bounding-box vertex. + \param y0 Y-coordinate of the first bounding-box vertex. + \param z0 Z-coordinate of the first bounding-box vertex. + \param x1 X-coordinate of the second bounding-box vertex. + \param y1 Y-coordinate of the second bounding-box vertex. + \param z1 Z-coordinate of the second bounding-box vertex. + **/ + template + static CImg streamline(const tfunc& func, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + if (dl<=0) + throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g " + "(should be >0).", + pixel_type(), + dl); + + const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1); + if (L<=0 || (is_bounded && (xx1 || yy1 || zz1))) return CImg(); + const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1); + CImg coordinates(size_L,3); + const float dl2 = dl/2; + float + *ptr_x = coordinates.data(0,0), + *ptr_y = coordinates.data(0,1), + *ptr_z = coordinates.data(0,2), + pu = (float)(dl*func(x,y,z,0)), + pv = (float)(dl*func(x,y,z,1)), + pw = (float)(dl*func(x,y,z,2)), + X = x, Y = y, Z = z; + + switch (interpolation_type) { + case 0 : { // Nearest integer interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + const int + xi = (int)(X>0?X + 0.5f:X - 0.5f), + yi = (int)(Y>0?Y + 0.5f:Y - 0.5f), + zi = (int)(Z>0?Z + 0.5f:Z - 0.5f); + float + u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)), + v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)), + w = (float)(dl*func((float)xi,(float)yi,(float)zi,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 1 : { // First-order interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u = (float)(dl*func(X,Y,Z,0)), + v = (float)(dl*func(X,Y,Z,1)), + w = (float)(dl*func(X,Y,Z,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 2 : { // Second order interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)), + v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)), + w = (float)(dl*func(X + u0,Y + v0,Z + w0,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + default : { // Fourth order interpolation + cimg_forX(coordinates,k) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)), + v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)), + w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2)); + if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; } + float + u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)), + v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)), + w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; } + float + u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)), + v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)), + w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; } + const float + u = (u0 + u3)/3 + (u1 + u2)/1.5f, + v = (v0 + v3)/3 + (v1 + v2)/1.5f, + w = (w0 + w3)/3 + (w1 + w2)/1.5f; + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } + } + if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0); + return coordinates; + } + + //! Return stream line of a 3D vector field \overloading. + static CImg streamline(const char *const expression, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=true, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + _functor4d_streamline_expr func(expression); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1); + } + + struct _functor4d_streamline2d_directed { + const CImg& ref; + _functor4d_streamline2d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0; + } + }; + + struct _functor4d_streamline3d_directed { + const CImg& ref; + _functor4d_streamline3d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref._linear_atXYZ(x,y,z,c); + } + }; + + struct _functor4d_streamline2d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline2d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,1,2); } + ~_functor4d_streamline2d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign2d(i,j) \ + if (I(i,j,0)*I(0,0,0) + I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z; + const float + dx = x - xi, + dy = y - yi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; + if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width() - 1; + if (nxi>=ref.width()) nxi = ref.width() - 1; + if (yi<0) yi = 0; + if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height() - 1; + if (nyi>=ref.height()) nyi = ref.height() - 1; + I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1); + I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1); + I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1); + I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1); + _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1); + } + return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0; + } + }; + + struct _functor4d_streamline3d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline3d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,2,3); } + ~_functor4d_streamline3d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0) + I(i,j,k,1)*I(0,0,0,1) + I(i,j,k,2)*I(0,0,0,2)<0) { \ + I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z - (z>=0?0:1), nzi = zi + 1; + const float + dx = x - xi, + dy = y - yi, + dz = z - zi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; + if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width() - 1; + if (nxi>=ref.width()) nxi = ref.width() - 1; + if (yi<0) yi = 0; + if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height() - 1; + if (nyi>=ref.height()) nyi = ref.height() - 1; + if (zi<0) zi = 0; + if (nzi<0) nzi = 0; + if (zi>=ref.depth()) zi = ref.depth() - 1; + if (nzi>=ref.depth()) nzi = ref.depth() - 1; + I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); + I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0); + I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2); + I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); + I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0); + I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2); + I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1); + I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0); + I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2); + I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1); + I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0); + I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2); + _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0); + _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1); + } + return (float)pI->_linear_atXYZ(dx,dy,dz,c); + } + }; + + struct _functor4d_streamline_expr { + _cimg_math_parser *mp; + ~_functor4d_streamline_expr() { mp->end(); delete mp; } + _functor4d_streamline_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,"streamline",CImg::const_empty(),0); + } + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)(*mp)(x,y,z,c); + } + }; + + //! Return a shared-memory image referencing a range of pixels of the image instance. + /** + \param x0 X-coordinate of the starting pixel. + \param x1 X-coordinate of the ending pixel. + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(x0,y0,z0,c0), + end = (ulongT)offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of pixels of the image instance \const. + const CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(x0,y0,z0,c0), + end = (ulongT)offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance. + /** + \param y0 Y-coordinate of the starting row. + \param y1 Y-coordinate of the ending row. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(0,y0,z0,c0), + end = (ulongT)offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", + cimg_instance, + _width - 1,y0,y1,z0,c0); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance \const. + const CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(0,y0,z0,c0), + end = (ulongT)offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", + cimg_instance, + _width - 1,y0,y1,z0,c0); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); + } + + //! Return a shared-memory image referencing one row of the image instance. + /** + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared-memory image referencing one row of the image instance \const. + const CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared memory image referencing a range of slices of the image instance. + /** + \param z0 Z-coordinate of the starting slice. + \param z1 Z-coordinate of the ending slice. + \param c0 C-coordinate. + **/ + CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(0,0,z0,c0), + end = (ulongT)offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width - 1,_height - 1,z0,z1,c0); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); + } + + //! Return a shared memory image referencing a range of slices of the image instance \const. + const CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(0,0,z0,c0), + end = (ulongT)offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width - 1,_height - 1,z0,z1,c0); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); + } + + //! Return a shared-memory image referencing one slice of the image instance. + /** + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing one slice of the image instance \const. + const CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) const { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing a range of channels of the image instance. + /** + \param c0 C-coordinate of the starting channel. + \param c1 C-coordinate of the ending channel. + **/ + CImg get_shared_channels(const unsigned int c0, const unsigned int c1) { + const ulongT + beg = (ulongT)offset(0,0,0,c0), + end = (ulongT)offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width - 1,_height - 1,_depth - 1,c0,c1); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); + } + + //! Return a shared-memory image referencing a range of channels of the image instance \const. + const CImg get_shared_channels(const unsigned int c0, const unsigned int c1) const { + const ulongT + beg = (ulongT)offset(0,0,0,c0), + end = (ulongT)offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width - 1,_height - 1,_depth - 1,c0,c1); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); + } + + //! Return a shared-memory image referencing one channel of the image instance. + /** + \param c0 C-coordinate. + **/ + CImg get_shared_channel(const unsigned int c0) { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory image referencing one channel of the image instance \const. + const CImg get_shared_channel(const unsigned int c0) const { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory version of the image instance. + CImg get_shared() { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Return a shared-memory version of the image instance \const. + const CImg get_shared() const { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Split image into a list along specified axis. + /** + \param axis Splitting axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param nb Number of split parts. + \note + - If \c nb==0, instance image is split into blocs of egal values along the specified axis. + - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide. + - If \c nb>0, instance image is split into \c nb blocs. + **/ + CImgList get_split(const char axis, const int nb=-1) const { + CImgList res; + if (is_empty()) return res; + const char _axis = cimg::lowercase(axis); + + if (nb<0) { // Split by bloc size + const unsigned int dp = (unsigned int)(nb?-nb:1); + switch (_axis) { + case 'x': { + if (_width>dp) { + res.assign(_width/dp + (_width%dp?1:0),1,1); + const unsigned int pe = _width - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _height*_depth*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'y': { + if (_height>dp) { + res.assign(_height/dp + (_height%dp?1:0),1,1); + const unsigned int pe = _height - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_depth*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'z': { + if (_depth>dp) { + res.assign(_depth/dp + (_depth%dp?1:0),1,1); + const unsigned int pe = _depth - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_height*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'c' : { + if (_spectrum>dp) { + res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1); + const unsigned int pe = _spectrum - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_height*_depth>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]); + get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } + } + } else if (nb>0) { // Split by number of (non-homogeneous) blocs + const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0; + if ((unsigned int)nb>siz) + throw CImgArgumentException(_cimg_instance + "get_split(): Instance cannot be split along %c-axis into %u blocs.", + cimg_instance, + axis,nb); + if (nb==1) res.assign(*this); + else { + int err = (int)siz; + unsigned int _p = 0; + switch (_axis) { + case 'x' : { + cimg_forX(*this,p) if ((err-=nb)<=0) { + get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'y' : { + cimg_forY(*this,p) if ((err-=nb)<=0) { + get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'z' : { + cimg_forZ(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'c' : { + cimg_forC(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } + } + } + } else { // Split by egal values according to specified axis + T current = *_data; + switch (_axis) { + case 'x' : { + int i0 = 0; + cimg_forX(*this,i) + if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); } + get_columns(i0,width() - 1).move_to(res); + } break; + case 'y' : { + int i0 = 0; + cimg_forY(*this,i) + if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); } + get_rows(i0,height() - 1).move_to(res); + } break; + case 'z' : { + int i0 = 0; + cimg_forZ(*this,i) + if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); } + get_slices(i0,depth() - 1).move_to(res); + } break; + case 'c' : { + int i0 = 0; + cimg_forC(*this,i) + if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); } + get_channels(i0,spectrum() - 1).move_to(res); + } break; + default : { + longT i0 = 0; + cimg_foroff(*this,i) + if ((*this)[i]!=current) { + CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = (longT)i; current = (*this)[i]; + } + CImg(_data + i0,1,(unsigned int)(size() - i0)).move_to(res); + } + } + } + return res; + } + + //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis. + /** + \param values Splitting value sequence. + \param axis Axis along which the splitting is performed. Can be '0' to ignore axis. + \param keep_values Tells if the splitting sequence must be kept in the split blocs. + **/ + template + CImgList get_split(const CImg& values, const char axis=0, const bool keep_values=true) const { + typedef _cimg_Tt Tt; + + CImgList res; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + if (!vsiz) return CImgList(*this); + if (vsiz==1) { // Split according to a single value + const T value = (T)*values; + switch (_axis) { + case 'x' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_width && (*this)(i)==value) ++i; + if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; } + while (i<_width && (*this)(i)!=value) ++i; + if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; } + } while (i<_width); + } break; + case 'y' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_height && (*this)(0,i)==value) ++i; + if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; } + while (i<_height && (*this)(0,i)!=value) ++i; + if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; } + } while (i<_height); + } break; + case 'z' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_depth && (*this)(0,0,i)==value) ++i; + if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; } + while (i<_depth && (*this)(0,0,i)!=value) ++i; + if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; } + } while (i<_depth); + } break; + case 'c' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_spectrum && (*this)(0,0,0,i)==value) ++i; + if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; } + while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i; + if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; } + } while (i<_spectrum); + } break; + default : { + const ulongT siz = size(); + ulongT i0 = 0, i = 0; + do { + while (ii0) { + if (keep_values) CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = i; + } + while (ii0) { CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } + } while (i=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_columns(i0,i1 - 1).move_to(res); + if (keep_values) get_columns(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_width); + if (i0<_width) get_columns(i0,width() - 1).move_to(res); + } break; + case 'y' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_rows(i0,i1 - 1).move_to(res); + if (keep_values) get_rows(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_height); + if (i0<_height) get_rows(i0,height() - 1).move_to(res); + } break; + case 'z' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_slices(i0,i1 - 1).move_to(res); + if (keep_values) get_slices(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_depth); + if (i0<_depth) get_slices(i0,depth() - 1).move_to(res); + } break; + case 'c' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,0,0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_channels(i0,i1 - 1).move_to(res); + if (keep_values) get_channels(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_spectrum); + if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res); + } break; + default : { + const ulongT siz = size(); + ulongT i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)[i]==(Tt)*values) { + i1 = i; j = 0; + while (i=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) CImg(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res); + if (keep_values) CImg(_data + i1,1,(unsigned int)(i - i1)).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i(_data + i0,1,(unsigned int)(siz - i0)).move_to(res); + } break; + } + } + return res; + } + + //! Append two images along specified axis. + /** + \param img Image to append with instance image. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Append alignment in \c [0,1]. + **/ + template + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,true).insert(img).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \specialization. + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,img,true).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \const. + template + CImg<_cimg_Tt> get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align); + } + + //! Append two images along specified axis \specialization. + CImg get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList(*this,img,true).get_append(axis,align); + } + + //@} + //--------------------------------------- + // + //! \name Filtering / Transforms + //@{ + //--------------------------------------- + + //! Correlate image by a kernel. + /** + \param kernel = the correlation kernel. + \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param is_normalized = enable local normalization. + \param channel mode Channel processing mode. Can be { 0=sum inputs | 1=one-for-one | 2=expand } + \param xcenter X-coordinate of the kernel center (~0U means 'centered'). + \param xstart Starting X-coordinate of the instance image. + \param xend Ending X-coordinate of the instance image. + \param xstride Stride along the X-axis. + \param xdilation Dilation along the X-axis. + \param ycenter Y-coordinate of the kernel center (~0U means 'centered'). + \param ystart Starting Y-coordinate of the instance image. + \param yend Ending Y-coordinate of the instance image. + \param ystride Stride along the Y-axis. + \param ydilation Dilation along the Y-axis. + \param zcenter Z-coordinate of the kernel center (~0U means 'centered'). + \param zstart Starting Z-coordinate of the instance image. + \param zend Ending Z-coordinate of the instance image. + \param zstride Stride along the Z-axis. + \param zdilation Dilation along the Z-axis. + \note + - The correlation of the image instance \p *this by the kernel \p kernel is defined to be: + res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j - + c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k). + **/ + template + CImg& correlate(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) { + if (is_empty() || !kernel) return *this; + return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + } + + template + CImg<_cimg_Ttfloat> get_correlate(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, + const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, + const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + } + + //! Correlate image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> _correlate(const CImg& kernel, const unsigned int boundary_conditions, + const bool is_normalized, const unsigned int channel_mode, + const unsigned int xcenter, const unsigned int ycenter, const unsigned int zcenter, + const unsigned int xstart, const unsigned int ystart, const unsigned zstart, + const unsigned int xend, const unsigned int yend, const unsigned int zend, + const float xstride, const float ystride, const float zstride, + const float xdilation, const float ydilation, const float zdilation, + const bool is_convolve) const { + if (is_empty() || !kernel) return *this; + typedef _cimg_Ttfloat Ttfloat; + CImg res; + _cimg_abort_init_openmp; + cimg_abort_init; + + if (xstart>xend || ystart>yend || zstart>zend) + throw CImgArgumentException(_cimg_instance + "%s(): Invalid xyz-start/end arguments (start = (%u,%u,%u), end = (%u,%u,%u)).", + cimg_instance, + is_convolve?"convolve":"correlate", + xstart,ystart,zstart,xend,yend,zend); + if (xstride<=0 || ystride<=0 || zstride<=0) + throw CImgArgumentException(_cimg_instance + "%s(): Invalid stride arguments (%g,%g,%g).", + cimg_instance, + is_convolve?"convolve":"correlate", + xstride,ystride,zstride); + const int + _xstart = (int)std::min(xstart,_width - 1), + _ystart = (int)std::min(ystart,_height - 1), + _zstart = (int)std::min(zstart,_depth - 1), + _xend = (int)std::min(xend,_width - 1), + _yend = (int)std::min(yend,_height - 1), + _zend = (int)std::min(zend,_depth - 1), + nwidth = 1 + (int)std::floor((_xend - _xstart)/xstride), + nheight = 1 + (int)std::floor((_yend - _ystart)/ystride), + ndepth = 1 + (int)std::floor((_zend + _zstart)/zstride), + _xstride = (int)cimg::round(xstride), + _ystride = (int)cimg::round(ystride), + _zstride = (int)cimg::round(zstride); + + const ulongT + res_whd = (ulongT)nwidth*nheight*ndepth, + res_size = res_whd*res._spectrum; + const bool + is_inner_parallel = res_whd>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + + int + _xcenter = xcenter==~0U?kernel.width()/2 - 1 + (kernel.width()%2):(int)std::min(xcenter,kernel._width - 1), + _ycenter = ycenter==~0U?kernel.height()/2 - 1 + (kernel.height()%2):(int)std::min(ycenter,kernel._height - 1), + _zcenter = zcenter==~0U?kernel.depth()/2 - 1 + (kernel.depth()%2):(int)std::min(zcenter,kernel._depth - 1), + _xdilation = (int)cimg::round(xdilation), + _ydilation = (int)cimg::round(ydilation), + _zdilation = (int)cimg::round(zdilation); + + const bool is_int_stride_dilation = + xstride==_xstride && ystride==_ystride && zstride==_zstride && + xdilation==_xdilation && ydilation==_ydilation && zdilation==_zdilation; + + CImg _kernel; + if (is_convolve) { // If convolution, go back to correlation + _kernel = CImg(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). + get_mirror('x').resize(kernel,-1); + _xcenter = kernel.width() - 1 - _xcenter; + _ycenter = kernel.height() - 1 - _ycenter; + _zcenter = kernel.depth() - 1 - _zcenter; + } else _kernel = kernel.get_shared(); + + if (_kernel._width==_kernel._height && _kernel._width>1 && _kernel._height>1 && + ((_kernel._depth==1 && _kernel._width<=5) || (_kernel._depth==_kernel._width && _kernel._width<=3)) && + boundary_conditions<=1 && channel_mode && + _xcenter==_kernel.width()/2 - 1 + (_kernel.width()%2) && + _ycenter==_kernel.height()/2 - 1 + (_kernel.height()%2) && + _zcenter==_kernel.depth()/2 - 1 + (_kernel.depth()%2) && + is_int_stride_dilation && _xdilation>=0 && _ydilation>=0 && _zdilation>=0) { + + // Optimized versions for centered 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernels. + const int dw = 1 - (_kernel.width()%2), dh = 1 - (_kernel.height()%2), dd = 1 - (_kernel.depth()%2); + if (dw || dh || dd) // Force kernel size to be odd + _kernel.get_resize(_kernel.width() + dw,_kernel.height() + dh,_kernel.depth() + dd,_kernel.spectrum(), + 0,0,1,1,1).move_to(_kernel.assign()); + + if (!boundary_conditions) { // Dirichlet -> Add a 1px zero border, then use _correlate() with Neumann + const int + dx = _kernel._width==1?0:1, + dy = _kernel._height==1?0:1, + dz = _kernel._depth==1?0:1; + return get_crop(-dx,-dy,-dz,width() - 1 + dx,height() - 1 + dy,depth() - 1 + dz). + _correlate(_kernel,true,is_normalized,channel_mode,_xcenter,_ycenter,_zcenter, + _xstart + dx,_ystart + dy,_zstart + dz,_xend + dx,_yend + dy,_zend + dz, + xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + + } else { // Neumann boundaries + res.assign(nwidth,nheight,ndepth,std::max(_spectrum,_kernel._spectrum)); + + switch (_kernel._depth) { + case 3 : { // 3x3x3 centered kernel + cimg_forC(res,c) { + cimg_abort_test; + const CImg I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + const int w1 = I.width() - 1, h1 = I.height() - 1, d1 = I.depth() - 1; + CImg _res = res.get_shared_channel(c); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,Z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, z = _zstart + _zstride*Z, + px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + const int w1 = I.width() - 1, h1 = I.height() - 1; + CImg _res = res.get_shared_channel(c); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, + px = x - _xdilation>0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, + nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, + ny = y + _ydilation0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, + nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, + ny = y + _ydilation I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg _res = res.get_shared_channel(c); + const int w1 = I.width() - 1, h1 = I.height() - 1; + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, + px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation res0 = res.get_shared_channel(0); + for (int c = 1; c K = _kernel.get_shared_channel(kc%_kernel._spectrum); + int w2 = 0, h2 = 0, d2 = 0; + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } + if (boundary_conditions>=3) { w2 = 2*width(); h2 = 2*height(); d2 = 2*depth(); } + res.fill(0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZC(res,x,y,z,c) { + Ttfloat _val, val = 0, N = 0; + + if (is_int_stride_dilation) + cimg_forXYZ(_kernel,p,q,r) { + const int + ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), + iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), + iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); + switch (boundary_conditions) { + case 0 : _val = atXYZ(ix,iy,iz,c,0); break; // Dirichlet + case 1 : _val = _atXYZ(ix,iy,iz,c); break; // Neumann + case 2 : _val = (*this)(cimg::mod(ix,width()),cimg::mod(iy,height()), // Periodic + cimg::mod(iz,depth()),c); break; + default : { // Mirror + const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); + _val = (*this)(mx I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(channel_mode==1?c%_kernel._spectrum:c/_spectrum); + int w2 = 0, h2 = 0, d2 = 0; + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } + if (boundary_conditions>=3) { w2 = 2*I.width(); h2 = 2*I.height(); d2 = 2*I.depth(); } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZ(res,x,y,z) { + Ttfloat _val, val = 0, N = 0; + + if (is_int_stride_dilation) + cimg_forXYZ(_kernel,p,q,r) { + const int + ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), + iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), + iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); + switch (boundary_conditions) { + case 0 : _val = I.atXYZ(ix,iy,iz,0,0); break; // Dirichlet + case 1 : _val = I._atXYZ(ix,iy,iz); break; // Neumann + case 2 : _val = I(cimg::mod(ix,I.width()),cimg::mod(iy,I.height()), // Periodic + cimg::mod(iz,I.depth())); break; + default : { // Mirror + const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); + _val = I(mx + CImg& convolve(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) { + if (is_empty() || !kernel) return *this; + return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + } + + //! Convolve image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> get_convolve(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, + const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, + const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation,true); + } + + //! Cumulate image values, optionally along specified axis. + /** + \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account. + **/ + CImg& cumulate(const char axis=0) { + switch (cimg::lowercase(axis)) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + Tlong cumul = (Tlong)0; + cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; } + } + break; + case 'y' : { + const ulongT w = (ulongT)_width; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && + _width*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) { + T *ptrd = data(x,0,z,c); + Tlong cumul = (Tlong)0; + cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; } + } + } break; + case 'z' : { + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && + _width*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) { + T *ptrd = data(x,y,0,c); + Tlong cumul = (Tlong)0; + cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; } + } + } break; + case 'c' : { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16)) + cimg_forXYZ(*this,x,y,z) { + T *ptrd = data(x,y,z,0); + Tlong cumul = (Tlong)0; + cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; } + } + } break; + default : { // Global cumulation + Tlong cumul = (Tlong)0; + cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; } + } + } + return *this; + } + + //! Cumulate image values, optionally along specified axis \newinstance. + CImg get_cumulate(const char axis=0) const { + return CImg(*this,false).cumulate(axis); + } + + //! Cumulate image values, along specified axes. + /** + \param axes Cumulation axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" + **/ + CImg& cumulate(const char *const axes) { + for (const char *s = axes; *s; ++s) cumulate(*s); + return *this; + } + + //! Cumulate image values, along specified axes \newinstance. + CImg get_cumulate(const char *const axes) const { + return CImg(*this,false).cumulate(axes); + } + + //! Erode image by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& erode(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) { + if (is_empty() || !kernel) return *this; + return get_erode(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Erode image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_erode(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) const { + if (is_empty() || !kernel) return *this; + if (!is_real && kernel==0) return CImg(width(),height(),depth(),spectrum(),0); + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + const int + mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, + mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1, + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_openmp; + cimg_abort_init; + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_openmp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real erosion + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); + if (cval::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + if (cval& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis + const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forYZC(*this,y,z,c) { + T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _height>1) { // Along Y-axis + const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXZC(*this,x,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _depth>1) { // Along Z-axis + const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXYC(*this,x,y,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).erode(sx,sy,sz); + } + + //! Erode the image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& erode(const unsigned int s) { + return erode(s,s,s); + } + + //! Erode the image by a square structuring element of specified size \newinstance. + CImg get_erode(const unsigned int s) const { + return (+*this).erode(s); + } + + //! Dilate image by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& dilate(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) { + if (is_empty() || !kernel) return *this; + return get_dilate(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Dilate image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_dilate(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) const { + if (is_empty() || !kernel || (!is_real && kernel==0)) return *this; + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + const int + mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2, + mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1, + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_openmp; + cimg_abort_init; + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_openmp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real dilation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } _cimg_abort_catch_openmp2 + if (boundary_conditions) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } else { // Binary dilation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } _cimg_abort_catch_openmp2 + if (boundary_conditions) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } + } _cimg_abort_catch_openmp + cimg_abort_test; + return res; + } + + //! Dilate image by a rectangular structuring element of specified size. + /** + \param sx Width of the structuring element. + \param sy Height of the structuring element. + \param sz Depth of the structuring element. + **/ + CImg& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis + const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forYZC(*this,y,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + + if (sy>1 && _height>1) { // Along Y-axis + const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXZC(*this,x,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + + if (sz>1 && _depth>1) { // Along Z-axis + const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXYC(*this,x,y,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + return *this; + } + + //! Dilate image by a rectangular structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).dilate(sx,sy,sz); + } + + //! Dilate image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& dilate(const unsigned int s) { + return dilate(s,s,s); + } + + //! Dilate image by a square structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int s) const { + return (+*this).dilate(s); + } + + //! Compute watershed transform. + /** + \param priority Priority map. + \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity + in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case. + \note Non-zero values of the instance instance are propagated to zero-valued ones according to + specified the priority map. + **/ + template + CImg& watershed(const CImg& priority, const bool is_high_connectivity=false) { +#define _cimg_watershed_init(cond,X,Y,Z) \ + if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds) + +#define _cimg_watershed_propagate(cond,X,Y,Z) \ + if (cond) { \ + if ((*this)(X,Y,Z)) { \ + ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \ + d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \ + if (d labels(_width,_height,_depth,1,0), seeds(64,3); + CImg::type> Q; + unsigned int sizeQ = 0; + int px, nx, py, ny, pz, nz; + bool is_px, is_nx, is_py, is_ny, is_pz, is_nz; + const bool is_3d = _depth>1; + + // Find seed points and insert them in priority queue. + unsigned int nb_seeds = 0; + const T *ptrs = _data; + cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version + if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0); + seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z; + px = x - 1; nx = x + 1; + py = y - 1; ny = y + 1; + pz = z - 1; nz = z + 1; + is_px = px>=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz::inf(); + T nlabel = (T)0; + _cimg_watershed_propagate(is_px,px,y,z); + _cimg_watershed_propagate(is_nx,nx,y,z); + _cimg_watershed_propagate(is_py,x,py,z); + _cimg_watershed_propagate(is_ny,x,ny,z); + if (is_3d) { + _cimg_watershed_propagate(is_pz,x,y,pz); + _cimg_watershed_propagate(is_nz,x,y,nz); + } + if (is_high_connectivity) { + _cimg_watershed_propagate(is_px && is_py,px,py,z); + _cimg_watershed_propagate(is_nx && is_py,nx,py,z); + _cimg_watershed_propagate(is_px && is_ny,px,ny,z); + _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z); + if (is_3d) { + _cimg_watershed_propagate(is_px && is_pz,px,y,pz); + _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz); + _cimg_watershed_propagate(is_px && is_nz,px,y,nz); + _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz); + _cimg_watershed_propagate(is_py && is_pz,x,py,pz); + _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz); + _cimg_watershed_propagate(is_py && is_nz,x,py,nz); + _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz); + _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz); + _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz); + _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz); + _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz); + _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz); + _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz); + _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz); + _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz); + } + } + (*this)(x,y,z) = nlabel; + labels(x,y,z) = ++nmin; + } + return *this; + } + + //! Compute watershed transform \newinstance. + template + CImg get_watershed(const CImg& priority, const bool is_high_connectivity=false) const { + return (+*this).watershed(priority,is_high_connectivity); + } + + // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. + template + bool _priority_queue_insert(CImg& is_queued, unsigned int& siz, const tv value, + const unsigned int x, const unsigned int y, const unsigned int z, + const unsigned int n=1) { + if (is_queued(x,y,z)) return false; + is_queued(x,y,z) = (tq)n; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz - 1,0) = (T)value; + (*this)(siz - 1,1) = (T)x; + (*this)(siz - 1,2) = (T)y; + (*this)(siz - 1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); + cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); + cimg::swap((*this)(pos,3),(*this)(par,3)); + } + return true; + } + + CImg& _priority_queue_remove(unsigned int& siz) { + (*this)(0,0) = (*this)(--siz,0); + (*this)(0,1) = (*this)(siz,1); + (*this)(0,2) = (*this)(siz,2); + (*this)(0,3) = (*this)(siz,3); + const float value = (*this)(0,0); + unsigned int pos = 0, swap = 0; + do { + const unsigned int left = 2*pos + 1, right = left + 1; + if (right(*this)(right,0)?left:right; + else if (left{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + **/ + CImg& deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) { +#define _cimg_deriche_apply \ + CImg Y(N); \ + double *ptrY = Y._data, yb = 0, yp = 0; \ + T xp = (T)0; \ + if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \ + for (int m = 0; m=0; --n) { \ + const T xc = *(ptrX-=off); \ + const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \ + xa = xn; xn = xc; ya = yn; yn = yc; \ + *ptrX = (T)(*(--ptrY)+yc); \ + } + const char naxis = cimg::lowercase(axis); + const double nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width: + naxis=='y'?_height: + naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.1f && !order)) return *this; + const double + nnsigma = nsigma<0.1f?0.1f:nsigma, + alpha = 1.695f/nnsigma, + ema = std::exp(-alpha), + ema2 = std::exp(-2*alpha), + b1 = -2*ema, + b2 = ema2; + double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; + switch (order) { + case 0 : { + const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2); + a0 = k; + a1 = k*(alpha - 1)*ema; + a2 = k*(alpha + 1)*ema; + a3 = -k*ema2; + } break; + case 1 : { + const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema); + a0 = a3 = 0; + a1 = k*ema; + a2 = -a1; + } break; + case 2 : { + const double + ea = std::exp(-alpha), + k = -(ema2 - 1)/(2*alpha*ema), + kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea)); + a0 = kn; + a1 = -kn*(1 + k*alpha)*ema; + a2 = kn*(1 - k*alpha)*ema; + a3 = -kn*ema2; + } break; + default : + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified filter order %u " + "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", + cimg_instance, + order); + } + coefp = (a0 + a1)/(1 + b1 + b2); + coefn = (a2 + a3)/(1 + b1 + b2); + switch (naxis) { + case 'x' : { + const int N = width(); + const ulongT off = 1U; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; } + } break; + case 'y' : { + const int N = height(); + const ulongT off = (ulongT)_width; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; } + } break; + case 'z' : { + const int N = depth(); + const ulongT off = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; } + } break; + default : { + const int N = spectrum(); + const ulongT off = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; } + } + } + return *this; + } + + //! Apply recursive Deriche filter \newinstance. + CImg get_deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).deriche(sigma,order,axis,boundary_conditions); + } + + // [internal] Apply a recursive filter (used by CImg::vanvliet()). + /* + \param ptr the pointer of the data + \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3]. + \param N size of the data + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005). + */ + static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off, + const unsigned int order, const bool boundary_conditions) { + double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] + const double + sumsq = filter[0], sum = sumsq * sumsq, + a1 = filter[1], a2 = filter[2], a3 = filter[3], + scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) ); + double M[9]; // Triggs matrix + M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2); + M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1); + M[2] = scaleM * a3 * (a1 + a3 * a2); + M[3] = scaleM * (a1 + a3 * a2); + M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1); + M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.); + M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2); + M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3); + M[8] = scaleM * a3 * (a1 + a3 * a2); + switch (order) { + case 0 : { + const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0); + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0); + } else { + // Apply Triggs boundary conditions + const double + uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3), + unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) val[k] = val[k - 1]; + } + if (!pass) data -= off; + } + } break; + case 1 : { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + } else { data-=off;} + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + case 2: { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + case 3: { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + } + } + + //! Van Vliet recursive Gaussian filter. + /** + \param sigma standard deviation of the Gaussian filter + \param order the order of the filter 0,1,2,3 + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note dirichlet boundary condition has a strange behavior + + I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering. + IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002. + + (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995) + + Boundary conditions (only for order 0) using Triggs matrix, from + B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet + recursive filtering. IEEE Trans. Signal Processing, + vol. 54, pp. 2365-2367, 2006. + **/ + CImg& vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) { + if (is_empty()) return *this; + if (!cimg::type::is_float()) + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this); + const char naxis = cimg::lowercase(axis); + const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.5f && !order)) return *this; + const double + nnsigma = nsigma<0.5f?0.5f:nsigma, + m0 = 1.16680, m1 = 1.10783, m2 = 1.40586, + m1sq = m1 * m1, m2sq = m2 * m2, + q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)), + qsq = q * q, + scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq), + b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale, + b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale, + b3 = -qsq * q / scale, + B = ( m0 * (m1sq + m2sq) ) / scale; + double filter[4]; + filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3; + switch (naxis) { + case 'x' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) + _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); + } break; + case 'y' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) + _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions); + } break; + case 'z' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) + _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height, + order,boundary_conditions); + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) + _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth, + order,boundary_conditions); + } + } + return *this; + } + + //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. + CImg get_vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions); + } + + //! Blur image. + /** + \param sigma_x Standard deviation of the blur, along the X-axis. + \param sigma_y Standard deviation of the blur, along the Y-axis. + \param sigma_z Standard deviation of the blur, along the Z-axis. + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel. + \note + - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian). + - This is a recursive algorithm, not depending on the values of the standard deviations. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=true) { + if (is_empty()) return *this; + if (is_gaussian) { + if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions); + if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions); + if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions); + } else { + if (_width>1) deriche(sigma_x,0,'x',boundary_conditions); + if (_height>1) deriche(sigma_y,0,'y',boundary_conditions); + if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions); + } + return *this; + } + + //! Blur image \newinstance. + CImg get_blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=true) const { + return CImg(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically. + /** + \param sigma Standard deviation of the blur. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) { + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically \newinstance. + CImg get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) const { + return CImg(*this,false).blur(sigma,boundary_conditions,is_gaussian); + } + + //! Blur image anisotropically, directed by a field of diffusion tensors. + /** + \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing. + \param amplitude Amplitude of the smoothing. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. + Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=1) { + + // Check arguments and init variables + if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6)) + throw CImgArgumentException(_cimg_instance + "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).", + cimg_instance, + G._width,G._height,G._depth,G._spectrum,G._data); + if (is_empty() || dl<0) return *this; + const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100; + unsigned int iamplitude = cimg::round(namplitude); + const bool is_3d = (G._spectrum==6); + T val_min, val_max = max_min(val_min); + _cimg_abort_init_openmp; + cimg_abort_init; + + if (da<=0) { // Iterated oriented Laplacians + CImg velocity(_width,_height,_depth,_spectrum); + for (unsigned int iteration = 0; iterationveloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + else // 2D version + cimg_forZC(*this,z,c) { + cimg_abort_test; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + if (veloc_max>0) *this+=(velocity*=dl/veloc_max); + } + } else { // LIC-based smoothing + const ulongT whd = (ulongT)_width*_height*_depth; + const float sqrt2amplitude = (float)std::sqrt(2*namplitude); + const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1; + CImg res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0); + int N = 0; + if (is_3d) { // 3D version + for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) { + const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), + da2 = datmp<1?360.f:datmp; + for (float theta = 0; theta<360; (theta+=da2),++N) { + const float + thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)*std::cos(phir)), + vy = (float)(std::sin(thetar)*std::cos(phir)), + vz = (float)std::sin(phir); + const t + *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2), + *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3); + cimg_forXYZ(G,xg,yg,zg) { + const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++); + const float + u = (float)(a*vx + b*vy + c*vz), + v = (float)(b*vx + d*vy + e*vz), + w = (float)(c*vx + e*vy + f*vz), + n = 1e-5f + cimg::hypot(u,v,w), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)(w*dln); + *(pd3++) = (Tfloat)n; + } + + cimg_abort_test; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2) + firstprivate(val)) + cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + cimg_forX(*this,x) { + val.fill(0); + const float + n = (float)W(x,y,z,3), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y, + Z = (float)z; + switch (interpolation_type) { + case 0 : { // Nearest neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)(X + 0.5f), + cy = (int)(Y + 0.5f), + cz = (int)(Z + 0.5f); + const float + u = (float)W(cx,cy,cz,0), + v = (float)W(cx,cy,cz,1), + w = (float)W(cx,cy,cz,2); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const float + u = (float)(W._linear_atXYZ(X,Y,Z,0)), + v = (float)(W._linear_atXYZ(X,Y,Z,1)), + w = (float)(W._linear_atXYZ(X,Y,Z,2)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + default : { // 2nd order Runge Kutta + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)), + v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)), + w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)), + u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)), + v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)), + w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + } + Tfloat *ptrd = res.data(x,y,z); + if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } + else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; } + } + } _cimg_abort_catch_openmp2 + } + } + } else { // 2D LIC algorithm + for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) { + const float thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); + const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2); + cimg_forXY(G,xg,yg) { + const t a = *(pa++), b = *(pb++), c = *(pc++); + const float + u = (float)(a*vx + b*vy), + v = (float)(b*vx + c*vy), + n = std::max(1e-5f,cimg::hypot(u,v)), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)n; + } + + cimg_abort_test; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2) + firstprivate(val)) + cimg_forY(*this,y) _cimg_abort_try_openmp2 { + cimg_abort_test2; + cimg_forX(*this,x) { + val.fill(0); + const float + n = (float)W(x,y,0,2), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y; + switch (interpolation_type) { + case 0 : { // Nearest-neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)(X + 0.5f), + cy = (int)(Y + 0.5f); + const float + u = (float)W(cx,cy,0,0), + v = (float)W(cx,cy,0,1); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u = (float)(W._linear_atXY(X,Y,0,0)), + v = (float)(W._linear_atXY(X,Y,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + default : { // 2nd-order Runge-kutta interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), + v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)), + u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)), + v = (float)(W._linear_atXY(X + u0,Y + v0,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } + } + Tfloat *ptrd = res.data(x,y); + if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } + else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; } + } + } _cimg_abort_catch_openmp2 + } + } + const Tfloat *ptrs = res._data; + cimg_for(*this,ptrd,T) { + const Tfloat _val = *(ptrs++)/N; + *ptrd = _valval_max?val_max:(T)_val); + } + } + cimg_abort_test; + return *this; + } + + //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance. + template + CImg get_blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return CImg(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way. + /** + \param amplitude Amplitude of the smoothing. + \param sharpness Sharpness. + \param anisotropy Anisotropy. + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. + Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) { + const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100; + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3), + amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way \newinstance. + CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30, const float gauss_prec=2, + const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return CImg(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec, + interpolation_type,is_fast_approx); + } + + //! Blur image, with the joint bilateral filter. + /** + \param guide Image used to model the smoothing weights. + \param sigma_x Amount of blur along the X-axis. + \param sigma_y Amount of blur along the Y-axis. + \param sigma_z Amount of blur along the Z-axis. + \param sigma_r Amount of blur along the value axis. + \param sampling_x Amount of downsampling along the X-axis used for the approximation. + Defaults (0) to sigma_x. + \param sampling_y Amount of downsampling along the Y-axis used for the approximation. + Defaults (0) to sigma_y. + \param sampling_z Amount of downsampling along the Z-axis used for the approximation. + Defaults (0) to sigma_z. + \param sampling_r Amount of downsampling along the value axis used for the approximation. + Defaults (0) to sigma_r. + \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 + (extended for 3D volumetric images). + It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m + **/ + template + CImg& blur_bilateral(const CImg& guide, + const float sigma_x, const float sigma_y, + const float sigma_z, const float sigma_r, + const float sampling_x, const float sampling_y, + const float sampling_z, const float sampling_r) { + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this; + T edge_min, edge_max = guide.max_min(edge_min); + if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z); + const float + edge_delta = (float)(edge_max - edge_min), + _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100, + _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100, + _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100, + _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100, + _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f), + _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f), + _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f), + _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256), + derived_sigma_x = _sigma_x / _sampling_x, + derived_sigma_y = _sigma_y / _sampling_y, + derived_sigma_z = _sigma_z / _sampling_z, + derived_sigma_r = _sigma_r / _sampling_r; + const int + padding_x = (int)(2*derived_sigma_x) + 1, + padding_y = (int)(2*derived_sigma_y) + 1, + padding_z = (int)(2*derived_sigma_z) + 1, + padding_r = (int)(2*derived_sigma_r) + 1; + const unsigned int + bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x), + by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y), + bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z), + br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r); + if (bx>0 || by>0 || bz>0 || br>0) { + const bool is_3d = (_depth>1); + if (is_3d) { // 3D version of the algorithm + CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); + cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); + bgrid.fill(0); bgridw.fill(0); + cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,c); + const float edge = (float)_guide(x,y,z); + const int + X = (int)cimg::round(x/_sampling_x) + padding_x, + Y = (int)cimg::round(y/_sampling_y) + padding_y, + Z = (int)cimg::round(z/_sampling_z) + padding_z, + R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; + bgrid(X,Y,Z,R)+=(float)val; + bgridw(X,Y,Z,R)+=1; + } + bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); + bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096)) + cimg_forXYZ(*this,x,y,z) { + const float edge = (float)_guide(x,y,z); + const float + X = x/_sampling_x + padding_x, + Y = y/_sampling_y + padding_y, + Z = z/_sampling_z + padding_z, + R = (edge - edge_min)/_sampling_r + padding_r; + const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R); + (*this)(x,y,z,c) = (T)(bval0/bval1); + } + } + } else { // 2D version of the algorithm + CImg bgrid(bx,by,br,2); + cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); + bgrid.fill(0); + cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,c); + const float edge = (float)_guide(x,y); + const int + X = (int)cimg::round(x/_sampling_x) + padding_x, + Y = (int)cimg::round(y/_sampling_y) + padding_y, + R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; + bgrid(X,Y,R,0)+=(float)val; + bgrid(X,Y,R,1)+=1; + } + bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096)) + cimg_forXY(*this,x,y) { + const float edge = (float)_guide(x,y); + const float + X = x/_sampling_x + padding_x, + Y = y/_sampling_y + padding_y, + R = (edge - edge_min)/_sampling_r + padding_r; + const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); + (*this)(x,y,c) = (T)(bval0/bval1); + } + } + } + } + return *this; + } + + //! Blur image, with the joint bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_x, const float sigma_y, + const float sigma_z, const float sigma_r, + const float sampling_x, const float sampling_y, + const float sampling_z, const float sampling_r) const { + return CImg(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r, + sampling_x,sampling_y,sampling_z,sampling_r); + } + + //! Blur image using the joint bilateral filter. + /** + \param guide Image used to model the smoothing weights. + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s. + \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r. + **/ + template + CImg& blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) { + const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; + return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r); + } + + //! Blur image using the bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) const { + return CImg(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r); + } + + // [internal] Apply a box filter (used by CImg::boxfilter() and CImg::blur_box()). + /* + \param ptr the pointer of the data + \param N size of the data + \param boxsize Size of the box filter (can be subpixel). + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + */ + static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off, + const int order, const bool boundary_conditions, + const unsigned int nb_iter) { + // Smooth. + if (boxsize>1 && nb_iter) { + const int w2 = (int)(boxsize - 1)/2; + const unsigned int winsize = 2*w2 + 1U; + const double frac = (boxsize - winsize)/2.; + CImg win(winsize); + for (unsigned int iter = 0; iter=N) return boundary_conditions?ptr[(N - 1)*off]:T(); + return ptr[x*off]; + } + + // Apply box filter of order 0,1,2. + /** + \param boxsize Size of the box window (can be subpixel) + \param order the order of the filter 0,1 or 2. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param nb_iter Number of filter iterations. + **/ + CImg& boxfilter(const float boxsize, const int order, const char axis='x', + const bool boundary_conditions=true, + const unsigned int nb_iter=1) { + if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this; + const char naxis = cimg::lowercase(axis); + const float nboxsize = boxsize>=0?boxsize:-boxsize* + (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + switch (naxis) { + case 'x' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) + _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter); + } break; + case 'y' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) + _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter); + } break; + case 'z' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) + _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter); + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) + _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth, + order,boundary_conditions,nb_iter); + } + } + return *this; + } + + // Apply box filter of order 0,1 or 2 \newinstance. + CImg get_boxfilter(const float boxsize, const int order, const char axis='x', + const bool boundary_conditions=true, + const unsigned int nb_iter=1) const { + return CImg(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter); + } + + //! Blur image with a box filter. + /** + \param boxsize_x Size of the box window, along the X-axis (can be subpixel). + \param boxsize_y Size of the box window, along the Y-axis (can be subpixel). + \param boxsize_z Size of the box window, along the Z-axis (can be subpixel). + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param nb_iter Number of filter iterations. + \note + - This is a recursive algorithm, not depending on the values of the box kernel size. + \see blur(). + **/ + CImg& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true, + const unsigned int nb_iter=1) { + if (is_empty()) return *this; + if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter); + if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter); + if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter); + return *this; + } + + //! Blur image with a box filter \newinstance. + CImg get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true) const { + return CImg(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions); + } + + //! Blur image with a box filter. + /** + \param boxsize Size of the box window (can be subpixel). + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \see deriche(), vanvliet(). + **/ + CImg& blur_box(const float boxsize, const bool boundary_conditions=true) { + const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100; + return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions); + } + + //! Blur image with a box filter \newinstance. + CImg get_blur_box(const float boxsize, const bool boundary_conditions=true) const { + return CImg(*this,false).blur_box(boxsize,boundary_conditions); + } + + //! Blur image, with the image guided filter. + /** + \param guide Image used to guide the smoothing process. + \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size. + \param regularization Regularization parameter. + If negative, it is expressed as a percentage of the guide value range. + \note This method implements the filtering algorithm described in: + He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence, + IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013 + **/ + template + CImg& blur_guided(const CImg& guide, const float radius, const float regularization) { + return get_blur_guided(guide,radius,regularization).move_to(*this); + } + + //! Blur image, with the image guided filter \newinstance. + template + CImg get_blur_guided(const CImg& guide, const float radius, const float regularization) const { + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || !radius) return *this; + const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100); + float _regularization = regularization; + if (regularization<0) { + T edge_min, edge_max = guide.max_min(edge_min); + if (edge_min==edge_max) return *this; + _regularization = -regularization*(edge_max - edge_min)/100; + } + _regularization = std::max(_regularization,0.01f); + const unsigned int psize = (unsigned int)(1 + 2*_radius); + CImg + mean_p = get_blur_box(psize,true), + mean_I = guide.get_blur_box(psize,true).resize(mean_p), + cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I), + var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(), + &a = cov_Ip.div(var_I+=_regularization), + &b = mean_p-=a.get_mul(mean_I); + a.blur_box(psize,true); + b.blur_box(psize,true); + return a.mul(guide)+=b; + } + + //! Blur image using patch-based space. + /** + \param guide Image used to model the smoothing weights. + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param patch_size Size of the patches. + \param lookup_size Size of the window to search similar patches. + \param smoothness Smoothness for the patch comparison. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_patch(const CImg& guide, + const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + if (is_empty() || !patch_size || !lookup_size) return *this; + return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this); + } + + //! Blur image using patch-based space \newinstance. + template + CImg get_blur_patch(const CImg& guide, + const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, + const bool is_fast_approx=true) const { + +#define _cimg_blur_patch3d_fast(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ + firstprivate(P,Q)) \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N##x##N(res,x,y,z); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + tfloat sum_weights = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \ + if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))3?0:1; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \ + } \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch3d(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ + firstprivate(P,Q)) \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N##x##N(res,x,y,z); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + tfloat sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \ + tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \ + tfloat distance2 = 0; \ + pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \ + alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch2d_fast(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ + firstprivate(P,Q)) \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N(res,x,y); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + tfloat sum_weights = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \ + if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))3?0:1; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \ + } \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch2d(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ + firstprivate(P,Q)) \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N(res,x,y); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + tfloat sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \ + tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \ + tfloat distance2 = 0; \ + pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \ + alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } _cimg_abort_catch_openmp } + + typedef _cimg_tfloat tfloat; + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || !patch_size || !lookup_size) return +*this; + Tfloat val_min, val_max = (Tfloat)max_min(val_min); + _cimg_abort_init_openmp; + cimg_abort_init; + + CImg res(_width,_height,_depth,_spectrum,0); + const CImg + __guide = guide?CImg(guide,guide.pixel_type()==cimg::type::string()): + CImg(*this,pixel_type()==cimg::type::string()), + _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared(); + CImg P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P); + + t guide_min = (t)0, guide_max = (t)0; + if (sigma_r<0) guide_max = guide.max_min(guide_min); + const float + guide_delta = (float)(guide_max - guide_min), + _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100, + _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100, + sigma_s2 = _sigma_s*_sigma_s, + sigma_r2 = _sigma_r*_sigma_r, + sigma_r3 = 3*_sigma_r, + Pnorm = P.size()*sigma_r2; + const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1; + const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size; + cimg::unused(N2,N3); + if (_depth>1) switch (patch_size) { // 3D + case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break; + default : { + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) + firstprivate(P,Q)) + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Fast + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + tfloat sum_weights = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) + if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))3?0:1; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); + } + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } _cimg_abort_catch_openmp + } else { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) + firstprivate(P,Q)) + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Exact + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + tfloat sum_weights = 0, weight_max = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { + (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P; + const tfloat + dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r, + distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), + weight = std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } _cimg_abort_catch_openmp + } + } + } else switch (patch_size) { // 2D + case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break; + case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break; + case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break; + case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break; + case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break; + case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break; + case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break; + default : { // Fast + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) { + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) + firstprivate(P,Q)) + cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Fast + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + tfloat sum_weights = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) + if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))3?0:1; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); + } + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } _cimg_abort_catch_openmp + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) + firstprivate(P,Q)) + cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Exact + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + tfloat sum_weights = 0, weight_max = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { + (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P; + const tfloat + dx = (tfloat)x - p, dy = (tfloat)y - q, + distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), + weight = std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } _cimg_abort_catch_openmp + } + } + } + return res.cut(val_min,val_max); + } + + //! Blur image using patch-based space \simplification. + CImg& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx); + } + + //! Blur image using patch-based space \simplification \newinstance. + CImg get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, + const bool is_fast_approx=true) const { + return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx); + } + + //! Blur image with the median filter. + /** + \param n Size of the median filter. + \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation. + **/ + CImg& blur_median(const unsigned int n, const float threshold=0) { + if (!n) return *this; + return get_blur_median(n,threshold).move_to(*this); + } + + //! Blur image with the median filter \newinstance. + CImg get_blur_median(const unsigned int n, const float threshold=0) const { + if (is_empty() || n<=1) return +*this; + CImg res(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg::unused(ptrd); + const int hr = (int)n/2, hl = n - hr - 1; + if (res._depth!=1) { // 3D + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(*this,x,y,z,c) { // With threshold + const int + x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; + const Tfloat val0 = (Tfloat)(*this)(x,y,z,c); + CImg values(n*n*n); + unsigned int nb_values = 0; + T *_ptrd = values.data(); + cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r) + if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; } + res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(*this,x,y,z,c) { // Without threshold + const int + x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; + res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); + } + } else { + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { // With threshold + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + const Tfloat val0 = (Tfloat)(*this)(x,y,c); + CImg values(n*n); + unsigned int nb_values = 0; + T *_ptrd = values.data(); + cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) + if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; } + res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c); + } + else { + const int + w1 = width() - 1, h1 = height() - 1, + w2 = width() - 2, h2 = height() - 2, + w3 = width() - 3, h3 = height() - 3, + w4 = width() - 4, h4 = height() - 4; + switch (n) { // Without threshold + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(9); + cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]); + cimg_for_borderXY(*this,x,y,1) + res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c, + std::min(w1,x + 1),std::min(h1,y + 1),0,c).median(); + } + } break; + case 5 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(25); + cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4], + I[5],I[6],I[7],I[8],I[9], + I[10],I[11],I[12],I[13],I[14], + I[15],I[16],I[17],I[18],I[19], + I[20],I[21],I[22],I[23],I[24]); + cimg_for_borderXY(*this,x,y,2) + res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c, + std::min(w1,x + 2),std::min(h1,y + 2),0,c).median(); + } + } break; + case 7 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(49); + cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6], + I[7],I[8],I[9],I[10],I[11],I[12],I[13], + I[14],I[15],I[16],I[17],I[18],I[19],I[20], + I[21],I[22],I[23],I[24],I[25],I[26],I[27], + I[28],I[29],I[30],I[31],I[32],I[33],I[34], + I[35],I[36],I[37],I[38],I[39],I[40],I[41], + I[42],I[43],I[44],I[45],I[46],I[47],I[48]); + cimg_for_borderXY(*this,x,y,3) + res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c, + std::min(w1,x + 3),std::min(h1,y + 3),0,c).median(); + } + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); + } + } + } + } + } + return res; + } + + //! Sharpen image. + /** + \param amplitude Sharpening amplitude + \param sharpen_type Select sharpening method. Can be { false=inverse diffusion | true=shock filters }. + \param edge Edge threshold (shock filters only). + \param alpha Gradient smoothness (shock filters only). + \param sigma Tensor smoothness (shock filters only). + **/ + CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, + const float alpha=0, const float sigma=0) { + if (is_empty()) return *this; + T val_min, val_max = max_min(val_min); + const float nedge = edge/2; + CImg velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum); + + if (_depth>1) { // 3D + if (sharpen_type) { // Shock filters + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && + _height*_depth>=16)) + cimg_forYZ(G,y,z) { + Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1), + *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3); + CImg val, vec; + cimg_forX(G,x) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + if (val[2]<0) val[2] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = vec(0,2); + *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + u = G(x,y,z,0), + v = G(x,y,z,1), + w = G(x,y,z,2), + amp = G(x,y,z,3), + ixx = Incc + Ipcc - 2*Iccc, + ixy = (Innc + Ippc - Inpc - Ipnc)/4, + ixz = (Incn + Ipcp - Incp - Ipcn)/4, + iyy = Icnc + Icpc - 2*Iccc, + iyz = (Icnn + Icpp - Icnp - Icpn)/4, + izz = Iccn + Iccp - 2*Iccc, + ixf = Incc - Iccc, + ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, + iyb = Iccc - Icpc, + izf = Iccn - Iccc, + izb = Iccc - Iccp, + itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb), + veloc = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else // Inverse diffusion + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else { // 2D + if (sharpen_type) { // Shock filters + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && + _height>=(cimg_openmp_sizefactor)*16)) + cimg_forY(G,y) { + CImg val, vec; + Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2); + cimg_forX(G,x) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + u = G(x,y,0), + v = G(x,y,1), + amp = G(x,y,2), + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + ixf = Inc - Icc, + ixb = Icc - Ipc, + iyf = Icn - Icc, + iyb = Icc - Icp, + itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb), + veloc = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else // Inverse diffusion + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } + const Tfloat veloc_max = _veloc_max.max(); + if (veloc_max<=0) return *this; + return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this); + } + + //! Sharpen image \newinstance. + CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, + const float alpha=0, const float sigma=0) const { + return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); + } + + //! Return image gradient. + /** + \param axes Axes considered for the gradient computation, as a C-string (e.g "xy"). + \param scheme = Numerical scheme used for the gradient computation: + - -1 = Backward finite differences + - 0 = Centered finite differences (default) + - 1 = Forward finite differences + - 2 = Using Sobel kernels + - 3 = Using rotation invariant kernels + - 4 = Using Deriche recursive filter. + - 5 = Using Van Vliet recursive filter. + **/ + CImgList get_gradient(const char *const axes=0, const int scheme=0) const { + CImgList res; + char __axes[4] = { 0 }; + const char *_axes = axes?axes:__axes; + if (!axes) { + unsigned int k = 0; + if (_width>1) __axes[k++] = 'x'; + if (_height>1) __axes[k++] = 'y'; + if (_depth>1) __axes[k++] = 'z'; + } + + CImg grad; + while (*_axes) { + const char axis = cimg::lowercase(*(_axes++)); + if (axis!='x' && axis!='y' && axis!='z') + throw CImgArgumentException(_cimg_instance + "get_gradient(): Invalid specified axes '%s'.", + cimg_instance, + axes); + const longT off = axis=='x'?1:axis=='y'?_width:_width*_height; + if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) { + grad.assign(_width,_height,_depth,_spectrum,0).move_to(res); + continue; + } + + const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme; + switch (_scheme) { + case -1 : { // Backward finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z)) + grad[pos] = 0; + else + grad[pos] = (Tfloat)_data[pos] - _data[pos - off]; + } + grad.move_to(res); + } break; + case 1 : { // Forward finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1)) + grad[pos] = 0; + else + grad[pos] = (Tfloat)_data[pos + off] - _data[pos]; + } + grad.move_to(res); + } break; + case 2 : { // Sobel scheme + grad.assign(_width,_height,_depth,_spectrum); + if (axis=='x') // X-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn; + } + else // Y-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn; + } + grad.move_to(res); + } break; + case 3 : { // Rotation invariant scheme + const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1)); + grad.assign(_width,_height,_depth,_spectrum); + if (axis=='x') // X-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; + } + else // Y-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; + } + grad.move_to(res); + } break; + case 4 : // Deriche filter + get_deriche(0,1,axis).move_to(res); + break; + case 5 : // Van Vliet filter + get_vanvliet(0,1,axis).move_to(res); + break; + default : { // Central finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z)) + grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2; + else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1)) + grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2; + else + grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2; + } + grad.move_to(res); + } break; + } + } + return res; + } + + //! Return image hessian. + /** + \param axes Axes considered for the hessian computation, as a C-string (e.g "xy"). + **/ + CImgList get_hessian(const char *const axes=0) const { + CImgList res; + char __axes[12] = { 0 }; + const char *_axes = axes?axes:__axes; + if (!axes) { + unsigned int k = 0; + if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; } + if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; } + if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; } + if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; } + if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; } + if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; } + } + const unsigned int len = (unsigned int)std::strlen(_axes); + if (len%2) + throw CImgArgumentException(_cimg_instance + "get_hessian(): Invalid specified axes '%s'.", + cimg_instance, + axes); + CImg hess; + for (unsigned int k = 0; k=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4; + } + else if (axis1=='x' && axis2=='z') // Ixz + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4; + } + else // Iyz + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4; + } + hess.move_to(res); + } + return res; + } + + //! Compute image Laplacian. + CImg& laplacian() { + return get_laplacian().move_to(*this); + } + + //! Compute image Laplacian \newinstance. + CImg get_laplacian() const { + if (is_empty()) return CImg(); + CImg res(_width,_height,_depth,_spectrum); + if (_depth>1) { // 3D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; + } + } else if (_height>1) { // 2D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; + } + } else { // 1D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 && + _height*_depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc; + } + } + return res; + } + + //! Compute the structure tensor field of an image. + /** + \param is_fwbw_scheme scheme. Can be { false=centered | true=forward-backward } + **/ + CImg& structure_tensors(const bool is_fwbw_scheme=false) { + return get_structure_tensors(is_fwbw_scheme).move_to(*this); + } + + //! Compute the structure tensor field of an image \newinstance. + CImg get_structure_tensors(const bool is_fwbw_scheme=false) const { + if (is_empty()) return *this; + CImg res; + if (_depth>1) { // 3D + res.assign(_width,_height,_depth,6,0); + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ix = (Incc - Ipcc)/2, + iy = (Icnc - Icpc)/2, + iz = (Iccn - Iccp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=ix*iz; + *(ptrd3++)+=iy*iy; + *(ptrd4++)+=iy*iz; + *(ptrd5++)+=iz*iz; + } + } + } else { // Forward/backward finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixf = Incc - Iccc, ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, iyb = Iccc - Icpc, + izf = Iccn - Iccc, izb = Iccc - Iccp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; + *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; + *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; + *(ptrd5++)+=(izf*izf + izb*izb)/2; + } + } + } + } else { // 2D + res.assign(_width,_height,_depth,3,0); + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ix = (Inc - Ipc)/2, + iy = (Icn - Icp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=iy*iy; + } + } + } else { // Forward/backward finite differences (version 2) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ixf = Inc - Icc, ixb = Icc - Ipc, + iyf = Icn - Icc, iyb = Icc - Icp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; + } + } + } + } + return res; + } + + //! Compute field of diffusion tensors for edge-preserving smoothing. + /** + \param sharpness Sharpness + \param anisotropy Anisotropy + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param is_sqrt Tells if the square root of the tensor field is computed instead. + **/ + CImg& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) { + CImg res; + const float + nsharpness = std::max(sharpness,1e-5f), + power1 = (is_sqrt?0.5f:1)*nsharpness, + power2 = power1/(1e-7f + 1 - anisotropy); + blur(alpha).normalize(0,(T)255); + + if (_depth>1) { // 3D + get_structure_tensors().move_to(res).blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth>=(cimg_openmp_sizefactor)*256)) + cimg_forYZ(*this,y,z) { + Tfloat + *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2), + *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5); + CImg val(3), vec(3,3); + cimg_forX(*this,x) { + res.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + const float + _l1 = val[2], _l2 = val[1], _l3 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0, + ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), + vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), + wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), + n1 = (float)std::pow(1 + l1 + l2 + l3,-power1), + n2 = (float)std::pow(1 + l1 + l2 + l3,-power2); + *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx; + *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy; + *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz; + *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy; + *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz; + *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz; + } + } + } else { // for 2D images + get_structure_tensors().move_to(res).blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height>=(cimg_openmp_sizefactor)*256)) + cimg_forY(*this,y) { + Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2); + CImg val(2), vec(2,2); + cimg_forX(*this,x) { + res.get_tensor_at(x,y).symmetric_eigen(val,vec); + const float + _l1 = val[1], _l2 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, + ux = vec(1,0), uy = vec(1,1), + vx = vec(0,0), vy = vec(0,1), + n1 = (float)std::pow(1 + l1 + l2,-power1), + n2 = (float)std::pow(1 + l1 + l2,-power2); + *(ptrd0++) = n1*ux*ux + n2*vx*vx; + *(ptrd1++) = n1*ux*uy + n2*vx*vy; + *(ptrd2++) = n1*uy*uy + n2*vy*vy; + } + } + } + return res.move_to(*this); + } + + //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance. + CImg get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const { + return CImg(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); + } + + //! Estimate displacement field between two images. + /** + \param source Reference image. + \param smoothness Smoothness of estimated displacement field. + \param precision Precision required for algorithm convergence. + \param nb_scales Number of scales used to estimate the displacement field. + \param iteration_max Maximum number of iterations allowed for one scale. + \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)). + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). + **/ + CImg& displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). + move_to(*this); + } + + //! Estimate displacement field between two images \newinstance. + CImg get_displacement(const CImg& source, + const float smoothness=0.1f, const float precision=5.f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) const { + if (is_empty() || !source) return +*this; + if (!is_sameXYZC(source)) + throw CImgArgumentException(_cimg_instance + "displacement(): Instance and source image (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + source._width,source._height,source._depth,source._spectrum,source._data); + if (precision<0) + throw CImgArgumentException(_cimg_instance + "displacement(): Invalid specified precision %g " + "(should be >=0)", + cimg_instance, + precision); + + const bool is_3d = source._depth>1; + const unsigned int constraint = is_3d?3:2; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum0?nb_scales: + (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1); + + const float _precision = (float)std::pow(10.,-(double)precision); + float sm, sM = source.max_min(sm), tm, tM = max_min(tm); + const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm); + + CImg U, V; + floatT bound = 0; + for (int scale = (int)_nb_scales - 1; scale>=0; --scale) { + const float factor = (float)std::pow(1.5,(double)scale); + const unsigned int + _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1, + _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1, + _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1; + if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // Skip too small scales + const CImg + I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta, + I2 = (get_resize(I1,2)-=tm)/=tdelta; + if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V); + if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); + else { + if (guide) + guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U); + else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); + } + + float dt = 2, energy = cimg::type::max(); + const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); + cimg_abort_init; + + for (unsigned int iteration = 0; iteration=0) // Isotropic regularization + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } + if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints + U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor; + U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor; + U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor; + } + } else { // Anisotropic regularization + const float nsmoothness = -smoothness; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints + U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor; + U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor; + U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor; + } + } + } + } else { // 2D version + if (smoothness>=0) // Isotropic regularization + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; + if (U(x,y,1)>y) U(x,y,1) = (float)y; + bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; + bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; + } else { + if (U(x,y,0)<-x) U(x,y,0) = -(float)x; + if (U(x,y,1)<-y) U(x,y,1) = -(float)y; + bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; + bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } + if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints + U(_x,_y,0) = V(_x,_y,0)/factor; + U(_x,_y,1) = V(_x,_y,1)/factor; + } + } else { // Anisotropic regularization + const float nsmoothness = -smoothness; + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; + if (U(x,y,1)>y) U(x,y,1) = (float)y; + bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; + bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; + } else { + if (U(x,y,0)<-x) U(x,y,0) = -(float)x; + if (U(x,y,1)<-y) U(x,y,1) = -(float)y; + bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; + bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints + U(_x,_y,0) = V(_x,_y,0)/factor; + U(_x,_y,1) = V(_x,_y,1)/factor; + } + } + } + } + const float d_energy = (_energy - energy)/(sw*sh*sd); + if (d_energy<=0 && -d_energy<_precision) break; + if (d_energy>0) dt*=0.5f; + energy = _energy; + } + } + return U; + } + + //! Compute correspondence map between two images, using a patch-matching algorithm. + /** + \param patch_image The image containing the reference patches to match with the instance image. + \param patch_width Width of the patch used for matching. + \param patch_height Height of the patch used for matching. + \param patch_depth Depth of the patch used for matching. + \param nb_iterations Number of patch-match iterations. + \param nb_randoms Number of randomization attempts (per pixel). + \param patch_penalization Penalization factor in score related patch occurrences. + if negative, also tells that identity result is not avoided. + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). + \param[out] matching_score Returned as the image of matching scores. + **/ + template + CImg& matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + CImg &matching_score) { + return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. + template + CImg get_matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + CImg &matching_score) const { + return _matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization, + guide,true,matching_score); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg& matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5, + const float patch_penalization=0, + const CImg &guide=CImg::const_empty()) { + return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg get_matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5, + const float patch_penalization=0, + const CImg &guide=CImg::const_empty()) const { + CImg matching_score; + return _matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score); + } + + template + CImg _matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + const bool is_matching_score, + CImg &matching_score) const { + if (is_empty()) return CImg::const_empty(); + if (patch_image._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) " + "have different spectrums.", + cimg_instance, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + if (patch_width>_width || patch_height>_height || patch_depth>_depth) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the instance image.", + cimg_instance,patch_width,patch_height,patch_depth); + if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the patch image image (%u,%u,%u,%u,%p).", + cimg_instance,patch_width,patch_height,patch_depth, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + const unsigned int + _constraint = patch_image._depth>1?3:2, + constraint = guide._spectrum>_constraint?_constraint:0; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint)) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " + "considering instance and patch image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + + CImg a_map(_width,_height,_depth,patch_image._depth>1?3:2); + CImg is_updated(_width,_height,_depth,1,3); + CImg score(_width,_height,_depth); + CImg occ; + const float _patch_penalization = cimg::abs(patch_penalization); + const bool allow_identity = patch_penalization>=0; + if (_patch_penalization!=0) occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0); + const int + psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1, + psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1, + psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1; + + // Interleave image buffers to speed up patch comparison (cache-friendly). + CImg in_this = get_permute_axes("cxyz"); + in_this._width = _width*_spectrum; + in_this._height = _height; + in_this._depth = _depth; + in_this._spectrum = 1; + CImg in_patch = patch_image.get_permute_axes("cxyz"); + in_patch._width = patch_image._width*patch_image._spectrum; + in_patch._height = patch_image._height; + in_patch._depth = patch_image._depth; + in_patch._spectrum = 1; + + if (_depth>1 || patch_image._depth>1) { // 3D version + + // Initialize correspondence map. + if (guide) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64)) + cimg_forXYZ(*this,x,y,z) { // User-defined initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for cimg_openmp_collapse(2)) + cimg_forXYZ(*this,x,y,z) { // Random initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } + cimg::srand(rng); + } + + // Start iteration loop. + cimg_abort_init; + for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && + iter0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor + u = a_map(x - 1,y,z,0); + v = a_map(x - 1,y,z,1); + w = a_map(x - 1,y,z,2); + if (u>=cx1 - 1 && u=cy1 && v=cz1 && w0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor + u = a_map(x,y - 1,z,0); + v = a_map(x,y - 1,z,1); + w = a_map(x,y - 1,z,2); + if (u>=cx1 && u=cy1 - 1 && v=cz1 && w0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor + u = a_map(x,y,z - 1,0); + v = a_map(x,y,z - 1,1); + w = a_map(x,y,z - 1,2); + if (u>=cx1 && u=cy1 && v=cz1 - 1 && w=cx1 + 1 && u=cy1 && v=cz1 && w=cx1 && u=cy1 + 1 && v=cz1 && w=cx1 && u=cy1 && v=cz1 + 1 && w::inf()); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_forXY(*this,x,y) { // Random initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } + cimg::srand(rng); + } + + // Start iteration loop. + cimg_abort_init; + for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && + iter0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor + u = a_map(x - 1,y,0); + v = a_map(x - 1,y,1); + if (u>=cx1 - 1 && u=cy1 && v0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor + u = a_map(x,y - 1,0); + v = a_map(x,y - 1,1); + if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v& img1, const CImg& img2, const CImg& occ, + const unsigned int psizew, const unsigned int psizeh, + const unsigned int psized, const unsigned int psizec, + const int x1, const int y1, const int z1, + const int x2, const int y2, const int z2, + const int xc, const int yc, const int zc, + const float patch_penalization, + const bool allow_identity, + const float max_score) { // 3D version + if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2,(float)z1-z2)::inf(); + const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2); + const unsigned int psizewc = psizew*psizec; + const ulongT + offx1 = (ulongT)img1._width - psizewc, + offx2 = (ulongT)img2._width - psizewc, + offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width, + offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width; + float ssd = 0; + for (unsigned int k = 0; kmax_score) return max_score; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + + patch_penalization*psizewc*psizeh*psized*occ(xc,yc,zc)/100); + } + + static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& occ, + const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec, + const int x1, const int y1, + const int x2, const int y2, + const int xc, const int yc, + const float patch_penalization, + const bool allow_identity, + const float max_score) { // 2D version + if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2)::inf(); + const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2); + const unsigned int psizewc = psizew*psizec; + const ulongT + offx1 = (ulongT)img1._width - psizewc, + offx2 = (ulongT)img2._width - psizewc; + float ssd = 0; + for (unsigned int j = 0; jmax_score) return max_score; + p1+=offx1; p2+=offx2; + } + return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + + patch_penalization*psizewc*psizeh*occ(xc,yc)/100); + } + + //! Compute Euclidean distance function to a specified value. + /** + \param value Reference value. + \param metric Type of metric. Can be { 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }. + \note + The distance transform implementation has been submitted by A. Meijster, and implements + the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink, + "A general algorithm for computing distance transforms in linear time.", + In: Mathematical Morphology and its Applications to Image and Signal Processing, + J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.' + The submitted code has then been modified to fit CImg coding style and constraints. + **/ + CImg& distance(const T& value, const unsigned int metric=2) { + if (is_empty()) return *this; + if (cimg::type::string()!=pixel_type()) // For datatype < int + return CImg(*this,false).distance((Tint)value,metric). + cut((Tint)cimg::type::min(),(Tint)cimg::type::max()).move_to(*this); + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning) + if (!is_value) return fill(cimg::type::max()); + switch (metric) { + case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev + case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan + case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean + default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean + } + return *this; + } + + //! Compute distance to a specified value \newinstance. + CImg get_distance(const T& value, const unsigned int metric=2) const { + return CImg(*this,false).distance((Tfloat)value,metric); + } + + static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) { + return (u*u - i*i + g[u] - g[i])/(2*(u - i)); + } + + static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) { + return (x - i)*(x - i) + g[i]; + } + + static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) { + return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2); + } + + static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) { + return (x=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; } + if (q<0) { q = 0; s[0] = u; } + else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }} + } + for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan + } + + CImg& _distance_core(longT (*const sep)(const longT, const longT, const longT *const), + longT (*const f)(const longT, const longT, const longT *const)) { + // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why. +#define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9) + + const ulongT wh = (ulongT)_width*_height; +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) +#endif + cimg_forC(*this,c) { + CImg g(_width), dt(_width), s(_width), t(_width); + CImg img = get_shared_channel(c); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forYZ(*this,y,z) { // Over X-direction + cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh); + _distance_scan(_width,g,sep,f,s,t,dt); + cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x]; + } + if (_height>1) { + g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forXZ(*this,x,z) { // Over Y-direction + cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh); + _distance_scan(_height,g,sep,f,s,t,dt); + cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y]; + } + } + if (_depth>1) { + g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forXY(*this,x,y) { // Over Z-direction + cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh); + _distance_scan(_depth,g,sep,f,s,t,dt); + cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z]; + } + } + } + return *this; + } + + //! Compute chamfer distance to a specified value, with a custom metric. + /** + \param value Reference value. + \param metric_mask Metric mask. + \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé. + **/ + template + CImg& distance(const T& value, const CImg& metric_mask) { + if (is_empty()) return *this; + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; + if (!is_value) return fill(cimg::type::max()); + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg img = get_shared_channel(c); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024)) + cimg_forXYZ(metric_mask,dx,dy,dz) { + const t weight = metric_mask(dx,dy,dz); + if (weight) { + for (int z = dz, nz = 0; z=0; --z,--nz) { // Backward scan + for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) { + for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) { + const T dd = img(nx,ny,nz,0,wh) + weight; + if (dd + CImg get_distance(const T& value, const CImg& metric_mask) const { + return CImg(*this,false).distance(value,metric_mask); + } + + //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm). + /** + \param value Reference value. + \param metric Field of distance potentials. + \param is_high_connectivity Tells if the algorithm uses low or high connectivity. + \param[out] return_path An image containing the nodes of the minimal path. + **/ + template + CImg& distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) { + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); + } + + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance. + template + CImg::type> + get_distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) const { + if (is_empty()) return return_path.assign(); + if (!is_sameXYZ(metric)) + throw CImgArgumentException(_cimg_instance + "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) " + "have incompatible dimensions.", + cimg_instance, + metric._width,metric._height,metric._depth,metric._spectrum); + typedef typename cimg::superset::type td; // Type used for computing cumulative distances + CImg result(_width,_height,_depth,_spectrum), Q; + CImg is_queued(_width,_height,_depth,1); + if (return_path) return_path.assign(_width,_height,_depth,_spectrum); + + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + CImg path = return_path?return_path.get_shared_channel(c):CImg(); + unsigned int sizeQ = 0; + + // Detect initial seeds. + is_queued.fill(0); + cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) { + Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z); + res(x,y,z) = 0; + if (path) path(x,y,z) = (to)0; + } + + // Start distance propagation. + while (sizeQ) { + + // Get and remove point with minimal potential from the queue. + const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); + const td P = (td)-Q(0,0); + Q._priority_queue_remove(sizeQ); + + // Update neighbors. + td npot = 0; + if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) { + res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2; + } + if (x + 1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) { + res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8; + } + if (y + 1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) { + res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32; + } + if (z + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) { + res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) { + res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9; + } + if (x - 1>=0 && y + 1=0) { // Diagonal neighbors on slice z - 1 + if (x - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) { + res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) { + res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40; + } + if (y + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)), + x - 1,y - 1,z - 1)) { + res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)), + x + 1,y - 1,z - 1)) { + res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41; + } + if (x - 1>=0 && y + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) { + res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) { + res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24; + } + if (y + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)), + x - 1,y - 1,z + 1)) { + res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)), + x + 1,y - 1,z + 1)) { + res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25; + } + if (x - 1>=0 && y + 1 + CImg& distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) { + return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this); + } + + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. + template + CImg get_distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) const { + CImg return_path; + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path); + } + + //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). + /** + \param value Reference value. + \param metric Field of distance potentials. + **/ + template + CImg& distance_eikonal(const T& value, const CImg& metric) { + return get_distance_eikonal(value,metric).move_to(*this); + } + + //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). + template + CImg get_distance_eikonal(const T& value, const CImg& metric) const { + if (is_empty()) return *this; + if (!is_sameXYZ(metric)) + throw CImgArgumentException(_cimg_instance + "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have " + "incompatible dimensions.", + cimg_instance, + metric._width,metric._height,metric._depth,metric._spectrum); + CImg result(_width,_height,_depth,_spectrum,cimg::type::max()), Q; + CImg state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen + + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state)) + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + unsigned int sizeQ = 0; + state.fill(-1); + + // Detect initial seeds. + Tfloat *ptr1 = res._data; char *ptr2 = state._data; + cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; } + + // Initialize seeds neighbors. + ptr2 = state._data; + cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) { + if (x - 1>=0 && state(x - 1,y,z)==-1) { + const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z); + } + if (x + 1=0 && state(x,y - 1,z)==-1) { + const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z); + } + if (y + 1=0 && state(x,y,z - 1)==-1) { + const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1); + } + if (z + 1=0) { + if (x - 1>=0 && state(x - 1,y,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); + if (dist=0 && state(x,y - 1,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + if (dist=0 && state(x,y,z - 1)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); + if (dist& res, const Tfloat P, + const int x=0, const int y=0, const int z=0) const { + const Tfloat M = (Tfloat)cimg::type::max(); + T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 11) { // 3D + T + T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1=0?res(x,y,z - 1):M,z + 1T2) cimg::swap(T1,T2); + if (T2>T3) cimg::swap(T2,T3); + if (T1>T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T31) { // 2D + T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T2 + void _eik_priority_queue_insert(CImg& state, unsigned int& siz, const t value, + const unsigned int x, const unsigned int y, const unsigned int z) { + if (state(x,y,z)>0) return; + state(x,y,z) = 0; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3)); + } + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE. + /** + \param nb_iterations Number of PDE iterations. + \param band_size Size of the narrow band. + \param time_step Time step of the PDE iterations. + **/ + CImg& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { + if (is_empty()) return *this; + CImg velocity(*this,false); + for (unsigned int iteration = 0; iteration1) { // 3D + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)0?(Incc - Iccc):(Iccc - Ipcc), + iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc), + iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp), + ng = 1e-5f + cimg::hypot(gx,gy,gz), + ngx = gx/ng, + ngy = gy/ng, + ngz = gz/ng, + veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } else { // 2D version + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)0?(Inc - Icc):(Icc - Ipc), + iy = gy*sgn>0?(Icn - Icc):(Icc - Icp), + ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)), + ngx = gx/ng, + ngy = gy/ng, + veloc = sgn*(ngx*ix + ngy*iy - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } + if (veloc_max>0) *this+=(velocity*=time_step/veloc_max); + } + return *this; + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance. + CImg get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, + const float time_step=0.5f) const { + return CImg(*this,false).distance_eikonal(nb_iterations,band_size,time_step); + } + + //! Compute Haar multiscale wavelet transform. + /** + \param axis Axis considered for the transform. + \param invert Set inverse of direct transform. + \param nb_scales Number of scales used for the transform. + **/ + CImg& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(axis,invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { + if (is_empty() || !nb_scales) return +*this; + CImg res; + const Tfloat sqrt2 = std::sqrt(2.f); + if (nb_scales==1) { + switch (cimg::lowercase(axis)) { // Single scale transform + case 'x' : { + const unsigned int w = _width/2; + if (w) { + if ((w%2) && w!=1) + throw CImgInstanceException(_cimg_instance + "haar(): Sub-image width %u is not even.", + cimg_instance, + w); + + res.assign(_width,_height,_depth,_spectrum); + if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X + for (unsigned int x = 0, xw = w, x2 = 0; x& haar(const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const bool invert=false, const unsigned int nb_scales=1) const { + CImg res; + if (nb_scales==1) { // Single scale transform + if (_width>1) get_haar('x',invert,1).move_to(res); + if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); } + if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); } + if (res) return res; + } else { // Multi-scale transform + if (invert) { // Inverse transform + res.assign(*this,false); + if (_width>1) { + if (_height>1) { + if (_depth>1) { + unsigned int w = _width, h = _height, d = _depth; + for (unsigned int s = 1; w && h && d && s1) { + unsigned int w = _width, d = _depth; + for (unsigned int s = 1; w && d && s1) { + if (_depth>1) { + unsigned int h = _height, d = _depth; + for (unsigned int s = 1; h && d && s1) { + unsigned int d = _depth; + for (unsigned int s = 1; d && s1) { + if (_height>1) { + if (_depth>1) + for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s1) { + if (_depth>1) + for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s1) for (unsigned int s = 1, d = _depth/2; d && s get_FFT(const char axis, const bool is_inverse=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],axis,is_inverse); + return res; + } + + //! Compute n-D Fast Fourier Transform. + /* + \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + CImgList get_FFT(const bool is_inverse=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],is_inverse); + return res; + } + + //! Compute 1D Fast Fourier Transform, along a specified axis. + /** + \param[in,out] real Real part of the pixel values. + \param[in,out] imag Imaginary part of the pixel values. + \param axis Axis along which the FFT is computed. + \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + static void FFT(CImg& real, CImg& imag, const char axis, const bool is_inverse=false, + const unsigned int nb_threads=0) { + if (!real) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.", + pixel_type()); + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " + "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + const char _axis = cimg::lowercase(axis); + if (_axis!='x' && _axis!='y' && _axis!='z') + throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts " + "(%u,%u,%u,%u) " + "(should be { x | y | z }).", + pixel_type(),axis, + real._width,real._height,real._depth,real._spectrum); + cimg::unused(nb_threads); +#ifdef cimg_use_fftw3 + cimg::mutex(12); +#ifndef cimg_use_fftw3_singlethread + fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus()); +#endif + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) + throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " + "for computing FFT of image (%u,%u,%u,%u) along the X-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth), + real._width,real._height,real._depth,real._spectrum); + double *const ptrf = (double*)data_in; + fftw_plan data_plan = + _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(), + data_in,0,1,real.width(), + data_in,0,1,real.width(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(), + data_in,0,1,real.height(), + data_in,0,1,real.height(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(), + data_in,0,1,real.depth(), + data_in,0,1,real.depth(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + CImg realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c); + switch (_axis) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + break; + case 'y' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + break; + default : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + } + + fftw_execute(data_plan); + + const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0; + switch (_axis) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + break; + case 'y' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + break; + default : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + } + } + + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#ifndef cimg_use_fftw3_singlethread + fftw_cleanup_threads(); +#endif + cimg::mutex(12,0); +#else + switch (_axis) { + case 'x' : { // Fourier along X, using built-in functions + const unsigned int N = real._width, N2 = N>>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the X-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forYZC(real,y,z,c) { + cimg::swap(real(i,y,z,c),real(j,y,z,c)); + cimg::swap(imag(i,y,z,c),imag(j,y,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = delta>>1; + for (unsigned int i = 0; i>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the Y-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXZC(real,x,z,c) { + cimg::swap(real(x,i,z,c),real(x,j,z,c)); + cimg::swap(imag(x,i,z,c),imag(x,j,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the Z-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXYC(real,x,y,c) { + cimg::swap(real(x,y,i,c),real(x,y,j,c)); + cimg::swap(imag(x,y,i,c),imag(x,y,j,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i& real, CImg& imag, const bool is_inverse=false, + const unsigned int nb_threads=0) { + if (!real) + throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.", + pixel_type()); + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " + "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + cimg::unused(nb_threads); +#ifdef cimg_use_fftw3 + cimg::mutex(12); +#ifndef cimg_use_fftw3_singlethread + fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus()); +#endif + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) + throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " + "for computing FFT of image (%u,%u,%u,%u).", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width* + real._height*real._depth*real._spectrum), + real._width,real._height,real._depth,real._spectrum); + double *const ptrf = (double*)data_in; + fftw_plan data_plan = + real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + fftw_plan_dft_1d(real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + CImg realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; } + fftw_execute(data_plan); + if (is_inverse) { + const double a = 1.0/(real.width()*real.height()*real.depth()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); } + } else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; } + } + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#ifndef cimg_use_fftw3_singlethread + fftw_cleanup_threads(); +#endif + cimg::mutex(12,0); +#else + if (real._depth>1) FFT(real,imag,'z',is_inverse); + if (real._height>1) FFT(real,imag,'y',is_inverse); + if (real._width>1) FFT(real,imag,'x',is_inverse); +#endif + } + + //@} + //------------------------------------- + // + //! \name 3D Objects Management + //@{ + //------------------------------------- + + //! Rotate 3D object's vertices. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or second quaternion coordinate. + \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). + **/ + CImg& rotate_object3d(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) { + return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this); + } + + CImg get_rotate_object3d(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) const { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "rotate_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + return CImg::rotation_matrix(x,y,z,w,is_quaternion)**this; + } + + //! Shift 3D object's vertices. + /** + \param tx X-coordinate of the 3D displacement vector. + \param ty Y-coordinate of the 3D displacement vector. + \param tz Z-coordinate of the 3D displacement vector. + **/ + CImg& shift_object3d(const float tx, const float ty=0, const float tz=0) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz; + return *this; + } + + //! Shift 3D object's vertices \newinstance. + CImg get_shift_object3d(const float tx, const float ty=0, const float tz=0) const { + return CImg(*this,false).shift_object3d(tx,ty,tz); + } + + //! Shift 3D object's vertices, so that it becomes centered. + /** + \note The object center is computed as its barycenter. + **/ + CImg& shift_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; + return *this; + } + + //! Shift 3D object's vertices, so that it becomes centered \newinstance. + CImg get_shift_object3d() const { + return CImg(*this,false).shift_object3d(); + } + + //! Resize 3D object. + /** + \param sx Width of the 3D object's bounding box. + \param sy Height of the 3D object's bounding box. + \param sz Depth of the 3D object's bounding box. + **/ + CImg& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + if (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } + if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } + if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } + return *this; + } + + //! Resize 3D object \newinstance. + CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { + return CImg(*this,false).resize_object3d(sx,sy,sz); + } + + //! Resize 3D object to unit size. + CImg resize_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz); + if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; } + return *this; + } + + //! Resize 3D object to unit size \newinstance. + CImg get_resize_object3d() const { + return CImg(*this,false).resize_object3d(); + } + + //! Merge two 3D objects together. + /** + \param[in,out] primitives Primitives data of the current 3D object. + \param obj_vertices Vertices data of the additional 3D object. + \param obj_primitives Primitives data of the additional 3D object. + **/ + template + CImg& append_object3d(CImgList& primitives, const CImg& obj_vertices, + const CImgList& obj_primitives) { + if (!obj_vertices || !obj_primitives) return *this; + if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a " + "set of 3D vertices.", + cimg_instance, + obj_vertices._width,obj_vertices._height, + obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data); + + if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); } + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + const unsigned int P = _width; + append(obj_vertices,'x'); + const unsigned int N = primitives._width; + primitives.insert(obj_primitives); + for (unsigned int i = N; i &p = primitives[i]; + switch (p.size()) { + case 1 : p[0]+=P; break; // Point + case 5 : p[0]+=P; p[1]+=P; break; // Sphere + case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment + case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle + case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle + } + } + return *this; + } + + //! Texturize primitives of a 3D object. + /** + \param[in,out] primitives Primitives data of the 3D object. + \param[in,out] colors Colors data of the 3D object. + \param texture Texture image to map to 3D object. + \param coords Texture-mapping coordinates. + **/ + template + const CImg& texturize_object3d(CImgList& primitives, CImgList& colors, + const CImg& texture, const CImg& coords=CImg::const_empty()) const { + if (is_empty()) return *this; + if (_height!=3) + throw CImgInstanceException(_cimg_instance + "texturize_object3d(): image instance is not a set of 3D points.", + cimg_instance); + if (coords && (coords._width!=_width || coords._height!=2)) + throw CImgArgumentException(_cimg_instance + "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).", + cimg_instance, + coords._width,coords._height,coords._depth,coords._spectrum,coords._data); + CImg _coords; + if (!coords) { // If no texture coordinates specified, do a default XY-projection + _coords.assign(_width,2); + float + xmin, xmax = (float)get_shared_row(0).max_min(xmin), + ymin, ymax = (float)get_shared_row(1).max_min(ymin), + dx = xmax>xmin?xmax-xmin:1, + dy = ymax>ymin?ymax-ymin:1; + cimg_forX(*this,p) { + _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx); + _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy); + } + } else _coords = coords; + + int texture_ind = -1; + cimglist_for(primitives,l) { + CImg &p = primitives[l]; + const unsigned int siz = p.size(); + switch (siz) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)p[0]; + const int x0 = _coords(i0,0), y0 = _coords(i0,1); + texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, + y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]); + } break; + case 2 : case 6 : { // Line + const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,x0,y0,x1,y1).move_to(p); + } break; + case 3 : case 9 : { // Triangle + const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1), + x2 = _coords(i2,0), y2 = _coords(i2,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p); + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1), + x2 = _coords(i2,0), y2 = _coords(i2,1), + x3 = _coords(i3,0), y3 = _coords(i3,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p); + } break; + } + } + return *this; + } + + //! Generate a 3D elevation of the image instance. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param[out] colors The returned list of the 3D object colors. + \param elevation The input elevation map. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + CImgList colors3d; + const CImg points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2); + CImg().display_object3d("Elevation3d",points3d,faces3d,colors3d); + \endcode + \image html ref_elevation3d.jpg + **/ + template + CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& elevation) const { + if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1) + throw CImgArgumentException(_cimg_instance + "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + elevation._width,elevation._height,elevation._depth, + elevation._spectrum,elevation._data); + if (is_empty()) return *this; + float m, M = (float)max_min(m); + if (M==m) ++M; + colors.assign(); + const unsigned int size_x1 = _width - 1, size_y1 = _height - 1; + for (unsigned int y = 0; y1?((*this)(x,y,1) - m)*255/(M-m):r), + b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r); + CImg::vector((tc)r,(tc)g,(tc)b).move_to(colors); + } + const typename CImg::_functor2d_int func(elevation); + return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height); + } + + //! Generate the 3D projection planes of the image instance. + /** + \param[out] primitives Primitives data of the returned 3D object. + \param[out] colors Colors data of the returned 3D object. + \param x0 X-coordinate of the projection point. + \param y0 Y-coordinate of the projection point. + \param z0 Z-coordinate of the projection point. + \param normalize_colors Tells if the created textures have normalized colors. + **/ + template + CImg get_projections3d(CImgList& primitives, CImgList& colors, + const unsigned int x0, const unsigned int y0, const unsigned int z0, + const bool normalize_colors=false) const { + float m = 0, M = 0, delta = 1; + if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); } + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + CImg img_xy, img_xz, img_yz; + if (normalize_colors) { + ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy); + ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1). + move_to(img_xz); + ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1). + move_to(img_yz); + } else { + get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy); + get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz); + get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz); + } + CImg points(12,3,1,1, + 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0, + 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0, + _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1); + primitives.assign(); + CImg::vector(0,1,2,3,0,0,img_xy._width - 1,0,img_xy._width - 1,img_xy._height - 1,0,img_xy._height - 1). + move_to(primitives); + CImg::vector(4,5,6,7,0,0,img_xz._width - 1,0,img_xz._width - 1,img_xz._height - 1,0,img_xz._height - 1). + move_to(primitives); + CImg::vector(8,9,10,11,0,0,img_yz._width - 1,0,img_yz._width - 1,img_yz._height - 1,0,img_yz._height - 1). + move_to(primitives); + colors.assign(); + img_xy.move_to(colors); + img_xz.move_to(colors); + img_yz.move_to(colors); + return points; + } + + //! Generate a isoline of the image instance as a 3D object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3D object colors. + \param size_x The number of subdivisions along the X-axis. + \param size_y The number of subdisivions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + const CImg points3d = img.get_isoline3d(faces3d,100); + CImg().display_object3d("Isoline3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isoline3d.jpg + **/ + template + CImg get_isoline3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a scalar image.", + cimg_instance); + if (_depth>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a 2D image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) { + const _functor2d_int func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height()); + } else { + const _functor2d_float func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y); + } + return vertices; + } + + //! Compute isolines of a function, as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Elevation functor. Must have operator()(x,y) defined. + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static CImg isoline3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + CImgList vertices; + primitives.assign(); + typename CImg::_functor_isoline3d add_vertex(vertices); + typename CImg::_functor_isoline3d add_segment(primitives); + isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y); + return vertices>'x'; + } + + //! Compute isolines of a function, as a 3D object. + /** + \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex. + \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x, const int size_y) { + static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, + 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, + { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, + { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, + { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; + const unsigned int + _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nxm1 = nx - 1, + nym1 = ny - 1; + + if (!nxm1 || !nym1) return; + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1; + CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); + CImg values1(nx), values2(nx); + float X = x0, Y = y0, nX = X + dx, nY = Y + dy; + int nb_vertices = 0; + + // Fill first line with values + cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; } + + // Run the marching squares algorithm + for (unsigned int yi = 0, nyi = 1; yi + static CImg isoline3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y); + } + + template + static int _isoline3d_index(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int nx) { + switch (edge) { + case 0 : return (int)indices1(x,0); + case 1 : return (int)indices1(nx,1); + case 2 : return (int)indices2(x,0); + case 3 : return (int)indices1(x,1); + } + return 0; + } + + //! Generate an isosurface of the image instance as a 3D object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3D object colors. + \param size_x Number of subdivisions along the X-axis. + \param size_y Number of subdisivions along the Y-axis. + \param size_z Number of subdisivions along the Z-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img = CImg("reference.jpg").resize(-100,-100,20); + CImgList faces3d; + const CImg points3d = img.get_isosurface3d(faces3d,100); + CImg().display_object3d("Isosurface3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isosurface3d.jpg + **/ + template + CImg get_isosurface3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100, const int size_z=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isosurface3d(): Instance is not a scalar image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) { + const _functor3d_int func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, + width(),height(),depth()); + } else { + const _functor3d_float func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, + size_x,size_y,size_z); + } + return vertices; + } + + //! Compute isosurface of a function, as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static CImg isosurface3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x=32, const int size_y=32, const int size_z=32) { + CImgList vertices; + primitives.assign(); + typename CImg::_functor_isosurface3d add_vertex(vertices); + typename CImg::_functor_isosurface3d add_triangle(primitives); + isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z); + return vertices>'x'; + } + + //! Compute isosurface of a function, as a 3D object. + /** + \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex. + \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x, const int size_y, const int size_z) { + static const unsigned int edges[256] = { + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 + }; + + static const int triangles[256][16] = { + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, + { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, + { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, + { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, + { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, + { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, + { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, + { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, + { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, + { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, + { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, + { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, + { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, + { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, + { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, + { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, + { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, + { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, + { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, + { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, + { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, + { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, + { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, + { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, + { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, + { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, + { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, + { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, + { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, + { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, + { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, + { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, + { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, + { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, + { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, + { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, + { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, + { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, + { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, + { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, + { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, + { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, + { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, + { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, + { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, + { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, + { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } + }; + + const unsigned int + _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nz = _nz?_nz:1, + nxm1 = nx - 1, + nym1 = ny - 1, + nzm1 = nz - 1; + if (!nxm1 || !nym1 || !nzm1) return; + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1; + CImg indices1(nx,ny,1,3,-1), indices2(indices1); + CImg values1(nx,ny), values2(nx,ny); + float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; + int nb_vertices = 0; + + // Fill the first plane with function values + Y = y0; + cimg_forY(values1,y) { + X = x0; + cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; } + Y+=dy; + } + + // Run Marching Cubes algorithm + Z = z0; nZ = Z + dz; + for (unsigned int zi = 0; zi + static CImg isosurface3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int dx=32, const int dy=32, const int dz=32) { + const _functor3d_expr func(expression); + return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz); + } + + template + static int _isosurface3d_index(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int y, + const unsigned int nx, const unsigned int ny) { + switch (edge) { + case 0 : return indices1(x,y,0); + case 1 : return indices1(nx,y,1); + case 2 : return indices1(x,ny,0); + case 3 : return indices1(x,y,1); + case 4 : return indices2(x,y,0); + case 5 : return indices2(nx,y,1); + case 6 : return indices2(x,ny,0); + case 7 : return indices2(x,y,1); + case 8 : return indices1(x,y,2); + case 9 : return indices1(nx,y,2); + case 10 : return indices1(nx,ny,2); + case 11 : return indices1(x,ny,2); + } + return 0; + } + + // Define functors for accessing image values (used in previous functions). + struct _functor2d_int { + const CImg& ref; + _functor2d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref((int)x,(int)y); + } + }; + + struct _functor2d_float { + const CImg& ref; + _functor2d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref._linear_atXY(x,y); + } + }; + + struct _functor2d_expr { + _cimg_math_parser *mp; + ~_functor2d_expr() { mp->end(); delete mp; } + _functor2d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } + float operator()(const float x, const float y) const { + return (float)(*mp)(x,y,0,0); + } + }; + + struct _functor3d_int { + const CImg& ref; + _functor3d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref((int)x,(int)y,(int)z); + } + }; + + struct _functor3d_float { + const CImg& ref; + _functor3d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref._linear_atXYZ(x,y,z); + } + }; + + struct _functor3d_expr { + _cimg_math_parser *mp; + ~_functor3d_expr() { mp->end(); delete mp; } + _functor3d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } + float operator()(const float x, const float y, const float z) const { + return (float)(*mp)(x,y,z,0); + } + }; + + struct _functor4d_int { + const CImg& ref; + _functor4d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref((int)x,(int)y,(int)z,c); + } + }; + + struct _functor_isoline3d { + CImgList& list; + _functor_isoline3d(CImgList& _list):list(_list) {} + template + void operator()(const t x, const t y, const t z) { CImg::vector((T)x,(T)y,(T)z).move_to(list); } + template + void operator()(const t i, const t j) { CImg::vector((T)i,(T)j).move_to(list); } + }; + + struct _functor_isosurface3d { + CImgList& list; + _functor_isosurface3d(CImgList& _list):list(_list) {} + template + void operator()(const t x, const t y, const t z) { CImg::vector((T)x,(T)y,(T)z).move_to(list); } + }; + + //! Compute 3D elevation of a function as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + **/ + template + static CImg elevation3d(CImgList& primitives, const tfunc& func, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const float + nx0 = x0=0?size_x:(nx1-nx0)*-size_x/100), + nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1, + _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), + nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1; + if (nsize_x<2 || nsize_y<2) + throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).", + pixel_type(), + nsize_x,nsize_y); + + CImg vertices(nsize_x*nsize_y,3); + floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2); + for (unsigned int y = 0; y + static CImg elevation3d(CImgList& primitives, const char *const expression, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y); + } + + //! Generate a 3D box object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the box (dimension along the X-axis). + \param size_y The height of the box (dimension along the Y-axis). + \param size_z The depth of the box (dimension along the Z-axis). + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::box3d(faces3d,10,20,30); + CImg().display_object3d("Box3d",points3d,faces3d); + \endcode + \image html ref_box3d.jpg + **/ + template + static CImg box3d(CImgList& primitives, + const float size_x=200, const float size_y=100, const float size_z=100) { + primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); + return CImg(8,3,1,1, + 0.,size_x,size_x, 0., 0.,size_x,size_x, 0., + 0., 0.,size_y,size_y, 0., 0.,size_y,size_y, + 0., 0., 0., 0.,size_z,size_z,size_z,size_z); + } + + //! Generate a 3D cone. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cone basis. + \param size_z The cone's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cone3d(faces3d,50); + CImg().display_object3d("Cone3d",points3d,faces3d); + \endcode + \image html ref_cone3d.jpg + **/ + template + static CImg cone3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,size_z, + 0.,0.,0.); + for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices); + } + const unsigned int nbr = vertices._width - 2; + for (unsigned int p = 0; p::vector(1,next,curr).move_to(primitives); + CImg::vector(0,curr,next).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D cylinder. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cylinder basis. + \param size_z The cylinder's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cylinder3d(faces3d,50); + CImg().display_object3d("Cylinder3d",points3d,faces3d); + \endcode + \image html ref_cylinder3d.jpg + **/ + template + static CImg cylinder3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,0., + 0.,0.,size_z); + for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices); + } + const unsigned int nbr = (vertices._width - 2)/2; + for (unsigned int p = 0; p::vector(0,next,curr).move_to(primitives); + CImg::vector(1,curr + 1,next + 1).move_to(primitives); + CImg::vector(curr,next,next + 1,curr + 1).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D torus. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius1 The large radius. + \param radius2 The small radius. + \param subdivisions1 The number of angular subdivisions for the large radius. + \param subdivisions2 The number of angular subdivisions for the small radius. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::torus3d(faces3d,20,4); + CImg().display_object3d("Torus3d",points3d,faces3d); + \endcode + \image html ref_torus3d.jpg + **/ + template + static CImg torus3d(CImgList& primitives, + const float radius1=100, const float radius2=30, + const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { + primitives.assign(); + if (!subdivisions1 || !subdivisions2) return CImg(); + CImgList vertices; + for (unsigned int v = 0; v::vector(x,y,z).move_to(vertices); + } + } + for (unsigned int vv = 0; vv::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives); + } + } + return vertices>'x'; + } + + //! Generate a 3D XY-plane. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the plane (dimension along the X-axis). + \param size_y The height of the plane (dimensions along the Y-axis). + \param subdivisions_x The number of planar subdivisions along the X-axis. + \param subdivisions_y The number of planar subdivisions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::plane3d(faces3d,100,50); + CImg().display_object3d("Plane3d",points3d,faces3d); + \endcode + \image html ref_plane3d.jpg + **/ + template + static CImg plane3d(CImgList& primitives, + const float size_x=100, const float size_y=100, + const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) { + primitives.assign(); + if (!subdivisions_x || !subdivisions_y) return CImg(); + CImgList vertices; + const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1; + const float fx = (float)size_x/w, fy = (float)size_y/h; + for (unsigned int y = 0; y::vector(fx*x,fy*y,0).move_to(vertices); + for (unsigned int y = 0; y::vector(off1,off4,off3,off2).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D sphere. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the sphere (dimension along the X-axis). + \param subdivisions The number of recursive subdivisions from an initial icosahedron. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::sphere3d(faces3d,100,4); + CImg().display_object3d("Sphere3d",points3d,faces3d); + \endcode + \image html ref_sphere3d.jpg + **/ + template + static CImg sphere3d(CImgList& primitives, + const float radius=50, const unsigned int subdivisions=3) { + + // Create initial icosahedron + primitives.assign(); + const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a; + CImgList vertices(12,1,3,1,1, b,a,0., -b,a,0., -b,-a,0., b,-a,0., a,0.,b, a,0.,-b, + -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a); + primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6, + 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3, + 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2); + // edge - length/2 + float he = (float)a; + + // Recurse subdivisions + for (unsigned int i = 0; i::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; } + if (i1<0) { CImg::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; } + if (i2<0) { CImg::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; } + primitives.remove(0); + CImg::vector(p0,i0,i1).move_to(primitives); + CImg::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives); + CImg::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives); + CImg::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives); + } + } + return (vertices>'x')*=radius; + } + + //! Generate a 3D ellipsoid. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param tensor The tensor which gives the shape and size of the ellipsoid. + \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg tensor = CImg::diagonal(10,7,3), + points3d = CImg::ellipsoid3d(faces3d,tensor,4); + CImg().display_object3d("Ellipsoid3d",points3d,faces3d); + \endcode + \image html ref_ellipsoid3d.jpg + **/ + template + static CImg ellipsoid3d(CImgList& primitives, + const CImg& tensor, const unsigned int subdivisions=3) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImg S, V; + tensor.symmetric_eigen(S,V); + const float orient = + (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) + + (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) + + (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2); + if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); } + const float l0 = S[0], l1 = S[1], l2 = S[2]; + CImg vertices = sphere3d(primitives,1.,subdivisions); + vertices.get_shared_row(0)*=l0; + vertices.get_shared_row(1)*=l1; + vertices.get_shared_row(2)*=l2; + return V*vertices; + } + + //! Convert 3D object into a CImg3d representation. + /** + \param primitives Primitives data of the 3D object. + \param colors Colors data of the 3D object. + \param opacities Opacities data of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + **/ + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + CImg& object3dtoCImg3d(const bool full_check=true) { + return get_object3dtoCImg3d(full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \newinstance. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true) const { + CImg error_message(1024); + if (!is_object3d(primitives,colors,opacities,full_check,error_message)) + throw CImgInstanceException(_cimg_instance + "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,_width,primitives._width,error_message.data()); + CImg res(1,_size_object3dtoCImg3d(primitives,colors,opacities)); + float *ptrd = res._data; + + // Put magick number. + *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f; + *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f; + + // Put number of vertices and primitives. + *(ptrd++) = cimg::uint2float(_width); + *(ptrd++) = cimg::uint2float(primitives._width); + + // Put vertex data. + if (is_empty() || !primitives) return res; + const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2); + cimg_forX(*this,p) { + *(ptrd++) = (float)*(ptrx++); + *(ptrd++) = (float)*(ptry++); + *(ptrd++) = (float)*(ptrz++); + } + + // Put primitive data. + cimglist_for(primitives,p) { + *(ptrd++) = (float)primitives[p].size(); + const tp *ptrp = primitives[p]._data; + cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++)); + } + + // Put color/texture data. + const unsigned int csiz = std::min(colors._width,primitives._width); + for (int c = 0; c<(int)csiz; ++c) { + const CImg& color = colors[c]; + const tc *ptrc = color._data; + if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; } + else { + *(ptrd++) = -128.f; + int shared_ind = -1; + if (color.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImgList& opacities, float *ptrd) const { + cimglist_for(opacities,o) { + const CImg& opacity = opacities[o]; + const to *ptro = opacity._data; + if (opacity.size()==1) *(ptrd++) = (float)*ptro; + else { + *(ptrd++) = -128.f; + int shared_ind = -1; + if (opacity.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImg& opacities, float *ptrd) const { + const to *ptro = opacities._data; + cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++); + return ptrd; + } + + template + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImgList& opacities) const { + unsigned int siz = 8U + 3*_width; + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { + if (colors[c].is_shared()) siz+=4; + else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; } + } + if (colors._width + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImg& opacities) const { + unsigned int siz = 8U + 3*_width; + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { + const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; + } + if (colors._width + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const bool full_check=true) const { + CImgList opacities; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) const { + CImgList colors, opacities; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert 3D object into a CImg3d representation \overloading. + CImg get_object3dtoCImg3d(const bool full_check=true) const { + CImgList opacities, colors; + CImgList primitives(width(),1,1,1,1); + cimglist_for(primitives,p) primitives(p,0) = p; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert CImg3d representation into a 3D object. + /** + \param[out] primitives Primitives data of the 3D object. + \param[out] colors Colors data of the 3D object. + \param[out] opacities Opacities data of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + **/ + template + CImg& CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) { + return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this); + } + + //! Convert CImg3d representation into a 3D object \newinstance. + template + CImg get_CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) const { + CImg error_message(1024); + if (!is_CImg3d(full_check,error_message)) + throw CImgInstanceException(_cimg_instance + "CImg3dtoobject3d(): image instance is not a CImg3d (%s).", + cimg_instance,error_message.data()); + const T *ptrs = _data + 6; + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + const CImg points = CImg(ptrs,3,nb_points,1,1,true).get_transpose(); + ptrs+=3*nb_points; + primitives.assign(nb_primitives); + cimglist_for(primitives,p) { + const unsigned int nb_inds = (unsigned int)*(ptrs++); + primitives[p].assign(1,nb_inds); + tp *ptrp = primitives[p]._data; + for (unsigned int i = 0; i::max(),(T)cimg::type::max()); \ + const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \ + const ulongT _sc_whd = (ulongT)_width*_height*_depth; \ + cimg::unused(_sc_maxval); + +#define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \ + _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval) + + // [internal] The following _draw_scanline() routines are *non user-friendly functions*, + // used only for internal purpose. + // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid. + template + CImg& _draw_scanline(const int x0, const int x1, const int y, + const tc *const color, const float opacity, + const float brightness, + const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) { + const int nx0 = x0>0?x0:0, nx1 = x1=0) { + const tc *col = color; + const ulongT off = whd - dx - 1; + T *ptrd = data(nx0,y); + if (opacity>=1) { // ** Opaque drawing ** + if (brightness==1) { // Brightness==1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)*(col++); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)*(col++); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } else if (brightness<1) { // Brightness<1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } else { // Brightness>1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } + } else { // ** Transparent drawing ** + if (brightness==1) { // Brightness==1 + cimg_forC(*this,c) { + const Tfloat val = *(col++)*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else if (brightness<=1) { // Brightness<1 + cimg_forC(*this,c) { + const Tfloat val = *(col++)*brightness*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else { // Brightness>1 + cimg_forC(*this,c) { + const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } + } + } + return *this; + } + + //! Draw a 3D point. + /** + \param x0 X-coordinate of the point. + \param y0 Y-coordinate of the point. + \param z0 Z-coordinate of the point. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_point(50,50,color); + \endcode + **/ + template + CImg& draw_point(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_point(): Specified color is (null).", + cimg_instance); + if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } + } + return *this; + } + + //! Draw a 2D point \simplification. + template + CImg& draw_point(const int x0, const int y0, + const tc *const color, const float opacity=1) { + return draw_point(x0,y0,0,color,opacity); + } + + // Draw a points cloud. + /** + \param points Image of vertices coordinates. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_point(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity); + } break; + default : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity); + } + } + return *this; + } + + //! Draw a 2D line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + \note + - Line routine uses Bresenham's algorithm. + - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,color); + \endcode + **/ + template + CImg& draw_line(int x0, int y0, + int x1, int y1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !opacity || !pattern || + std::min(y0,y1)>=height() || std::max(y0,y1)<0 || + std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1); + dx01*=-1; dy01*=-1; + } + + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + const int + step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + T *const ptrd = is_horizontal?data(y,x):data(x,y); + cimg_forC(*this,c) { + const T val = color[c]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a 2D line, with z-buffering. + /** + \param zbuffer Zbuffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + **/ + template + CImg& draw_line(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_line(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float diz01 = iz1 - iz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1); + dx01*=-1; dy01*=-1; diz01*=-1; + } + + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float iz = iz0 + diz01*yy0/dy01; + tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y); + + if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) { + *ptrz = (tz)iz; + T *const ptrd = is_horizontal?data(y,x):data(x,y); + cimg_forC(*this,c) { + const T val = color[c]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + \note + - Line routine uses the well known Bresenham's algorithm. + \par Example: + \code + CImg img(100,100,1,3,0), texture("texture256x256.ppm"); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,texture,0,0,255,255); + \endcode + **/ + template + CImg& draw_line(int x0, int y0, + int x1, int y1, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + + if (is_empty() || !opacity || !pattern) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + int + dtx01 = tx1 - tx0, dty01 = ty1 - ty0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1); + dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy01ty = dy01*cimg::sign(dty01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01, + tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01, + ty = ty0 + (dty01*yy0 + hdy01ty)/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line, with perspective correction. + /** + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(int x0, int y0, const float z0, + int x1, int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float + diz01 = iz1 - iz0, + txz0 = tx0*iz0, txz1 = tx1*iz1, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, + dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1); + dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float + iz = iz0 + diz01*yy0/dy01, + txz = txz0 + dtxz01*yy0/dy01, + tyz = tyz0 + dtyz01*yy0/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line, with perspective correction and z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float + diz01 = iz1 - iz0, + txz0 = tx0*iz0, txz1 = tx1*iz1, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, + dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1); + dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float + iz = iz0 + diz01*yy0/dy01, + txz = txz0 + dtxz01*yy0/dy01, + tyz = tyz0 + dtyz01*yy0/dy01; + tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y); + + if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) { + *ptrz = (tz)iz; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a set of consecutive lines. + /** + \param points Coordinates of vertices, stored as a list of vectors. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If set to true, init hatch motif. + \note + - This function uses several call to the single CImg::draw_line() procedure, + depending on the vectors size in \p points. + **/ + template + CImg& draw_line(const CImg& points, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + default : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i + CImg& draw_arrow(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1, + const float angle=30, const float length=-10, + const unsigned int pattern=~0U) { + if (is_empty()) return *this; + const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v, + deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f, + l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; + if (sq>0) { + const float + cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg), + cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg); + const int + xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl), + xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr), + xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2; + draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); + } else draw_point(x0,y0,color,opacity); + return *this; + } + + //! Draw a 2D spline. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + \note + - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points + and corresponding velocity vectors. + - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the + average number of pixels in each drawn segment. + - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), + (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point + and (\p xa,\p ya), (\p xb,\p yb) are two + \e control points. + The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from + the control points as + \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,255,255 }; + img.draw_spline(30,30,0,100,90,40,0,-100,color); + \endcode + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const tc *const color, const float opacity=1, + const float precision=0.25, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Specified color is (null).", + cimg_instance); + if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); + int ox = x0, oy = y0; + for (float t = 0; t<1; t+=_precision) { + const float t2 = t*t, t3 = t2*t; + const int + nx = (int)(ax*t3 + bx*t2 + u0*t + x0), + ny = (int)(ay*t3 + by*t2 + v0*t + y0); + draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch); + ninit_hatch = false; + ox = nx; oy = ny; + } + return draw_line(ox,oy,x1,y1,color,opacity,pattern,false); + } + + //! Draw a textured 2D spline. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param texture Texture image defining line pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch if \c true, reinit hatch motif. + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const CImg& texture, + const int tx0, const int ty0, const int tx1, const int ty1, + const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_empty()) return *this; + if (is_overlapped(texture)) + return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch); + if (x0==x1 && y0==y1) + return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, + y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(), + opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); + int ox = x0, oy = y0, otx = tx0, oty = ty0; + for (float t1 = 0; t1<1; t1+=_precision) { + const float t2 = t1*t1, t3 = t2*t1; + const int + nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0), + ny = (int)(ay*t3 + by*t2 + v0*t1 + y0), + ntx = tx0 + (int)((tx1 - tx0)*t1), + nty = ty0 + (int)((ty1 - ty0)*t1); + draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch); + ninit_hatch = false; + ox = nx; oy = ny; otx = ntx; oty = nty; + } + return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false); + } + + //! Draw a set of consecutive splines. + /** + \param points Vertices data. + \param tangents Tangents data. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param is_closed_set Tells if the drawn spline set is closed. + \param precision Precision of the drawing. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + **/ + template + CImg& draw_spline(const CImg& points, const CImg& tangents, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + default : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); + int ox = x0, oy = y0; + float ou = u0, ov = v0; + for (unsigned int i = 1; i + CImg& draw_spline(const CImg& points, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + CImg tangents; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + tangents.assign(points._width,points._height); + cimg_forX(points,p) { + const unsigned int + p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0), + p1 = is_closed_set?(p + 1)%points.width():(p + 1 + CImg& _draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, const float opacity, + const float brightness) { + if (y0>y1) cimg::swap(x0,x1,y0,y1); + if (y0>y2) cimg::swap(x0,x2,y0,y2); + if (y1>y2) cimg::swap(x1,x2,y1,y2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM); + cimg_draw_scanline(xm,xM,y,color,opacity,cbs); + } + return *this; + } + + //! Draw a filled 2D triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); + return *this; + } + + //! Draw a outlined 2D triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + draw_line(x0,y0,x1,y1,color,opacity,pattern,true). + draw_line(x1,y1,x2,y2,color,opacity,pattern,false). + draw_line(x2,y2,x0,y0,color,opacity,pattern,false); + return *this; + } + + //! Draw a filled 2D triangle, with z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param z0 Z-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param z1 Z-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param z2 Z-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param brightness Brightness factor. + **/ + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; + + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param bs0 Brightness factor of the first vertex (in [0,2]). + \param bs1 brightness factor of the second vertex (in [0,2]). + \param bs2 brightness factor of the third vertex (in [0,2]). + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, + float bs0, + float bs1, + float bs2, + float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a color-interpolated 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex. + \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex. + \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc1 *const color1, + const tc2 *const color2, + const tc3 *const color3, + const float opacity=1) { + const unsigned char one = 1; + cimg_forC(*this,c) + get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity); + return *this; + } + + //! Draw a textured 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param opacity Drawing opacity. + \param brightness Brightness factor of the drawing (in [0,2]). + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2; + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM; + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a 2D textured triangle, with perspective correction. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured 2D triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a Phong-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + + const ulongT lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,lxm,lxM,lym,lyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dlxmM = lxM - lxm, dlymM = lyM - lym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a Phong-shaded 2D triangle, with z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; + + const ulongT lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dlxmM = lxM - lxm, dlymM = lyM - lym; + const float dizmM = izM - izm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const int + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const tc col = color[c]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param bs0 Brightness factor of the first vertex. + \param bs1 Brightness factor of the second vertex. + \param bs2 Brightness factor of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + bs0,bs1,bs2,opacity); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2; + const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym; + const float dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + bs0,bs1,bs2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym, + dlxmM = lxM - lxm, dlymM = lyM - lym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM, + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle, with perspective correction. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2, + lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2, + dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1, + dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float + dizmM = izM - izm, + dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, + dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + lxz = lxzm + dlxzmM*xxm/dxmM, + lyz = lyzm + dlyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz), + lx = (int)cimg::round(lxz/iz), + ly = (int)cimg::round(lyz/iz); + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2, + lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2, + dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1, + dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float + dizmM = izM - izm, + dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, + dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + lxz = lxzm + dlxzmM*xxm/dxmM, + lyz = lyzm + dlyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz), + lx = (int)cimg::round(lxz/iz), + ly = (int)cimg::round(lyz/iz); + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a filled 4D rectangle. + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param z0 Z-coordinate of the upper-left rectangle corner. + \param c0 C-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param z1 Z-coordinate of the lower-right rectangle corner. + \param c1 C-coordinate of the lower-right rectangle corner. + \param val Scalar value used to fill the rectangle area. + \param opacity Drawing opacity. + **/ + CImg& draw_rectangle(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const T val, const float opacity=1) { + if (is_empty()) return *this; + const int + nx0 = x0=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0), + ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0), + lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0), + lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0); + const ulongT + offX = (ulongT)_width - lx, + offY = (ulongT)_width*(_height - ly), + offZ = (ulongT)_width*_height*(_depth - lz); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0); + if (lx>0 && ly>0 && lz>0 && lc>0) + for (int v = 0; v=1) { + if (sizeof(T)!=1) { for (int x = 0; x + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_rectangle(): Specified color is (null).", + cimg_instance); + cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity); + return *this; + } + + //! Draw a filled 2D rectangle. + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1) { + return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity); + } + + //! Draw a outlined 2D rectangle \overloading. + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); + if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); + const int + nx0 = x0 + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + if (points.height()!=2) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum); + if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity); + if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)), + cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity); + if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)), + cimg::uiround(points(1,0)),cimg::uiround(points(1,1)), + cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity); + cimg_init_scanline(opacity); + int + xmin = 0, ymin = 0, + xmax = points.get_shared_row(0).max_min(xmin), + ymax = points.get_shared_row(1).max_min(ymin); + if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; + if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity); + + ymin = std::max(0,ymin); + ymax = std::min(height() - 1,ymax); + CImg Xs(points._width,ymax - ymin + 1); + CImg count(Xs._height,1,1,1,0); + unsigned int n = 0, nn = 1; + bool go_on = true; + + while (go_on) { + unsigned int an = (nn + 1)%points._width; + const int + x0 = cimg::uiround(points(n,0)), + y0 = cimg::uiround(points(n,1)); + if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; } + const int + x1 = cimg::uiround(points(nn,0)), + y1 = cimg::uiround(points(nn,1)); + unsigned int tn = an; + while (points(tn,1)==y1) (tn+=1)%=points._width; + + if (y0!=y1) { + const int + y2 = cimg::uiround(points(tn,1)), + x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1, + step = cimg::sign(y01), + tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2, + tend = tmax - (step==cimg::sign(y12)); + unsigned int y = (unsigned int)y0 - ymin; + for (int t = 0; t<=tend; ++t, y+=step) + if (yn; + n = nn; + nn = an; + } + + cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512)) + cimg_forY(Xs,y) { + const CImg Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort(); + int px = width(); + for (unsigned int k = 0; k + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity, const unsigned int pattern) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity); + if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1), + (int)points(1,0),(int)points(1,1),color,opacity,pattern); + bool ninit_hatch = true; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum); + default : { + CImg npoints(points._width,2); + int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); + unsigned int nb_points = 1; + for (unsigned int p = 1; p + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity=1) { + return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true); + } + + //! Draw a filled 2D ellipse \overloading. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param tensor Diffusion tensor describing the ellipse. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity=1) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity); + } + + //! Draw an outlined 2D ellipse. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param r1 First radius of the ellipse. + \param r2 Second radius of the ellipse. + \param angle Angle of the first radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity, const unsigned int pattern) { + if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false); + return *this; + } + + //! Draw an outlined 2D ellipse \overloading. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param tensor Diffusion tensor describing the ellipse. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity, + const unsigned int pattern) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity,pattern); + } + + template + CImg& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle, + const tc *const color, const float opacity, + const unsigned int pattern, const bool is_filled) { + if (is_empty() || (!is_filled && !pattern)) return *this; + const float radiusM = std::max(radius1,radius2); + if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_ellipse(): Specified color is (null).", + cimg_instance); + const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2); + if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity); + if (iradius1==iradius2) { + if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity); + else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern); + } + const float ang = (float)(angle*cimg::PI/180); + + if (!is_filled) { // Outlined + const float ca = std::cos(ang), sa = std::sin(ang); + CImg points((unsigned int)cimg::round(6*radiusM),2); + cimg_forX(points,k) { + const float + _ang = (float)(2*cimg::PI*k/points._width), + X = (float)(radius1*std::cos(_ang)), + Y = (float)(radius2*std::sin(_ang)); + points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa)); + points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca)); + } + draw_polygon(points,color,opacity,pattern); + } else { // Filled + cimg_init_scanline(opacity); + const float + ca = std::cos(ang), + sa = -std::sin(ang), + ca2 = ca*ca, + sa2 = sa*sa, + casa = ca*sa, + i1 = 1/cimg::sqr(radius1), + i2 = 1/cimg::sqr(radius2), + t1 = i1*ca2 + i2*sa2, + t2 = (i2 - i1)*casa, + t3 = i2*ca2 + i1*sa2, + t12 = t1*2; + const int + _ymin = (int)std::floor(y0 - radiusM), + _ymax = (int)std::ceil(y0 + radiusM), + ymin = _ymin<0?0:_ymin, + ymax = _ymax>=height()?height() - 1:_ymax; + for (int y = ymin; y<=ymax; ++y) { + const float + Y = y - y0 + 0.5f, + B = 2*t2*Y, + C = t3*Y*Y - 1, + D = B*B - 4*t1*C; + if (D>=0) { + const float sD = std::sqrt(D); + const int + xmin = (int)(x0 + cimg::round((-B - sD)/t12)), + xmax = (int)(x0 + cimg::round((-B + sD)/t12)); + cimg_draw_scanline(xmin,xmax,y,color,opacity,1); + } + } + } + return *this; + } + + //! Draw a filled 2D circle. + /** + \param x0 X-coordinate of the circle center. + \param y0 Y-coordinate of the circle center. + \param radius Circle radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - Circle version of the Bresenham's algorithm is used. + **/ + template + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (!radius) return draw_point(x0,y0,color,opacity); + cimg_init_scanline(opacity); + if (y0>=0 && y0=0) { + const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y; + if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern); + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; + if (!radius) return draw_point(x0,y0,color,opacity); + + draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity). + draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity); + if (radius==1) return *this; + for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x=0) { f+=(ddFy+=2); --y; } + ++x; ++(f+=(ddFx+=2)); + if (x!=y + 1) { + const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x, + x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y; + draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). + draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); + if (x!=y) + draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). + draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); + } + } + return *this; + } + + //! Draw an image. + /** + \param sprite Sprite image. + \param x0 X-coordinate of the sprite position. + \param y0 Y-coordinate of the sprite position. + \param z0 Z-coordinate of the sprite position. + \param c0 C-coordinate of the sprite position. + \param opacity Drawing opacity. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) + return assign(sprite,false); + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c=1) for (int x = 0; x& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) + return assign(sprite,false); + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + const ulongT slx = lx*sizeof(T); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c=1) std::memcpy(ptrd,ptrs,slx); + else for (int x = 0; x + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,z0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const CImg& sprite, const float opacity=1) { + return draw_image(0,sprite,opacity); + } + + //! Draw a masked image. + /** + \param sprite Sprite image. + \param mask Mask image. + \param x0 X-coordinate of the sprite position in the image instance. + \param y0 Y-coordinate of the sprite position in the image instance. + \param z0 Z-coordinate of the sprite position in the image instance. + \param c0 C-coordinate of the sprite position in the image instance. + \param mask_max_value Maximum pixel value of the mask image \c mask. + \param opacity Drawing opacity. + \note + - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. + - Dimensions along x,y and z of \p sprite and \p mask must be the same. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + if (is_empty() || !sprite || !mask) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value); + if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value); + if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth) + throw CImgArgumentException(_cimg_instance + "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data, + mask._width,mask._height,mask._depth,mask._spectrum,mask._data); + + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + const ulongT msize = mask.size(); + + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw an image. + template + CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a text string. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color. + \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color. + \param opacity Drawing opacity. + \param font Font used for drawing text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \note A transparent background is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \note A transparent foreground is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Array of spectrum() values of type \c T, + defining the foreground color (0 means 'transparent'). + \param background_color Array of spectrum() values of type \c T, + defining the background color (0 means 'transparent'). + \param opacity Drawing opacity. + \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise). + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + const CImgList& font = CImgList::font(font_height,true); + _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); + return *this; + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int background_color=0, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + cimg::unused(background_color); + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data); + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data); + } + + template + CImg& _draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, + const bool is_native_font) { + if (!text) return *this; + if (!font) + throw CImgArgumentException(_cimg_instance + "draw_text(): Empty specified font.", + cimg_instance); + + const unsigned int text_length = (unsigned int)std::strlen(text); + const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4; + unsigned char o_ch, ch = 0; + int x, y, w; + CImg left_paddings(text_length,1,1,1,0); + const CImg empty = CImg::empty(); + + if (is_empty() || is_native_font) { + // Pre-compute necessary size of the image as well as left paddings of each character. + x = y = w = 0; + o_ch = 0; + for (unsigned int i = 0; iw) w = x; x = 0; break; + case '\t' : x+=4*font[(int)' ']._width; break; + case ' ' : x+=font[(int)' ']._width; break; + default : if (ch'9')) || o_ch==';' || o_ch==':' || o_ch=='!') + left_padding = 4*padding_x; + else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') && + ((ch>='0' && ch<='9') || + (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') || + (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) || + o_ch=='.' || o_ch=='\'' || ch=='\'') + left_padding = padding_x; + else if ((o_ch<'0' || o_ch>'9') && ch!='-') { + const CImg &mask = ch + 256U' ' && o_ch>' ' && mask._height>13) { + const CImg &o_mask = o_ch + 256U13) { + const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0; + left_padding = -10; + cimg_forY(mask,k) { + const int + lpad = o_mask(w1,k)>=8?0: + o_mask._width<=2 || o_mask(w2,k)>=8?-1: + o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3, + rpad = mask(0,k)>=8?0: + mask._width<=2 || mask(1,k)>=8?-1: + mask._width<=3 || mask(2,k)>=8?-2:-3; + left_padding = std::max(left_padding,lpad + rpad); + } + } + } + } + left_paddings[i] = left_padding; + } + x+=left_padding + font[ch]._width + padding_x; + o_ch = ch; + } + } + } + if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; } + if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0); + } + + // Draw font characters on image. + x = x0; y = y0; + for (unsigned int i = 0; i letter = font[ch]; + if (letter) { + const CImg &mask = ch + 256Uletter._spectrum) + letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false); + const unsigned int cmin = std::min(_spectrum,letter._spectrum); + if (foreground_color) + for (unsigned int c = 0; c& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) { + CImg tmp(2048); + std::va_list ap; + va_start(ap,is_down); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + CImg a_label, a_labelmask; + const unsigned char a_labelcolor = 255; + unsigned int ofs = font_size, fs = ofs; + do { // Determine best font size + a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data); + if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) { + font_size = fs; break; + } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) { + ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f)); + } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) { + ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f)); + } else { font_size = fs; break; } + } while (true); + a_label.normalize(0,255); + a_label+=(255 - a_label.get_dilate(3)).normalize(0,80); + a_label.resize(-100,-100,1,3,1); + return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f); + } + + //! Draw a 2D vector field. + /** + \param flow Image of 2D vectors used as input data. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const t2 *const color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + return draw_quiver(flow,CImg(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern); + } + + //! Draw a 2D vector field, using a field of colors. + /** + \param flow Image of 2D vectors used as input data. + \param color Image of spectrum()-D vectors corresponding to the color of each arrow. + \param opacity Opacity of the drawing. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const CImg& color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + if (is_empty()) return *this; + if (!flow || flow._spectrum!=2) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).", + cimg_instance, + flow._width,flow._height,flow._depth,flow._spectrum,flow._data); + if (sampling<=0) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid sampling value %g " + "(should be >0)", + cimg_instance, + sampling); + const bool colorfield = (color._width==flow._width && color._height==flow._height && + color._depth==1 && color._spectrum==_spectrum); + if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern); + float vmax,fact; + if (factor<=0) { + float m, M = (float)flow.get_norm(2).max_min(m); + vmax = (float)std::max(cimg::abs(m),cimg::abs(M)); + if (!vmax) vmax = 1; + fact = -factor; + } else { fact = factor; vmax = 1; } + + for (unsigned int y = sampling/2; y<_height; y+=sampling) + for (unsigned int x = sampling/2; x<_width; x+=sampling) { + const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height; + float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax; + if (is_arrow) { + const int xx = (int)(x + u), yy = (int)(y + v); + if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern); + else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern); + } else { + if (colorfield) + draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), + color.get_vector_at(X,Y)._data,opacity,pattern); + else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), + color._data,opacity,pattern); + } + } + return *this; + } + + //! Draw a labeled horizontal axis. + /** + \param values_x Values along the horizontal axis. + \param y Y-coordinate of the horizontal axis in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const CImg& values_x, const int y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true, const float round_x=0) { + if (is_empty()) return *this; + const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height; + const int siz = (int)values_x.size() - 1; + CImg txt(32); + CImg a_label; + if (siz<=0) { // Degenerated case + draw_line(0,y,_width - 1,y,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x); + a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _xt = (width() - a_label.width())/2, + xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt; + draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case + if (values_x[0]=width() - 2?width() - 3 - a_label.width():_xt; + draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw a labeled vertical axis. + /** + \param x X-coordinate of the vertical axis in the image instance. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const int x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true, const float round_y=0) { + if (is_empty()) return *this; + int siz = (int)values_y.size() - 1; + CImg txt(32); + CImg a_label; + if (siz<=0) { // Degenerated case + draw_line(x,0,x,_height - 1,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y); + a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _yt = (height() - a_label.height())/2, + yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt, + _xt = x - 2 - a_label.width(), + xt = _xt>=0?_xt:x + 3; + draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case + if (values_y[0]=height()?height() - 1 - a_label.height():_yt, + _xt = x - 2 - a_label.width(), + xt = _xt>=0?_xt:x + 3; + draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes. + /** + \param values_x Values along the X-axis. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for the X-axis. + \param pattern_y Drawing pattern for the Y-axis. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axes(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13, const bool allow_zero=true, + const float round_x=0, const float round_y=0) { + if (is_empty()) return *this; + const CImg nvalues_x(values_x._data,values_x.size(),1,1,1,true); + const int sizx = (int)values_x.size() - 1, wm1 = width() - 1; + if (sizx>=0) { + float ox = (float)*nvalues_x; + for (unsigned int x = sizx?1U:0U; x<_width; ++x) { + const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1); + if (nx*ox<=0) { + draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y); + break; + } + ox = nx; + } + } + const CImg nvalues_y(values_y._data,values_y.size(),1,1,1,true); + const int sizy = (int)values_y.size() - 1, hm1 = height() - 1; + if (sizy>0) { + float oy = (float)nvalues_y[0]; + for (unsigned int y = sizy?1U:0U; y<_height; ++y) { + const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1); + if (ny*oy<=0) { + draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x); + break; + } + oy = ny; + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes \overloading. + template + CImg& draw_axes(const float x0, const float x1, const float y0, const float y1, + const tc *const color, const float opacity=1, + const int subdivisionx=-60, const int subdivisiony=-60, + const float precisionx=0, const float precisiony=0, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13) { + if (is_empty()) return *this; + const bool allow_zero = (x0*x1>0) || (y0*y1>0); + const float + dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0), + px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx, + py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony; + if (x0!=x1 && y0!=y1) + draw_axes(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1), + CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1), + color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py); + else if (x0==x1 && y0!=y1) + draw_axis((int)x0,CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1), + color,opacity,pattern_y,font_height,py); + else if (x0!=x1 && y0==y1) + draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0, + color,opacity,pattern_x,font_height,px); + return *this; + } + + //! Draw 2D grid. + /** + \param values_x X-coordinates of the vertical lines. + \param values_y Y-coordinates of the horizontal lines. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for vertical lines. + \param pattern_y Drawing pattern for horizontal lines. + **/ + template + CImg& draw_grid(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + if (values_x) cimg_foroff(values_x,x) { + const int xi = (int)values_x[x]; + if (xi>=0 && xi=0 && yi + CImg& draw_grid(const float delta_x, const float delta_y, + const float offsetx, const float offsety, + const bool invertx, const bool inverty, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + CImg seqx, seqy; + if (delta_x!=0) { + const float dx = delta_x>0?delta_x:_width*-delta_x/100; + const unsigned int nx = (unsigned int)(_width/dx); + seqx = CImg::sequence(1 + nx,0,(unsigned int)(dx*nx)); + if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width); + if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x); + } + if (delta_y!=0) { + const float dy = delta_y>0?delta_y:_height*-delta_y/100; + const unsigned int ny = (unsigned int)(_height/dy); + seqy = CImg::sequence(1 + ny,0,(unsigned int)(dy*ny)); + if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height); + if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y); + } + return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y); + } + + //! Draw 1D graph. + /** + \param data Image containing the graph values I = f(x). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + + \param plot_type Define the type of the plot: + - 0 = No plot. + - 1 = Plot using segments. + - 2 = Plot using cubic splines. + - 3 = Plot with bars. + \param vertex_type Define the type of points: + - 0 = No points. + - 1 = Point. + - 2 = Straight cross. + - 3 = Diagonal cross. + - 4 = Filled circle. + - 5 = Outlined circle. + - 6 = Square. + - 7 = Diamond. + \param ymin Lower bound of the y-range. + \param ymax Upper bound of the y-range. + \param pattern Drawing pattern. + \note + - if \c ymin==ymax==0, the y-range is computed automatically from the input samples. + **/ + template + CImg& draw_graph(const CImg& data, + const tc *const color, const float opacity=1, + const unsigned int plot_type=1, const int vertex_type=1, + const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { + if (is_empty() || _height<=1) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_graph(): Specified color is (null).", + cimg_instance); + + // Create shaded colors for displaying bar plots. + CImg color1, color2; + if (plot_type==3) { + color1.assign(_spectrum); color2.assign(_spectrum); + cimg_forC(*this,c) { + color1[c] = (tc)std::min((float)cimg::type::max(),(float)color[c]*1.2f); + color2[c] = (tc)(color[c]*0.4f); + } + } + + // Compute min/max and normalization factors. + const ulongT + siz = data.size(), + _siz1 = siz - (plot_type!=3), + siz1 = _siz1?_siz1:1; + const unsigned int + _width1 = _width - (plot_type!=3), + width1 = _width1?_width1:1; + double m = ymin, M = ymax; + if (ymin==ymax) m = (double)data.max_min(M); + if (m==M) { --m; ++M; } + const float ca = (float)(M-m)/(_height - 1); + bool init_hatch = true; + + // Draw graph edges + switch (plot_type%4) { + case 1 : { // Segments + int oX = 0, oY = (int)cimg::round((data[0] - m)/ca); + if (siz==1) { + const int Y = (int)cimg::round((*data - m)/ca); + draw_line(0,Y,width() - 1,Y,color,opacity,pattern); + } else { + const float fx = (float)_width/siz1; + for (ulongT off = 1; off ndata(data._data,siz,1,1,1,true); + int oY = (int)cimg::round((data[0] - m)/ca); + cimg_forX(*this,x) { + const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca); + if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch); + init_hatch = false; + oY = Y; + } + } break; + case 3 : { // Bars + const int Y0 = (int)cimg::round(-m/ca); + const float fx = (float)_width/siz1; + int oX = 0; + cimg_foroff(data,off) { + const int + X = (int)cimg::round((off + 1)*fx) - 1, + Y = (int)cimg::round((data[off] - m)/ca); + draw_rectangle(oX,Y0,X,Y,color,opacity). + draw_line(oX,Y,oX,Y0,color2.data(),opacity). + draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity). + draw_line(X,Y,X,Y0,color1.data(),opacity). + draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity); + oX = X + 1; + } + } break; + default : break; // No edges + } + + // Draw graph points + const unsigned int wb2 = plot_type==3?_width1/(2*siz):0; + const float fx = (float)_width1/siz1; + switch (vertex_type%8) { + case 1 : { // Point + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_point(X,Y,color,opacity); + } + } break; + case 2 : { // Straight Cross + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity); + } + } break; + case 3 : { // Diagonal Cross + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity); + } + } break; + case 4 : { // Filled Circle + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity); + } + } break; + case 5 : { // Outlined circle + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity,~0U); + } + } break; + case 6 : { // Square + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U); + } + } break; + case 7 : { // Diamond + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X,Y - 4,X + 4,Y,color,opacity). + draw_line(X + 4,Y,X,Y + 4,color,opacity). + draw_line(X,Y + 4,X - 4,Y,color,opacity). + draw_line(X - 4,Y,X,Y - 4,color,opacity); + } + } break; + default : break; // No points + } + return *this; + } + + bool _draw_fill(const int x, const int y, const int z, + const CImg& ref, const float tolerance2) const { + const T *ptr1 = data(x,y,z), *ptr2 = ref._data; + const ulongT off = _width*_height*_depth; + float diff = 0; + cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; } + return diff<=tolerance2; + } + + //! Draw filled 3D region with the flood fill algorithm. + /** + \param x0 X-coordinate of the starting point of the region to fill. + \param y0 Y-coordinate of the starting point of the region to fill. + \param z0 Z-coordinate of the starting point of the region to fill. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param[out] region Image that will contain the mask of the filled region mask, as an output. + \param tolerance Tolerance concerning neighborhood values. + \param opacity Opacity of the drawing. + \param is_high_connectivity Tells if 8-connexity must be used. + \return \c region is initialized with the binary mask of the filled region. + **/ + template + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity, + CImg ®ion, + const float tolerance = 0, + const bool is_high_connectivity = false) { +#define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \ + stack[N] = x; stack(N,1) = y; stack(N++,2) = z +#define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2) +#define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2) + + if (!containsXYZC(x0,y0,z0,0)) return *this; + const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f); + const float tolerance2 = cimg::sqr(tolerance); + const CImg ref = get_vector_at(x0,y0,z0); + CImg stack(256,1,1,3); + CImg _region(_width,_height,_depth,1,0); + unsigned int N = 0; + int x, y, z; + + _draw_fill_push(x0,y0,z0); + while (N>0) { + _draw_fill_pop(x,y,z); + if (!_region(x,y,z)) { + const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1; + int xl = x, xr = x; + + // Using these booleans reduces the number of pushes drastically. + bool is_yp = false, is_yn = false, is_zp = false, is_zn = false; + for (int step = -1; step<2; step+=2) { + while (x>=0 && x=0 && _draw_fill_is_inside(x,yp,z)) { + if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; } + } else is_yp = false; + if (yn1) { + if (zp>=0 && _draw_fill_is_inside(x,y,zp)) { + if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; } + } else is_zp = false; + if (zn=0 && !is_yp) { + if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) { + _draw_fill_push(xp,yp,z); if (step<0) is_yp = true; + } + if (xn0) is_yp = true; + } + } + if (yn=0 && _draw_fill_is_inside(xp,yn,z)) { + _draw_fill_push(xp,yn,z); if (step<0) is_yn = true; + } + if (xn0) is_yn = true; + } + } + if (depth()>1) { + if (zp>=0 && !is_zp) { + if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) { + _draw_fill_push(xp,y,zp); if (step<0) is_zp = true; + } + if (xn0) is_zp = true; + } + + if (yp>=0 && !is_yp) { + if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); } + if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); } + if (xn=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); } + if (xn=0 && _draw_fill_is_inside(xp,y,zn)) { + _draw_fill_push(xp,y,zn); if (step<0) is_zn = true; + } + if (xn0) is_zn = true; + } + + if (yp>=0 && !is_yp) { + if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); } + if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); } + if (xn=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); } + if (xn + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1, + const float tolerance=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity); + } + + //! Draw filled 2D region with the flood fill algorithm \simplification. + template + CImg& draw_fill(const int x0, const int y0, + const tc *const color, const float opacity=1, + const float tolerance=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity); + } + + //! Draw a random plasma texture. + /** + \param alpha Alpha-parameter. + \param beta Beta-parameter. + \param scale Scale-parameter. + \note Use the mid-point algorithm to render. + **/ + CImg& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) { + if (is_empty()) return *this; + const int w = width(), h = height(); + const Tfloat m = (Tfloat)cimg::type::min(), M = (Tfloat)cimg::type::max(); + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + cimg_forZC(*this,z,c) { + CImg ref = get_shared_slice(z,c); + for (int delta = 1<1; delta>>=1) { + const int delta2 = delta>>1; + const float r = alpha*delta + beta; + + // Square step. + for (int y0 = 0; y0M?M:val); + } + + // Diamond steps. + for (int y = -delta2; yM?M:val); + } + for (int y0 = 0; y0M?M:val); + } + for (int y = -delta2; yM?M:val); + } + } + } + cimg::srand(rng); + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2D fractal. + /** + \param x0 X-coordinate of the upper-left pixel. + \param y0 Y-coordinate of the upper-left pixel. + \param x1 X-coordinate of the lower-right pixel. + \param y1 Y-coordinate of the lower-right pixel. + \param colormap Colormap. + \param opacity Drawing opacity. + \param z0r Real part of the upper-left fractal vertex. + \param z0i Imaginary part of the upper-left fractal vertex. + \param z1r Real part of the lower-right fractal vertex. + \param z1i Imaginary part of the lower-right fractal vertex. + \param iteration_max Maximum number of iterations for each estimated point. + \param is_normalized_iteration Tells if iterations are normalized. + \param is_julia_set Tells if the Mandelbrot or Julia set is rendered. + \param param_r Real part of the Julia set parameter. + \param param_i Imaginary part of the Julia set parameter. + \note Fractal rendering is done by the Escape Time Algorithm. + **/ + template + CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, + const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + if (is_empty()) return *this; + CImg palette; + if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true); + if (palette && palette._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.); + const int + _x0 = cimg::cut(x0,0,width() - 1), + _y0 = cimg::cut(y0,0,height() - 1), + _x1 = cimg::cut(x1,0,width() - 1), + _y1 = cimg::cut(y1,0,height() - 1); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048)) + for (int q = _y0; q<=_y1; ++q) + for (int p = _x0; p<=_x1; ++p) { + unsigned int iteration = 0; + const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height; + double zr, zi, cr, ci; + if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; } + else { zr = param_r; zi = param_i; cr = x; ci = y; } + for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) { + const double temp = zr*zr - zi*zi + cr; + zi = 2*zr*zi + ci; + zr = temp; + } + if (iteration>iteration_max) { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity); + } + } else if (is_normalized_iteration) { + const float + normz = (float)cimg::abs(zr*zr + zi*zi), + niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2); + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c); + else cimg_forC(*this,c) + (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } else { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } + } + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading. + template + CImg& draw_mandelbrot(const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity, + z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i); + } + + //! Draw a 1D gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param sigma Standard variation of the gaussian distribution. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float sigma, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + cimg_forX(*this,x) { + const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2); + T *ptrd = data(x,0,0,0); + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 2D gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param yc Y-coordinate of the gaussian center. + \param tensor Covariance matrix (must be 2x2). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + typedef typename CImg::Tfloat tfloat; + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + float dy = -yc; + cimg_forY(*this,y) { + float dx = -xc; + cimg_forX(*this,x) { + const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); + T *ptrd = data(x,y,0,0); + if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + ++dx; + } + ++dy; + } + return *this; + } + + //! Draw a 2D gaussian function \overloading. + template + CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity=1) { + const double + a = r1*ru*ru + r2*rv*rv, + b = (r1-r2)*ru*rv, + c = r1*rv*rv + r2*ru*ru; + const CImg tensor(2,2,1,1, a,b,b,c); + return draw_gaussian(xc,yc,tensor,color,opacity); + } + + //! Draw a 2D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); + } + + //! Draw a 3D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + typedef typename CImg::Tfloat tfloat; + if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + cimg_forXYZ(*this,x,y,z) { + const float + dx = (x - xc), dy = (y - yc), dz = (z - zc), + val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); + T *ptrd = data(x,y,z,0); + if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 3D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); + } + + //! Draw a 3D object. + /** + \param x0 X-coordinate of the 3D object position + \param y0 Y-coordinate of the 3D object position + \param z0 Z-coordinate of the 3D object position + \param vertices Image Nx3 describing 3D point coordinates + \param primitives List of P primitives + \param colors List of P color (or textures) + \param opacities Image or list of P opacities + \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) + \param is_double_sided Tells if object faces have two sides or are oriented. + \param focale length of the focale (0 for parallel projection) + \param lightx X-coordinate of the light + \param lighty Y-coordinate of the light + \param lightz Z-coordinate of the light + \param specular_lightness Amount of specular light. + \param specular_shininess Shininess of the object + \param g_opacity Global opacity of the object. + **/ + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } +#endif + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } +#endif + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,zbuffer); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,zbuffer); + } +#endif + + template + static float __draw_object3d(const CImgList& opacities, const unsigned int n_primitive, CImg& opacity) { + if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; } + if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); } + opacity.assign(opacities[n_primitive],true); + return 1.f; + } + + template + static float __draw_object3d(const CImg& opacities, const unsigned int n_primitive, CImg& opacity) { + opacity.assign(); + return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive]; + } + + template + static float ___draw_object3d(const CImgList& opacities, const unsigned int n_primitive) { + return n_primitive + static float ___draw_object3d(const CImg& opacities, const unsigned int n_primitive) { + return n_primitive + CImg& _draw_object3d(void *const pboard, CImg& zbuffer, + const float X, const float Y, const float Z, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, const float sprite_scale) { + typedef typename cimg::superset2::type tpfloat; + typedef typename to::value_type _to; + if (is_empty() || !vertices || !primitives) return *this; + CImg error_message(1024); + if (!vertices.is_object3d(primitives,colors,opacities,false,error_message)) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); +#ifndef cimg_use_board + if (pboard) return *this; +#endif + if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety + + const float + nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)), + nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess), + nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1), + nsl2 = 1 - 2*nsl1*nspec, + nsl3 = nspec2 - nsl1 - nsl2; + + // Create light texture for phong-like rendering. + CImg light_texture; + if (render_type==5) { + if (colors._width>primitives._width) { + static CImg default_light_texture; + static const tc *lptr = 0; + static tc ref_values[64] = { 0 }; + const CImg& img = colors.back(); + bool is_same_texture = (lptr==img._data); + if (is_same_texture) + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) { + is_same_texture = false; break; + } + if (!is_same_texture || default_light_texture._spectrum<_spectrum) { + (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum); + lptr = colors.back().data(); + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum); + } + light_texture.assign(default_light_texture,true); + } else { + static CImg default_light_texture; + static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0; + if (!default_light_texture || + lightx!=olightx || lighty!=olighty || lightz!=olightz || + specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) { + default_light_texture.assign(512,512); + const float + dlx = lightx - X, + dly = lighty - Y, + dlz = lightz - Z, + nl = cimg::hypot(dlx,dly,dlz), + nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl), + nly = (default_light_texture._height - 1)/2*(1 + dly/nl), + white[] = { 1 }; + default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white); + cimg_forXY(default_light_texture,x,y) { + const float factor = default_light_texture(x,y); + if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3); + } + default_light_texture.resize(-100,-100,1,_spectrum); + olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess; + } + light_texture.assign(default_light_texture,true); + } + } + + // Compute 3D to 2D projection. + CImg projections(vertices._width,2); + tpfloat parallzmin = cimg::type::max(); + const float absfocale = focale?cimg::abs(focale):0; + if (absfocale) { + cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) + cimg_forX(projections,l) { // Perspective projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + const tpfloat projectedz = z + Z + absfocale; + projections(l,1) = Y + absfocale*y/projectedz; + projections(l,0) = X + absfocale*x/projectedz; + } + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) + cimg_forX(projections,l) { // Parallel projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + if (z visibles(primitives._width,1,1,1,~0U); + CImg zrange(primitives._width); + const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); + bool is_forward = zbuffer?true:false; + + cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096)) + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + switch (primitive.size()) { + case 1 : { // Point + CImg<_to> _opacity; + __draw_object3d(opacities,l,_opacity); + if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false; + const unsigned int i0 = (unsigned int)primitive(0); + const tpfloat z0 = Z + vertices(i0,2); + if (z0>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = z0; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)), + Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)), + Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)), + _zc = Z + Zc, + zc = _zc + _focale, + xc = X + Xc*(absfocale?absfocale/zc:1), + yc = Y + Yc*(absfocale?absfocale/zc:1), + radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0), + vertices(i1,1) - vertices(i0,1), + vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1), + xm = xc - radius, + ym = yc - radius, + xM = xc + radius, + yM = yc + radius; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = _zc; + } + is_forward = false; + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2); + tpfloat xm, xM, ym, yM; + if (x0=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1)/2; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (y0yM) yM = y2; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { + const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0); + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2)/3; + } + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = (unsigned int)primitive(3); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2), + x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (x3xM) xM = x3; + if (y0yM) yM = y2; + if (y3yM) yM = y3; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) { + const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0); + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2 + z3)/4; + } + } + } break; + default : + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid primitive[%u] with size %u " + "(should have size 1,2,3,4,5,6,9 or 12).", + cimg_instance, + l,primitive.size()); + } + } + + // Force transparent primitives to be drawn last when zbuffer is activated + // (and if object contains no spheres or sprites). + if (is_forward) + cimglist_for(primitives,l) + if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l); + + // Sort only visibles primitives. + unsigned int *p_visibles = visibles._data; + tpfloat *p_zrange = zrange._data; + const tpfloat *ptrz = p_zrange; + cimg_for(visibles,ptr,unsigned int) { + if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; } + ++ptrz; + } + const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data); + if (!nb_visibles) { + if (render_type==5) cimg::mutex(10,0); + return *this; + } + CImg permutations; + CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); + + // Compute light properties + CImg lightprops; + switch (render_type) { + case 3 : { // Flat Shading + lightprops.assign(nb_visibles); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const CImg& primitive = primitives(visibles(permutations(l))); + const unsigned int psize = (unsigned int)primitive.size(); + if (psize==3 || psize==4 || psize==9 || psize==12) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nx = dy1*dz2 - dz1*dy2, + ny = dz1*dx2 - dx1*dz2, + nz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + cimg::hypot(nx,ny,nz), + lx = X + (x0 + x1 + x2)/3 - lightx, + ly = Y + (y0 + y1 + y2)/3 - lighty, + lz = Z + (z0 + z1 + z2)/3 - lightz, + nl = 1e-5f + cimg::hypot(lx,ly,lz), + factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } else lightprops[l] = 1; + } + } break; + + case 4 : // Gouraud Shading + case 5 : { // Phong-Shading + CImg vertices_normals(vertices._width,6,1,1,0); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + for (int l = 0; l<(int)nb_visibles; ++l) { + const CImg& primitive = primitives[visibles(l)]; + const unsigned int psize = (unsigned int)primitive.size(); + const bool + triangle_flag = (psize==3) || (psize==9), + quadrangle_flag = (psize==4) || (psize==12); + if (triangle_flag || quadrangle_flag) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = quadrangle_flag?(unsigned int)primitive(3):0; + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nnx = dy1*dz2 - dz1*dy2, + nny = dz1*dx2 - dx1*dz2, + nnz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + cimg::hypot(nnx,nny,nnz), + nx = nnx/norm, + ny = nny/norm, + nz = nnz/norm; + unsigned int ix = 0, iy = 1, iz = 2; + if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; } + vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz; + vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz; + vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz; + if (quadrangle_flag) { + vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz; + } + } + } + + if (is_double_sided) cimg_forX(vertices_normals,p) { + const float + nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2), + nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5), + n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1; + if (n1>n0) { + vertices_normals(p,0) = -nx1; + vertices_normals(p,1) = -ny1; + vertices_normals(p,2) = -nz1; + } + } + + if (render_type==4) { + lightprops.assign(vertices._width); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = 1e-5f + cimg::hypot(nx,ny,nz), + lx = X + vertices(l,0) - lightx, + ly = Y + vertices(l,1) - lighty, + lz = Z + vertices(l,2) - lightz, + nl = 1e-5f + cimg::hypot(lx,ly,lz), + factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } + } else { + const unsigned int + lw2 = light_texture._width/2 - 1, + lh2 = light_texture._height/2 - 1; + lightprops.assign(vertices._width,2); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = 1e-5f + cimg::hypot(nx,ny,nz), + nnx = nx/norm, + nny = ny/norm; + lightprops(l,0) = lw2*(1 + nnx); + lightprops(l,1) = lh2*(1 + nny); + } + } + } break; + } + + // Draw visible primitives + const CImg default_color(1,_spectrum,1,1,(tc)200); + CImg<_to> _opacity; + + for (unsigned int l = 0; l& primitive = primitives[n_primitive]; + const CImg + &__color = n_primitive(), + _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)? + __color.get_resize(-100,-100,-100,_spectrum,0):CImg(), + &color = _color?_color:(__color?__color:default_color); + const tc *const pcolor = color._data; + float opacity = __draw_object3d(opacities,n_primitive,_opacity); + if (_opacity.is_empty()) opacity*=g_opacity; + +#ifdef cimg_use_board + LibBoard::Board &board = *(LibBoard::Board*)pboard; +#endif + + switch (primitive.size()) { + case 1 : { // Colored point or sprite + const unsigned int n0 = (unsigned int)primitive[0]; + const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)); + + if (_opacity.is_empty()) { // Scalar opacity + + if (color.size()==_spectrum) { // Colored point + draw_point(x0,y0,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height()-(float)y0); + } +#endif + } else { // Sprite + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(color._width*factor), + _sh = (unsigned int)(color._height*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && + (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + draw_image(nx0,ny0,sprite,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); + } +#endif + } + } + } else { // Opacity mask + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor), + _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && + (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + const CImg<_to> + _nopacity = (sw!=_opacity._width || sh!=_opacity._height)? + _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(), + &nopacity = _nopacity?_nopacity:_opacity; + draw_image(nx0,ny0,sprite,nopacity,g_opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); + } +#endif + } + } + } break; + case 2 : { // Colored line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity); + else draw_line(x0,y0,x1,y1,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1); + } +#endif + } else { + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + } +#endif + } + } break; + case 5 : { // Colored sphere + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + is_wireframe = (unsigned int)primitive[2], + is_radius = (unsigned int)primitive[3]; + float Xc,Yc,Zc,radius; + if (is_radius) { + Xc = (float)vertices(n0,0); + Yc = (float)vertices(n0,1); + Zc = (float)vertices(n0,2); + radius = cimg::hypot(vertices(n1,0) - vertices(n0,0), + vertices(n1,1) - vertices(n0,1), + vertices(n1,2) - vertices(n0,2)); + } else { + Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)); + Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)); + Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)); + radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0), + vertices(n1,1) - vertices(n0,1), + vertices(n1,2) - vertices(n0,2)); + } + const float + zc = Z + Zc + _focale, + af = absfocale?absfocale/zc:1, + xc = X + Xc*af, + yc = Y + Yc*af; + radius*=af; + + switch (render_type) { + case 0 : + draw_point((int)xc,(int)yc,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot(xc,height() - yc); + } +#endif + break; + case 1 : + draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.setFillColor(LibBoard::Color::Null); + board.drawCircle(xc,height() - yc,radius); + } +#endif + break; + default : + if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); + else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + if (!is_wireframe) board.fillCircle(xc,height() - yc,radius); + else { + board.setFillColor(LibBoard::Color::Null); + board.drawCircle(xc,height() - yc,radius); + } + } +#endif + break; + } + } break; + case 6 : { // Textured line + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for line primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + tx0 = (int)primitive[2], ty0 = (int)primitive[3], + tx1 = (int)primitive[4], ty1 = (int)primitive[5], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + } +#endif + } else { + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + } +#endif + } + } break; + case 3 : { // Colored triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity). + draw_line(x1,y1,x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 3 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)); + else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), + (float)x1,height() - (float)y1,lightprops(n1), + (float)x2,height() - (float)y2,lightprops(n2)); + } +#endif + break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), + (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), + (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), + (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + } +#endif + } break; + } + } break; + case 4 : { // Colored quadrangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)), + x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)), + xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4; + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale, + zc = (z0 + z1 + z2 + z3)/4; + + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity). + draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + board.drawDot((float)x3,height() - (float)y3); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity). + draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); + board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity); + else + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l)); + else + _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)). + _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp),(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3), + lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4; + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity). + draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity). + draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity). + draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity); + else + draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity). + draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity). + draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity). + draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x1,height() - (float)y1,lightprop1, + (float)x2,height() - (float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x2,height() - (float)y2,lightprop2, + (float)x3,height() - (float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)), + lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)), + lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4; + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). + draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). + draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). + draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); + else + draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). + draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). + draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). + draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x2,height() - (float)y2,l2, + (float)x3,height() - (float)y3,l3); + } +#endif + } break; + } + } break; + case 9 : { // Textured triangle + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for triangle primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + tx0 = (int)primitive[3], ty0 = (int)primitive[4], + tx1 = (int)primitive[5], ty1 = (int)primitive[6], + tx2 = (int)primitive[7], ty2 = (int)primitive[8], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, + ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), + (float)x1,height() - (float)y1,lightprops(n1), + (float)x2,height() - (float)y2,lightprops(n2)); + } +#endif + break; + case 5 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), + opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), + opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), + (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), + (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), + (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + } +#endif + break; + } + } break; + case 12 : { // Textured quadrangle + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for quadrangle primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + tx0 = (int)primitive[4], ty0 = (int)primitive[5], + tx1 = (int)primitive[6], ty1 = (int)primitive[7], + tx2 = (int)primitive[8], ty2 = (int)primitive[9], + tx3 = (int)primitive[10], ty3 = (int)primitive[11], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)), + x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale; + + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, + ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity). + draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3, + ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + board.drawDot((float)x3,height() - (float)y3); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); + board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x1,height() - (float)y1,lightprop1, + (float)x2,height() - (float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0, + (float)x2,height() - (float)y2,lightprop2, + (float)x3,height() - (float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)), + lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + board.fillGouraudTriangle((float)x0,height() -(float)y0,l0, + (float)x2,height() - (float)y2,l2, + (float)x3,height() - (float)y3,l3); + } +#endif + } break; + } + } break; + } + } + if (render_type==5) cimg::mutex(10,0); + return *this; + } + + //@} + //--------------------------- + // + //! \name Data Input + //@{ + //--------------------------- + + //! Launch simple interface to select a shape from an image. + /** + \param disp Display window to use. + \param feature_type Type of feature to select. Can be { 0=point | 1=line | 2=rectangle | 3=ellipse }. + \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images. + \param exit_on_anykey Exit function when any key is pressed. + **/ + CImg& select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); + } + + //! Simple interface to select a shape from an image \overloading. + CImg& select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + CImgDisplay disp; + return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); + } + + CImg _select(CImgDisplay &disp, const char *const title, + const unsigned int feature_type, unsigned int *const XYZ, + const int origX, const int origY, const int origZ, + const bool exit_on_anykey, + const bool reset_view3d, + const bool force_display_z_coord, + const bool is_deep_selection_default) const { + if (is_empty()) return CImg(1,feature_type==0?3:6,1,1,-1); + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + } else { + if (title) disp.set_title("%s",title); + disp.move_inside_screen(); + } + + CImg thumb; + if (width()>disp.screen_width() || height()>disp.screen_height()) + get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb); + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).set_wheel().show_mouse(); + + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + int area = 0, area_started = 0, area_clicked = 0, phase = 0, + X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), + Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), + Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth), + X1 =-1, Y1 = -1, Z1 = -1, + X3d = -1, Y3d = -1, + oX3d = X3d, oY3d = -1, + omx = -1, omy = -1; + float X = -1, Y = -1, Z = -1; + unsigned int key = 0, font_size = 32; + + bool is_deep_selection = is_deep_selection_default, + shape_selected = false, text_down = false, visible_cursor = true; + static CImg pose3d; + static bool is_view3d = false, is_axes = true; + if (reset_view3d) { pose3d.assign(); is_view3d = false; } + CImg points3d, opacities3d, sel_opacities3d; + CImgList primitives3d, sel_primitives3d; + CImgList colors3d, sel_colors3d; + CImg visu, visu0, view3d; + CImg text(1024); *text = 0; + + while (!key && !disp.is_closed() && !shape_selected) { + + // Handle mouse motion and selection + int + mx = disp.mouse_x(), + my = disp.mouse_y(); + + const float + mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height(); + + area = 0; + if (mX>=0 && mY>=0 && mX=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); } + if (mY>=0 && mX>=width() && mY=width() && mY>=height()) area = 4; + if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0; + + CImg filename(32); + + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break; + case cimg::keyPAGEDOWN : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break; + case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp); + visu0.save(filename); + (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + } + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp); + save(filename); + (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + } + + switch (area) { + + case 0 : // When mouse is out of image range + mx = my = -1; X = Y = Z = -1; + break; + + case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections + const unsigned int but = disp.button(); + const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4); + + if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step) + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; + } + if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes) + switch (area_started) { + case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break; + case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break; + case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break; + } + } + if (b2 && area_clicked==area) { // When moving through the image/volume + if (phase) { + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; + } else { + if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign(); + X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; + } + } + if (b3) { // Reset selection + X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0; + visu0.assign(); + } + if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel) + if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && + !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) { + switch (area) { + case 1 : + if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); + visu0.assign(); break; + case 2 : + if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel()); + visu0.assign(); break; + case 3 : + if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel()); + visu0.assign(); break; + } + disp.set_wheel(); + } else key = ~0U; + } + + if ((phase==0 && b1) || + (phase==1 && !b1) || + (phase==2 && b1)) switch (phase) { // Detect change of phase + case 0 : + if (area==area_clicked) { + X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase; + } break; + case 1 : + if (area==area_started) { + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; + if (_depth>1) { + if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default; + if (is_deep_selection) ++phase; + } + } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } + break; + case 2 : ++phase; break; + } + } break; + + case 4 : // When mouse is over the 3D view + if (is_view3d && points3d) { + X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0)); + Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0)); + if (oX3d<0) { oX3d = X3d; oY3d = Y3d; } + // Left + right buttons: reset. + if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } + else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate + const float + R = 0.45f*std::min(view3d._width,view3d._height), + R2 = R*R, + u0 = (float)(oX3d - view3d.width()/2), + v0 = (float)(oY3d - view3d.height()/2), + u1 = (float)(X3d - view3d.width()/2), + v1 = (float)(Y3d - view3d.height()/2), + n0 = cimg::hypot(u0,v0), + n1 = cimg::hypot(u1,v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), + u = nv0*nw1 - nw0*nv1, + v = nw0*nu1 - nu0*nw1, + w = nv0*nu1 - nu0*nv1, + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + pose3d.draw_image(CImg::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2)); + view3d.assign(); + } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom + pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign(); + } + if (disp.wheel()) { // Wheel: zoom + pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel(); + } + if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift + pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign(); + } + oX3d = X3d; oY3d = Y3d; + } + mx = my = -1; X = Y = Z = -1; + break; + } + + if (phase) { + if (!feature_type) shape_selected = phase?true:false; + else { + if (_depth>1) shape_selected = (phase==3)?true:false; + else shape_selected = (phase==2)?true:false; + } + } + + if (X0<0) X0 = 0; + if (X0>=width()) X0 = width() - 1; + if (Y0<0) Y0 = 0; + if (Y0>=height()) Y0 = height() - 1; + if (Z0<0) Z0 = 0; + if (Z0>=depth()) Z0 = depth() - 1; + if (X1<1) X1 = 0; + if (X1>=width()) X1 = width() - 1; + if (Y1<0) Y1 = 0; + if (Y1>=height()) Y1 = height() - 1; + if (Z1<0) Z1 = 0; + if (Z1>=depth()) Z1 = depth() - 1; + + // Draw visualization image on the display + if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { + + if (!visu0) { // Create image of projected planes + if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + visu0.resize(disp); + view3d.assign(); + points3d.assign(); + } + + if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images + const unsigned int + _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1), + _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1), + x3d = _x3d>=visu0._width?visu0._width - 1:_x3d, + y3d = _y3d>=visu0._height?visu0._height - 1:_y3d; + CImg(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3). + move_to(view3d); + if (!points3d) { + get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d); + points3d.append(CImg(8,3,1,1, + 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0, + 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1, + 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x'); + CImg::vector(12,13).move_to(primitives3d); CImg::vector(13,14).move_to(primitives3d); + CImg::vector(14,15).move_to(primitives3d); CImg::vector(15,12).move_to(primitives3d); + CImg::vector(16,17).move_to(primitives3d); CImg::vector(17,18).move_to(primitives3d); + CImg::vector(18,19).move_to(primitives3d); CImg::vector(19,16).move_to(primitives3d); + CImg::vector(12,16).move_to(primitives3d); CImg::vector(13,17).move_to(primitives3d); + CImg::vector(14,18).move_to(primitives3d); CImg::vector(15,19).move_to(primitives3d); + colors3d.insert(12,CImg::vector(255,255,255)); + opacities3d.assign(primitives3d.width(),1,1,1,0.5f); + if (!phase) { + opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f; + sel_primitives3d.assign(); + sel_colors3d.assign(); + sel_opacities3d.assign(); + } else { + if (feature_type==2) { + points3d.append(CImg(8,3,1,1, + X0,X1,X1,X0,X0,X1,X1,X0, + Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1, + Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x'); + sel_primitives3d.assign(); + CImg::vector(20,21).move_to(sel_primitives3d); + CImg::vector(21,22).move_to(sel_primitives3d); + CImg::vector(22,23).move_to(sel_primitives3d); + CImg::vector(23,20).move_to(sel_primitives3d); + CImg::vector(24,25).move_to(sel_primitives3d); + CImg::vector(25,26).move_to(sel_primitives3d); + CImg::vector(26,27).move_to(sel_primitives3d); + CImg::vector(27,24).move_to(sel_primitives3d); + CImg::vector(20,24).move_to(sel_primitives3d); + CImg::vector(21,25).move_to(sel_primitives3d); + CImg::vector(22,26).move_to(sel_primitives3d); + CImg::vector(23,27).move_to(sel_primitives3d); + } else { + points3d.append(CImg(2,3,1,1, + X0,X1, + Y0,Y1, + Z0,Z1),'x'); + sel_primitives3d.assign(CImg::vector(20,21)); + } + sel_colors3d.assign(sel_primitives3d._width,CImg::vector(255,255,255)); + sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f); + } + points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d(); + points3d*=0.75f*std::min(view3d._width,view3d._height); + } + + if (!pose3d) CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d); + CImg zbuffer3d(view3d._width,view3d._height,1,1,0); + const CImg rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d; + if (sel_primitives3d) + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d, + 2,true,500,0,0,0,0,0,1,zbuffer3d); + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,primitives3d,colors3d,opacities3d, + 2,true,500,0,0,0,0,0,1,zbuffer3d); + visu0.draw_image(x3d,y3d,view3d); + } + visu = visu0; + + if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} + else { + if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }} + else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} + const int d = (depth()>1)?depth():0; + int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z; + if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; } + int + w = disp.width(), W = width() + d, + h = disp.height(), H = height() + d, + _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX), + _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY), + _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1), + _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1), + _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()), + _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()), + _zxn = (int)((_vZ + width() + 1.f)*w/W - 1), + zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1), + _zyn = (int)((_vZ + height() + 1.f)*h/H - 1), + zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1), + _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()), + _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()), + xc = (xp + xn)/2, + yc = (yp + yn)/2, + zxc = (zxp + zxn)/2, + zyc = (zyp + zyn)/2, + xf = (int)(X*w/W), + yf = (int)(Y*h/H), + zxf = (int)((Z + width())*w/W), + zyf = (int)((Z + height())*h/H); + + if (is_axes) { // Draw axes + visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00). + draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF). + draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00). + draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF); + if (_depth>1) + visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00). + draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF). + draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00). + draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF); + } + + // Draw box cursor. + if (xn - xp>=4 && yn - yp>=4) + visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). + draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); + if (_depth>1) { + if (yn - yp>=4 && zxn - zxp>=4) + visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). + draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); + if (xn - xp>=4 && zyn - zyp>=4) + visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). + draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); + } + + // Draw selection. + if (phase && (phase!=1 || area_started==area)) { + const int + _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0), + _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0), + _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1), + _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1), + _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()), + _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()), + _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1), + zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1), + _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1), + zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1), + xc0 = (xp0 + xn0)/2, + yc0 = (yp0 + yn0)/2, + zxc0 = (zxp0 + zxn0)/2, + zyc0 = (zyp0 + zyn0)/2; + + switch (feature_type) { + case 1 : { // Vector + visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333). + draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC); + if (d) { + visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333). + draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC). + draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333). + draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC); + } + } break; + case 2 : { // Box + visu.draw_rectangle(X0=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false; + if (!feature_type || !phase) { + if (X>=0 && Y>=0 && Z>=0 && X1 || force_display_z_coord) + cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z); + else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y); + CImg values = get_vector_at((int)X,(int)Y,(int)Z); + const bool is_large_spectrum = values._height>8; + if (is_large_spectrum) + values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0); + char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; + for (unsigned int c = 0; c::format_s(), + cimg::type::format(values[c])); + ctext += std::strlen(ctext); + if (c==3 && is_large_spectrum) { + cimg_snprintf(ctext,24," ..."); + ctext += std::strlen(ctext); + } + *(ctext++) = ' '; *ctext = 0; + } + std::strcpy(text._data + std::strlen(text),"] "); + } + } else switch (feature_type) { + case 1 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length); + else if (_width!=1 && _height!=1) + cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length, + cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1)); + else + cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length); + } break; + case 2 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width, + " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ", + origX + (X01 || force_display_z_coord) + cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1, + 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1)); + else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ", + origX + X0,origY + Y0,origX + X1,origY + Y1, + 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1)); + } + if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data); + } + + disp.display(visu); + } + if (!shape_selected) disp.wait(); + if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } + omx = mx; omy = my; + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + + // Return result. + CImg res(1,feature_type==0?3:6,1,1,-1); + if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } + if (shape_selected) { + if (feature_type==2) { + if (is_deep_selection) switch (area_started) { + case 1 : Z0 = 0; Z1 = _depth - 1; break; + case 2 : Y0 = 0; Y1 = _height - 1; break; + case 3 : X0 = 0; X1 = _width - 1; break; + } + if (X0>X1) cimg::swap(X0,X1); + if (Y0>Y1) cimg::swap(Y0,Y1); + if (Z0>Z1) cimg::swap(Z0,Z1); + } + if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; + switch (feature_type) { + case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; + case 3 : + res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); + res[0] = X0; res[1] = Y0; res[2] = Z0; + break; + default : res[0] = X0; res[1] = Y0; res[2] = Z0; + } + } + if (!exit_on_anykey || !(disp.button()&4)) disp.set_button(); + if (!visible_cursor) disp.show_mouse(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + if (key!=~0U) disp.set_key(key); + return res; + } + + // Return a visualizable uchar8 image for display routines. + CImg _get_select(const CImgDisplay& disp, const int normalization, + const int x, const int y, const int z) const { + if (is_empty()) return CImg(1,1,1,1,0); + const CImg crop = get_shared_channels(0,std::min(2,spectrum() - 1)); + CImg img2d; + if (_depth>1) { + const int mdisp = std::min(disp.screen_width(),disp.screen_height()); + if (depth()>mdisp) { + crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d); + img2d.projections2d(x,y,z*img2d._depth/_depth); + } else crop.get_projections2d(x,y,z).move_to(img2d); + } else CImg(crop,false).move_to(img2d); + + // Check for inf and NaN values. + if (cimg::type::is_float() && normalization) { + bool is_inf = false, is_nan = false; + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) { is_inf = true; break; } + else if (cimg::type::is_nan(*ptr)) { is_nan = true; break; } + if (is_inf || is_nan) { + Tint m0 = (Tint)cimg::type::max(), M0 = (Tint)cimg::type::min(); + if (!normalization) { m0 = 0; M0 = 255; } + else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; } + else + cimg_for(img2d,ptr,Tuchar) + if (!cimg::type::is_inf(*ptr) && !cimg::type::is_nan(*ptr)) { + if (*ptr<(Tuchar)m0) m0 = *ptr; + if (*ptr>(Tuchar)M0) M0 = *ptr; + } + const T + val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0), + val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0); + if (is_nan) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values + if (is_inf) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values + } + } + + switch (normalization) { + case 1 : img2d.normalize((ucharT)0,(ucharT)255); break; + case 2 : { + const float m = disp._min, M = disp._max; + (img2d-=m)*=255.f/(M - m>0?M - m:1); + } break; + case 3 : + if (cimg::type::is_float()) img2d.normalize((ucharT)0,(ucharT)255); + else { + const float + m = (float)cimg::type::min(), + M = (float)cimg::type::max(); + (img2d-=m)*=255.f/(M - m>0?M - m:1); + } break; + } + if (img2d.spectrum()==2) img2d.channels(0,2); + return img2d; + } + + //! Select sub-graph in a graph. + CImg get_select_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "select_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). + set_title("CImg<%s>",pixel_type()); + const ulongT siz = (ulongT)_width*_height*_depth; + const unsigned int old_normalization = disp.normalization(); + disp.show().set_button().set_wheel()._normalization = 0; + + double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; + if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; } + if (nymin==nymax) { --nymin; ++nymax; } + if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; } + + static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; + static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; + + CImg colormap(3,_spectrum); + if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; } + else { + colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10; + if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; } + if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; } + if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10; } + if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10; colormap(2,4) = 220; } + if (_spectrum>5) { colormap(0,5) = 10; colormap(1,5) = 220; colormap(2,5) = 220; } + if (_spectrum>6) { + cimg_uint64 rng = 10; + cimg_for_inY(colormap,6,colormap.height()-1,k) { + colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + } + } + } + + CImg visu0, visu, graph, text, axes; + int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; + const unsigned int one = plot_type==3?0U:1U; + unsigned int okey = 0, obutton = 0, font_size = 32; + CImg message(1024); + CImg_3x3(I,unsigned char); + + for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) { + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + const unsigned int key = disp.key(), button = disp.button(); + + // Generate graph representation. + if (!visu0) { + visu0.assign(disp.width(),disp.height(),1,3,220); + const int gdimx = disp.width() - 32, gdimy = disp.height() - 32; + if (gdimx>0 && gdimy>0) { + graph.assign(gdimx,gdimy,1,3,255); + if (siz<32) { + if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0, + false,true,black,0.2f,0x33333333,0x33333333); + } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); + cimg_forC(*this,c) + graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f, + plot_type,vertex_type,nymax,nymin); + + axes.assign(gdimx,gdimy,1,1,0); + const float + dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin), + px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.), + py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.); + const CImg + seqx = dx<=0?CImg::vector(nxmin): + CImg::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz), + seqy = CImg::sequence(1 + gdimy/60,nymax,nymin); + + const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0); + axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py); + if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px); + if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px); + if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py); + if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py); + + cimg_for3x3(axes,x,y,0,0,I,unsigned char) + if (Icc) { + if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0; + else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3); + } + else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) + cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3); + + visu0.draw_image(16,16,graph); + visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2). + draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white); + } else graph.assign(); + text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3); + visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text); + text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3); + visu0.draw_image(1,(visu0.height() - text.height())/2,~text); + visu.assign(); + } + + // Generate and display current view. + if (!visu) { + visu.assign(visu0); + if (graph && x0>=0 && x1>=0) { + const int + nx0 = x0<=x1?x0:x1, + nx1 = x0<=x1?x1:x0, + ny0 = y0<=y1?y0:y1, + ny1 = y0<=y1?y1:y0, + sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)), + sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)), + sy0 = 16 + ny0, + sy1 = 16 + ny1; + if (y0>=0 && y1>=0) + visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU); + else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f). + draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU). + draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU); + } + if (mouse_x>=16 && mouse_y>=16 && mouse_x=7) + cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx, + (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), + (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3), + (double)(*this)(x,0,0,_spectrum - 1)); + else { + cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx); + cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); + cimg_sprintf(message._data + std::strlen(message),")"); + } + if (x0>=0 && x1>=0) { + const unsigned int + nx0 = (unsigned int)(x0<=x1?x0:x1), + nx1 = (unsigned int)(x0<=x1?x1:x0), + ny0 = (unsigned int)(y0<=y1?y0:y1), + ny1 = (unsigned int)(y0<=y1?y1:y0); + const double + cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), + cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), + cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32), + cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32); + if (y0>=0 && y1>=0) + cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + x0,cx0,cy0,x1 + one,cx1,cy1); + else + cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", + x0,cx0,x1 + one,cx1); + } + text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); + visu.draw_image((visu.width() - text.width())/2,1,~text); + } + visu.display(disp); + } + + // Test keys. + CImg filename(32); + switch (okey = key) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + CImgDisplay::screen_height()/2,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp); + screen.save(filename); + (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp); + save(filename); + (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + } + + // Handle mouse motion and mouse buttons. + if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) { + visu.assign(); + if (disp.mouse_x()>=0 && disp.mouse_y()>=0) { + const int + mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32), + cx = cimg::cut(mx,0,(int)(siz - 1 - one)), + my = mouse_y - 16, + cy = cimg::cut(my,0,disp.height() - 32); + if (button&1) { + if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; } + } + else if (button&2) { + if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; } + } + else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; } + } else if (!button && obutton) selected = true; + obutton = button; omouse_x = mouse_x; omouse_y = mouse_y; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (visu && visu0) disp.wait(); + if (!exit_on_anykey && okey && okey!=cimg::keyESC && + (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + okey = 0; + } + } + + disp._normalization = old_normalization; + if (x1>=0 && x1(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1); + } + + //! Load image from a file. + /** + \param filename Filename, as a C-string. + \note The extension of \c filename defines the file format. If no filename + extension is provided, CImg::get_load() will try to load the file as a .cimg or .cimgz file. + **/ + CImg& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load(): Specified filename is (null).", + cimg_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg filename_local(256); + load(cimg::load_network(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + bool is_loaded = true; + try { +#ifdef cimg_load_plugin + cimg_load_plugin(filename); +#endif +#ifdef cimg_load_plugin1 + cimg_load_plugin1(filename); +#endif +#ifdef cimg_load_plugin2 + cimg_load_plugin2(filename); +#endif +#ifdef cimg_load_plugin3 + cimg_load_plugin3(filename); +#endif +#ifdef cimg_load_plugin4 + cimg_load_plugin4(filename); +#endif +#ifdef cimg_load_plugin5 + cimg_load_plugin5(filename); +#endif +#ifdef cimg_load_plugin6 + cimg_load_plugin6(filename); +#endif +#ifdef cimg_load_plugin7 + cimg_load_plugin7(filename); +#endif +#ifdef cimg_load_plugin8 + cimg_load_plugin8(filename); +#endif + // Text formats + if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); + else if (!cimg::strcasecmp(ext,"csv") || + !cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) load_dlm(filename); + else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename); + + // 2D binary formats + else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); + else if (!cimg::strcasecmp(ext,"png")) load_png(filename); + else if (!cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"pnm") || + !cimg::strcasecmp(ext,"pbm") || + !cimg::strcasecmp(ext,"pnk")) load_pnm(filename); + else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename); + else if (!cimg::strcasecmp(ext,"cr2") || + !cimg::strcasecmp(ext,"crw") || + !cimg::strcasecmp(ext,"dcr") || + !cimg::strcasecmp(ext,"mrw") || + !cimg::strcasecmp(ext,"nef") || + !cimg::strcasecmp(ext,"orf") || + !cimg::strcasecmp(ext,"pix") || + !cimg::strcasecmp(ext,"ptx") || + !cimg::strcasecmp(ext,"raf") || + !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"heic") || + !cimg::strcasecmp(ext,"avif")) load_heif(filename); + + // 3D binary formats + else if (!cimg::strcasecmp(ext,"dcm") || + !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) load_analyze(filename); + else if (!cimg::strcasecmp(ext,"par") || + !cimg::strcasecmp(ext,"rec")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename); + else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) return load_cimg(filename); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + + // Image sequences + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + + // If nothing loaded, try to guess file format from magic number in file. + if (!is_loaded) { + std::FILE *file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load(): Failed to open file '%s'.", + cimg_instance, + filename); + } + + const char *const f_type = cimg::ftype(file,filename); + cimg::fclose(file); + is_loaded = true; + try { + if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename); + else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename); + else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(f_type,"png")) load_png(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + } + + // If nothing loaded, try to load file with other means. + if (!is_loaded) { + try { + load_other(filename); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load image from a file \newinstance. + static CImg get_load(const char *const filename) { + return CImg().load(filename); + } + + //! Load image from an ascii file. + /** + \param filename Filename, as a C -string. + **/ + CImg& load_ascii(const char *const filename) { + return _load_ascii(0,filename); + } + + //! Load image from an ascii file \inplace. + static CImg get_load_ascii(const char *const filename) { + return CImg().load_ascii(filename); + } + + //! Load image from an ascii file \overloading. + CImg& load_ascii(std::FILE *const file) { + return _load_ascii(file,0); + } + + //! Loadimage from an ascii file \newinstance. + static CImg get_load_ascii(std::FILE *const file) { + return CImg().load_ascii(file); + } + + CImg& _load_ascii(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_ascii(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg line(256); *line = 0; + int err = std::fscanf(nfile,"%255[^\n]",line._data); + unsigned int dx = 0, dy = 1, dz = 1, dc = 1; + cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); + err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]"); + if (!dx || !dy || !dz || !dc) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_ascii(): Invalid ascii header in file '%s', image dimensions are set " + "to (%u,%u,%u,%u).", + cimg_instance, + filename?filename:"(FILE*)",dx,dy,dz,dc); + } + assign(dx,dy,dz,dc); + const ulongT siz = size(); + ulongT off = 0; + double val; + T *ptr = _data; + for (err = 1, off = 0; off& load_dlm(const char *const filename) { + return _load_dlm(0,filename); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(const char *const filename) { + return CImg().load_dlm(filename); + } + + //! Load image from a DLM file \overloading. + CImg& load_dlm(std::FILE *const file) { + return _load_dlm(file,0); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(std::FILE *const file) { + return CImg().load_dlm(file); + } + + CImg& _load_dlm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_dlm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + CImg delimiter(256), tmp(256); *delimiter = *tmp = 0; + unsigned int cdx = 0, dx = 0, dy = 0; + int err = 0; + double val; + assign(256,256,1,1,(T)0); + while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) { + if (err>0) (*this)(cdx++,dy) = (T)val; + if (cdx>=_width) resize(3*_width/2,_height,1,1,0); + char c = 0; + if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') { + dx = std::max(cdx,dx); + if (++dy>=_height) resize(_width,3*_height/2,1,1,0); + cdx = 0; + } + } + if (cdx && err==1) { dx = cdx; ++dy; } + if (!dx || !dy) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_dlm(): Invalid DLM file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + resize(dx,dy,1,1,0); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a BMP file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_bmp(const char *const filename) { + return _load_bmp(0,filename); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(const char *const filename) { + return CImg().load_bmp(filename); + } + + //! Load image from a BMP file \overloading. + CImg& load_bmp(std::FILE *const file) { + return _load_bmp(file,0); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(std::FILE *const file) { + return CImg().load_bmp(file); + } + + CImg& _load_bmp(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_bmp(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg header(54); + cimg::fread(header._data,54,nfile); + if (*header!='B' || header[1]!='M') { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid BMP file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read header and pixel buffer + int + file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), + offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), + header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24), + dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), + dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), + compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), + nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), + bpp = header[0x1C] + (header[0x1D]<<8); + + if (!file_size || file_size==offset) { + cimg::fseek(nfile,0,SEEK_END); + file_size = (int)cimg::ftell(nfile); + cimg::fseek(nfile,54,SEEK_SET); + } + if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); + + const int + dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)), + align_bytes = (4 - dx_bytes%4)%4; + const ulongT + cimg_iobuffer = (ulongT)24*1024*1024, + buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes); + + CImg colormap; + if (bpp<16) { if (!nb_colors) nb_colors = 1<0) cimg::fseek(nfile,xoffset,SEEK_CUR); + + CImg buffer; + if (buf_size=2) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + unsigned char mask = 0x80, val = 0; + cimg_forX(*this,x) { + if (mask==0x80) val = *(ptrs++); + const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask); + } + ptrs+=align_bytes; + } + } break; + case 4 : { // 16 colors + if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + unsigned char mask = 0xF0, val = 0; + cimg_forX(*this,x) { + if (mask==0xF0) val = *(ptrs++); + const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); + const unsigned char *col = (unsigned char*)(colormap._data + color); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask,4); + } + ptrs+=align_bytes; + } + } break; + case 8 : { // 256 colors + if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + } + ptrs+=align_bytes; + } + } break; + case 16 : { // 16 bits colors (RGB565) + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); + const unsigned short col = (unsigned short)c2<<8 | c1; + (*this)(x,y,2) = (T)((col&0x1F)<<3); + (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3); + (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3); + } + ptrs+=align_bytes; + } + } break; + case 24 : { // 24 bits colors + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + } + ptrs+=align_bytes; + } + } break; + case 32 : { // 32 bits colors + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + ++ptrs; + } + ptrs+=align_bytes; + } + } break; + } + if (dy<0) mirror('y'); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a JPEG file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_jpeg(const char *const filename) { + return _load_jpeg(0,filename); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(const char *const filename) { + return CImg().load_jpeg(filename); + } + + //! Load image from a JPEG file \overloading. + CImg& load_jpeg(std::FILE *const file) { + return _load_jpeg(file,0); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(std::FILE *const file) { + return CImg().load_jpeg(file); + } + + // Custom error handler for libjpeg. +#ifdef cimg_use_jpeg + struct _cimg_error_mgr { + struct jpeg_error_mgr original; + jmp_buf setjmp_buffer; + char message[JMSG_LENGTH_MAX]; + }; + + typedef struct _cimg_error_mgr *_cimg_error_ptr; + + METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) { + _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point + (*cinfo->err->format_message)(cinfo,c_err->message); + jpeg_destroy(cinfo); // Clean memory and temp files + longjmp(c_err->setjmp_buffer,1); + } +#endif + + CImg& _load_jpeg(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_jpeg(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_jpeg + if (file) + throw CImgIOException(_cimg_instance + "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.", + cimg_instance); + else return load_other(filename); +#else + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + struct jpeg_decompress_struct cinfo; + struct _cimg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr.original); + jerr.original.error_exit = _cimg_jpeg_error_exit; + if (setjmp(jerr.setjmp_buffer)) { // JPEG error + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_jpeg(): Error message returned by libjpeg: %s.", + cimg_instance,jerr.message); + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo,nfile); + jpeg_read_header(&cinfo,TRUE); + jpeg_start_decompress(&cinfo); + + if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { + if (!file) { + cimg::fclose(nfile); + return load_other(filename); + } else + throw CImgIOException(_cimg_instance + "load_jpeg(): Failed to load JPEG data from file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + } + CImg buffer(cinfo.output_width*cinfo.output_components); + JSAMPROW row_pointer[1]; + try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); } + catch (...) { if (!file) cimg::fclose(nfile); throw; } + T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, + *ptr_a = _data + 3UL*_width*_height; + while (cinfo.output_scanline + // This is experimental code, not much tested, use with care. + CImg& load_magick(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_magick(): Specified filename is (null).", + cimg_instance); + +#ifdef cimg_use_magick + Magick::Image image(filename); + const unsigned int W = image.size().width(), H = image.size().height(); + switch (image.type()) { + case Magick::PaletteMatteType : + case Magick::TrueColorMatteType : + case Magick::ColorSeparationType : { + assign(W,H,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + case Magick::PaletteType : + case Magick::TrueColorType : { + assign(W,H,1,3); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + ++pixels; + } + } break; + case Magick::GrayscaleMatteType : { + assign(W,H,1,2); + T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + default : { + assign(W,H,1,1); + T *ptr_r = data(0,0,0,0); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + ++pixels; + } + } + } + return *this; +#else + throw CImgIOException(_cimg_instance + "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + } + + //! Load image from a file, using Magick++ library \newinstance. + static CImg get_load_magick(const char *const filename) { + return CImg().load_magick(filename); + } + + //! Load image from a PNG file. + /** + \param filename Filename, as a C-string. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + **/ + CImg& load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return _load_png(0,filename,bits_per_value); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return CImg().load_png(filename,bits_per_value); + } + + //! Load image from a PNG file \overloading. + CImg& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return _load_png(file,0,bits_per_value); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return CImg().load_png(file,bits_per_value); + } + + // (Note: Most of this function has been written by Eric Fausett) + CImg& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_png(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_png + cimg::unused(bits_per_value); + if (file) + throw CImgIOException(_cimg_instance + "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.", + cimg_instance); + + else return load_other(filename); +#else + // Open file and check for PNG validity +#if defined __GNUC__ + const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); +#else + const char *nfilename = filename; + std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb"); +#endif + unsigned char pngCheck[8] = { 0 }; + cimg::fread(pngCheck,8,(std::FILE*)nfile); + if (png_sig_cmp(pngCheck,0,8)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Invalid PNG file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Setup PNG structures for read + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn); + if (!png_ptr) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'end_info' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Error handling callback for png file reading + if (setjmp(png_jmpbuf(png_ptr))) { + if (!file) cimg::fclose((std::FILE*)nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Encountered unknown fatal error in libpng for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + png_set_sig_bytes(png_ptr, 8); + + // Get PNG Header Info up to data block + png_read_info(png_ptr,info_ptr); + png_uint_32 W, H; + int bit_depth, color_type, interlace_type; + bool is_gray = false; + png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0); + if (bits_per_value) *bits_per_value = (unsigned int)bit_depth; + + // Transforms to unify image data + if (color_type==PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + color_type = PNG_COLOR_TYPE_RGB; + bit_depth = 8; + } + if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + is_gray = true; + bit_depth = 8; + } + if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + color_type |= PNG_COLOR_MASK_ALPHA; + } + if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + color_type |= PNG_COLOR_MASK_COLOR; + is_gray = true; + } + if (color_type==PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER); + + png_read_update_info(png_ptr,info_ptr); + if (bit_depth!=8 && bit_depth!=16) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Invalid bit depth %u in file '%s'.", + cimg_instance, + bit_depth,nfilename?nfilename:"(FILE*)"); + } + const int byte_depth = bit_depth>>3; + + // Allocate memory for image reading + png_bytep *const imgData = new png_bytep[H]; + for (unsigned int row = 0; row& load_pnm(const char *const filename) { + return _load_pnm(0,filename); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(const char *const filename) { + return CImg().load_pnm(filename); + } + + //! Load image from a PNM file \overloading. + CImg& load_pnm(std::FILE *const file) { + return _load_pnm(file,0); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(std::FILE *const file) { + return CImg().load_pnm(file); + } + + CImg& _load_pnm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pnm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned int ppm_type, W, H, D = 1, colormax = 255; + CImg item(16384,1,1,1,0); + int err, rval, gval, bval; + const longT cimg_iobuffer = (longT)24*1024*1024; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item," P%u",&ppm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (ppm_type!=1 && ppm_type!=4) { + if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item,"%u",&colormax)!=1) + cimg::warn(_cimg_instance + "load_pnm(): COLORMAX field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else { colormax = D; D = 1; } + } + std::fgetc(nfile); + + if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension + const cimg_int64 siz = cimg::fsize(filename); + if (W*H*D>siz) + throw CImgIOException(_cimg_instance + "load_pnm(): Specified image dimensions in file '%s' exceed file size.", + cimg_instance, + filename); + } + + switch (ppm_type) { + case 1 : { // 2D B&W ascii + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; } + } break; + case 2 : { // 2D grey ascii + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; } + } break; + case 3 : { // 2D color ascii + assign(W,H,1,3); + T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forXY(*this,x,y) { + if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { + *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; + } else break; + } + } break; + case 4 : { // 2D b&w binary (support 3D PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + unsigned int w = 0, h = 0, d = 0; + for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + unsigned char mask = 0, val = 0; + for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) { + if (!mask) { if (off--) val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?0:255); + if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }} + } + } + } break; + case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension) + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } else { // 16 bits + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } + } break; + case 6 : { // 2D color binary + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { // 16 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } + } break; + case 8 : { // 2D/3D grey binary with int32 integers (PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const int *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + case 9 : { // 2D/3D grey binary with float values (PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const float *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + default : + assign(); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM type 'P%d' found, but type is not supported.", + cimg_instance, + filename?filename:"(FILE*)",ppm_type); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PFM file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pfm(const char *const filename) { + return _load_pfm(0,filename); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(const char *const filename) { + return CImg().load_pfm(filename); + } + + //! Load image from a PFM file \overloading. + CImg& load_pfm(std::FILE *const file) { + return _load_pfm(file,0); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(std::FILE *const file) { + return CImg().load_pfm(file); + } + + CImg& _load_pfm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pfm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char pfm_type; + CImg item(16384,1,1,1,0); + int W = 0, H = 0, err = 0; + double scale = 0; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item," P%c",&pfm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): PFM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else if (W<=0 || H<=0) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.", + cimg_instance,W,H, + filename?filename:"(FILE*)"); + } + if (err==2) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item,"%lf",&scale)!=1) + cimg::warn(_cimg_instance + "load_pfm(): SCALE field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + std::fgetc(nfile); + const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness(); + if (is_color) { + assign(W,H,1,3,(T)0); + CImg buf(3*W); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forY(*this,y) { + cimg::fread(buf._data,3*W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,3*W); + const float *ptrs = buf._data; + cimg_forX(*this,x) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { + assign(W,H,1,1,(T)0); + CImg buf(W); + T *ptrd = data(0,0,0,0); + cimg_forY(*this,y) { + cimg::fread(buf._data,W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,W); + const float *ptrs = buf._data; + cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return mirror('y'); // Most of the .pfm files are flipped along the y-axis + } + + //! Load image from a RGB file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(0,filename,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(filename,dimw,dimh); + } + + //! Load image from a RGB file \overloading. + CImg& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(file,0,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(file,dimw,dimh); + } + + CImg& _load_rgb(std::FILE *const file, const char *const filename, + const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgb(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const longT cimg_iobuffer = (longT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = raw._width/3UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a RGBA file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(0,filename,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(filename,dimw,dimh); + } + + //! Load image from a RGBA file \overloading. + CImg& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(file,0,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(file,dimw,dimh); + } + + CImg& _load_rgba(std::FILE *const file, const char *const filename, + const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgba(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const longT cimg_iobuffer = (longT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,4); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2), + *ptr_a = data(0,0,0,3); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = raw._width/4UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a TIFF file. + /** + \param filename Filename, as a C-string. + \param first_frame First frame to read (for multi-pages tiff). + \param last_frame Last frame to read (for multi-pages tiff). + \param step_frame Step value of frame reading. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + \param[out] voxel_size Voxel size, as stored in the filename. + \param[out] description Description, as stored in the filename. + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tiff is not defined at compile time the + function uses CImg& load_other(const char*). + **/ + CImg& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Specified filename is (null).", + cimg_instance); + + const unsigned int + nfirst_frame = first_frame1) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.", + cimg_instance, + filename); + return load_other(filename); +#else +#if cimg_verbosity<3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn(_cimg_instance + "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).", + cimg_instance, + filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + TIFFSetDirectory(tif,0); + CImg frame; + for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { + frame._load_tiff(tif,l,bits_per_value,voxel_size,description); + if (l==nfirst_frame) + assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum); + if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) + resize(std::max(frame._width,_width), + std::max(frame._height,_height),-100, + std::max(frame._spectrum,_spectrum),0); + draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame); + } + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "load_tiff(): Failed to open file '%s'.", + cimg_instance, + filename); + return *this; +#endif + } + + //! Load image from a TIFF file \newinstance. + static CImg get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); + } + + // (Original contribution by Jerome Boulanger). +#ifdef cimg_use_tiff + template + void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, + const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int row = 0; row + void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, + const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int vv = 0; vv + void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (row = 0; rowny?ny - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0; rr + void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (unsigned int vv = 0; vvny?ny - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0;rr& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value, + float *const voxel_size, CImg *const description) { + if (!TIFFSetDirectory(tif,directory)) return assign(); + uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; + uint16 sampleformat = 1; + uint32 nx = 1, ny = 1; + const char *const filename = TIFFFileName(tif); + const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); + TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); + TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); + TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); + TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); + TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + if (bits_per_value) *bits_per_value = (unsigned int)bitspersample; + if (voxel_size) { + const char *s_description = 0; + float vx = 0, vy = 0, vz = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) { + const char *s_desc = std::strstr(s_description,"VX="); + if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format + voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz; + } + s_desc = std::strstr(s_description,"spacing="); + if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format + voxel_size[2] = vz; + } + } + TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size); + TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1); + voxel_size[0] = 1.f/voxel_size[0]; + voxel_size[1] = 1.f/voxel_size[1]; + } + if (description) { + const char *s_description = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) + CImg::string(s_description).move_to(*description); + } + const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; + assign(nx,ny,1,spectrum); + + if ((photo>=3 && sampleformat==1 && + (bitspersample==4 || bitspersample==8) && + (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) || + (bitspersample==1 && samplesperpixel==1)) { + // Special case for unsigned color images. + uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32)); + if (!raster) { + _TIFFfree(raster); TIFFClose(tif); + throw CImgException(_cimg_instance + "load_tiff(): Failed to allocate memory (%s) for file '%s'.", + cimg_instance, + cimg::strbuffersize(nx*ny*sizeof(uint32)),filename); + } + TIFFReadRGBAImage(tif,nx,ny,raster,0); + switch (spectrum) { + case 1 : + cimg_forXY(*this,x,y) + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); + break; + case 3 : + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); + } + break; + case 4 : + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]); + } + break; + } + _TIFFfree(raster); + } else { // Other cases + uint16 config; + TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); + if (TIFFIsTiled(tif)) { + uint32 tw = 1, th = 1; + TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); + TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + } + } else { + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + } + } + } + return *this; + } +#endif + + //! Load image from a MINC2 file. + /** + \param filename Filename, as a C-string. + **/ + // (Original code by Haz-Edine Assemlal). + CImg& load_minc2(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_minc2(): Specified filename is (null).", + cimg_instance); +#ifndef cimg_use_minc2 + return load_other(filename); +#else + minc::minc_1_reader rdr; + rdr.open(filename); + assign(rdr.ndim(1)?rdr.ndim(1):1, + rdr.ndim(2)?rdr.ndim(2):1, + rdr.ndim(3)?rdr.ndim(3):1, + rdr.ndim(4)?rdr.ndim(4):1); + if (pixel_type()==cimg::type::string()) + rdr.setup_read_byte(); + else if (pixel_type()==cimg::type::string()) + rdr.setup_read_int(); + else if (pixel_type()==cimg::type::string()) + rdr.setup_read_double(); + else + rdr.setup_read_float(); + minc::load_standard_volume(rdr,this->_data); + return *this; +#endif + } + + //! Load image from a MINC2 file \newinstance. + static CImg get_load_minc2(const char *const filename) { + return CImg().load_analyze(filename); + } + + //! Load image from an ANALYZE7.5/NIFTI file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_analyze(const char *const filename, float *const voxel_size=0) { + return _load_analyze(0,filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(const char *const filename, float *const voxel_size=0) { + return CImg().load_analyze(filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \overloading. + CImg& load_analyze(std::FILE *const file, float *const voxel_size=0) { + return _load_analyze(file,0,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(std::FILE *const file, float *const voxel_size=0) { + return CImg().load_analyze(file,voxel_size); + } + + CImg& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_analyze(): Specified filename is (null).", + cimg_instance); + + std::FILE *nfile_header = 0, *nfile = 0; + if (!file) { + CImg body(1024); + const char *const ext = cimg::split_filename(filename,body); + if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file + nfile_header = cimg::fopen(filename,"rb"); + cimg_sprintf(body._data + std::strlen(body),".img"); + nfile = cimg::fopen(body,"rb"); + } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file + nfile = cimg::fopen(filename,"rb"); + cimg_sprintf(body._data + std::strlen(body),".hdr"); + nfile_header = cimg::fopen(body,"rb"); + } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file + } else nfile_header = nfile = file; // File is a Niftii file + if (!nfile || !nfile_header) + throw CImgIOException(_cimg_instance + "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + // Read header. + bool endian = false; + unsigned int header_size; + cimg::fread(&header_size,1,nfile_header); + if (!header_size) + throw CImgIOException(_cimg_instance + "load_analyze(): Invalid zero-size header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } + + unsigned char *const header = new unsigned char[header_size]; + cimg::fread(header + 4,header_size - 4,nfile_header); + if (!file && nfile_header!=nfile) cimg::fclose(nfile_header); + if (endian) { + cimg::invert_endianness((short*)(header + 40),5); + cimg::invert_endianness((short*)(header + 70),1); + cimg::invert_endianness((short*)(header + 72),1); + cimg::invert_endianness((float*)(header + 76),4); + cimg::invert_endianness((float*)(header + 108),1); + cimg::invert_endianness((float*)(header + 112),1); + } + + if (nfile_header==nfile) { + const unsigned int vox_offset = (unsigned int)*(float*)(header + 108); + std::fseek(nfile,vox_offset,SEEK_SET); + } + + unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1; + if (!dim[0]) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with zero dimensions.", + cimg_instance, + filename?filename:"(FILE*)"); + + if (dim[0]>4) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.", + cimg_instance, + filename?filename:"(FILE*)",dim[0]); + + if (dim[0]>=1) dimx = dim[1]; + if (dim[0]>=2) dimy = dim[2]; + if (dim[0]>=3) dimz = dim[3]; + if (dim[0]>=4) dimv = dim[4]; + float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1; + const unsigned short datatype = *(unsigned short*)(header + 70); + if (voxel_size) { + const float *vsize = (float*)(header + 76); + voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3]; + } + delete[] header; + + // Read pixel data. + assign(dimx,dimy,dimz,dimv); + const size_t pdim = (size_t)dimx*dimy*dimz*dimv; + switch (datatype) { + case 2 : { + unsigned char *const buffer = new unsigned char[pdim]; + cimg::fread(buffer,pdim,nfile); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 4 : { + short *const buffer = new short[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 8 : { + int *const buffer = new int[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 16 : { + float *const buffer = new float[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 64 : { + double *const buffer = new double[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_analyze(): Unable to load datatype %d in file '%s'", + cimg_instance, + datatype,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a .cimg[z] file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(const char *const filename, const char axis='z', const float align=0) { + return CImg().load_cimg(filename,axis,align); + } + + //! Load image from a .cimg[z] file \overloading. + CImg& load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + return CImg().load_cimg(file,axis,align); + } + + //! Load sub-images of a .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Starting frame. + \param n1 Ending frame (~0U for max). + \param x0 X-coordinate of the starting sub-image vertex. + \param y0 Y-coordinate of the starting sub-image vertex. + \param z0 Z-coordinate of the starting sub-image vertex. + \param c0 C-coordinate of the starting sub-image vertex. + \param x1 X-coordinate of the ending sub-image vertex (~0U for max). + \param y1 Y-coordinate of the ending sub-image vertex (~0U for max). + \param z1 Z-coordinate of the ending sub-image vertex (~0U for max). + \param c1 C-coordinate of the ending sub-image vertex (~0U for max). + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load sub-images of a .cimg file \overloading. + CImg& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load image from an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_inr(const char *const filename, float *const voxel_size=0) { + return _load_inr(0,filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(const char *const filename, float *const voxel_size=0) { + return CImg().load_inr(filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \overloading. + CImg& load_inr(std::FILE *const file, float *const voxel_size=0) { + return _load_inr(file,0,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(std::FILE *const file, float *voxel_size=0) { + return CImg().load_inr(file,voxel_size); + } + + static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { + CImg item(1024), tmp1(64), tmp2(64); + *item = *tmp1 = *tmp2 = 0; + out[0] = std::fscanf(file,"%63s",item._data); + out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; + if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) + throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.", + pixel_type()); + + while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) { + cimg_sscanf(item," XDIM%*[^0-9]%d",out); + cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1); + cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2); + cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3); + cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6); + if (voxel_size) { + cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size); + cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); + cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); + } + if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { + case 0 : break; + case 2 : + out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; + std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough + case 1 : + if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; + if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; + if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; + if (out[4]>=0) break; // fallthrough + default : + throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", + pixel_type(), + tmp2._data); + } + } + if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) + throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.", + pixel_type(), + out[0],out[1],out[2],out[3]); + if (out[4]<0 || out[5]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.", + pixel_type()); + if (out[6]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.", + pixel_type()); + if (out[7]<0) + throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.", + pixel_type()); + } + + CImg& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) { +#define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ + if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ + Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \ + cimg_forYZ(*this,y,z) { \ + cimg::fread(val,fopt[0]*fopt[3],nfile); \ + if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \ + xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \ + } \ + delete[] val; \ + loaded = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_inr(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + int fopt[8], endian = cimg::endianness()?1:0; + bool loaded = false; + if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1; + _load_inr_header(nfile,fopt,voxel_size); + assign(fopt[0],fopt[1],fopt[2],fopt[3]); + _cimg_load_inr_case(0,0,8,unsigned char); + _cimg_load_inr_case(0,1,8,char); + _cimg_load_inr_case(0,0,16,unsigned short); + _cimg_load_inr_case(0,1,16,short); + _cimg_load_inr_case(0,0,32,unsigned int); + _cimg_load_inr_case(0,1,32,int); + _cimg_load_inr_case(1,0,32,float); + _cimg_load_inr_case(1,1,32,float); + _cimg_load_inr_case(1,0,64,double); + _cimg_load_inr_case(1,1,64,double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_inr(): Unknown pixel type defined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a EXR file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_exr(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_exr(): Specified filename is (null).", + cimg_instance); +#if defined(cimg_use_openexr) + Imf::RgbaInputFile file(filename); + Imath::Box2i dw = file.dataWindow(); + const int + inwidth = dw.max.x - dw.min.x + 1, + inheight = dw.max.y - dw.min.y + 1; + Imf::Array2D pixels; + pixels.resizeErase(inheight,inwidth); + file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth); + file.readPixels(dw.min.y, dw.max.y); + assign(inwidth,inheight,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + cimg_forXY(*this,x,y) { + *(ptr_r++) = (T)pixels[y][x].r; + *(ptr_g++) = (T)pixels[y][x].g; + *(ptr_b++) = (T)pixels[y][x].b; + *(ptr_a++) = (T)pixels[y][x].a; + } + return *this; +#elif defined(cimg_use_tinyexr) + float *res; + const char *err = 0; + int width = 0, height = 0; + const int ret = LoadEXR(&res,&width,&height,filename,&err); + if (ret) throw CImgIOException(_cimg_instance + "load_exr(): Unable to load EXR file '%s'.", + cimg_instance,filename); + CImg(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this); + std::free(res); + return *this; +#else + return load_other(filename); +#endif + } + + //! Load image from a EXR file \newinstance. + static CImg get_load_exr(const char *const filename) { + return CImg().load_exr(filename); + } + + //! Load image from a PANDORE-5 file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pandore(const char *const filename) { + return _load_pandore(0,filename); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(const char *const filename) { + return CImg().load_pandore(filename); + } + + //! Load image from a PANDORE-5 file \overloading. + CImg& load_pandore(std::FILE *const file) { + return _load_pandore(file,0); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(std::FILE *const file) { + return CImg().load_pandore(file); + } + + CImg& _load_pandore(std::FILE *const file, const char *const filename) { +#define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ + cimg::fread(dims,nbdim,nfile); \ + if (endian) cimg::invert_endianness(dims,nbdim); \ + assign(nwidth,nheight,ndepth,ndim); \ + const size_t siz = size(); \ + stype *buffer = new stype[siz]; \ + cimg::fread(buffer,siz,nfile); \ + if (endian) cimg::invert_endianness(buffer,siz); \ + T *ptrd = _data; \ + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ + buffer-=siz; \ + delete[] buffer + +#define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \ + if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \ + else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \ + else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \ + else throw CImgIOException(_cimg_instance \ + "load_pandore(): Unknown pixel datatype in file '%s'.", \ + cimg_instance, \ + filename?filename:"(FILE*)"); } + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pandore(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg header(32); + cimg::fread(header._data,12,nfile); + if (cimg::strncasecmp("PANDORE",header,7)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): PANDORE header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + unsigned int imageid, dims[8] = { 0 }; + int ptbuf[4] = { 0 }; + cimg::fread(&imageid,1,nfile); + const bool endian = imageid>255; + if (endian) cimg::invert_endianness(imageid); + cimg::fread(header._data,20,nfile); + + switch (imageid) { + case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break; + case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break; + case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break; + case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break; + case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break; + case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break; + case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break; + case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break; + case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break; + case 11 : { // Region 1D + cimg::fread(dims,3,nfile); + if (endian) cimg::invert_endianness(dims,3); + assign(dims[1],1,1,1); + const unsigned siz = size(); + if (dims[2]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[2]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 12 : { // Region 2D + cimg::fread(dims,4,nfile); + if (endian) cimg::invert_endianness(dims,4); + assign(dims[2],dims[1],1,1); + const size_t siz = size(); + if (dims[3]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[3]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 13 : { // Region 3D + cimg::fread(dims,5,nfile); + if (endian) cimg::invert_endianness(dims,5); + assign(dims[3],dims[2],dims[1],1); + const size_t siz = size(); + if (dims[4]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[4]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break; + case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break; + case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break; + case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break; + case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; + case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; + case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break; + case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break; + case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break; + case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; + case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break; + case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break; + case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break; + case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break; + case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); + break; + case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break; + case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); + break; + case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; + case 34 : { // Points 1D + cimg::fread(ptbuf,1,nfile); + if (endian) cimg::invert_endianness(ptbuf,1); + assign(1); (*this)(0) = (T)ptbuf[0]; + } break; + case 35 : { // Points 2D + cimg::fread(ptbuf,2,nfile); + if (endian) cimg::invert_endianness(ptbuf,2); + assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; + } break; + case 36 : { // Points 3D + cimg::fread(ptbuf,3,nfile); + if (endian) cimg::invert_endianness(ptbuf,3); + assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): Unable to load data with ID_type %u in file '%s'.", + cimg_instance, + imageid,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PAR-REC (Philips) file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_parrec(const char *const filename, const char axis='c', const float align=0) { + CImgList list; + list.load_parrec(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a PAR-REC (Philips) file \newinstance. + static CImg get_load_parrec(const char *const filename, const char axis='c', const float align=0) { + return CImg().load_parrec(filename,axis,align); + } + + //! Load image from a raw binary file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the image buffer. + \param size_y Height of the image buffer. + \param size_z Depth of the image buffer. + \param size_c Spectrum of the image buffer. + \param is_multiplexed Tells if the image values are multiplexed along the C-axis. + \param invert_endianness Tells if the endianness of the image buffer must be inverted. + \param offset Starting offset of the read in the specified file. + **/ + CImg& load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return CImg().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \overloading. + CImg& load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return CImg().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + CImg& _load_raw(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const bool is_multiplexed, const bool invert_endianness, + const ulongT offset) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename is (null).", + cimg_instance); + if (cimg::is_directory(filename)) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename '%s' is a directory.", + cimg_instance,filename); + const bool is_bool = pixel_type()==cimg::type::string(); + ulongT siz = (ulongT)size_x*size_y*size_z*size_c; + unsigned int + _size_x = size_x, + _size_y = size_y, + _size_z = size_z, + _size_c = size_c; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + if (!siz) { // Retrieve file size + const longT fpos = cimg::ftell(nfile); + if (fpos<0) throw CImgArgumentException(_cimg_instance + "load_raw(): Cannot determine size of input file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + cimg::fseek(nfile,0,SEEK_END); + siz = (ulongT)cimg::ftell(nfile); + if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; } + else _size_y = (unsigned int)(siz*8); + _size_x = _size_z = _size_c = 1; + cimg::fseek(nfile,fpos,SEEK_SET); + } + cimg::fseek(nfile,(longT)offset,SEEK_SET); + assign(_size_x,_size_y,_size_z,_size_c,0); + + if (is_bool) { // Boolean data (bitwise) + unsigned char *const buf = new unsigned char[siz]; + cimg::fread(buf,siz,nfile); + _uchar2bool(buf,siz,is_multiplexed); + delete[] buf; + } else { // Non-boolean data + if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed + cimg::fread(_data,siz,nfile); + if (invert_endianness) cimg::invert_endianness(_data,siz); + } else if (siz) { // Multiplexed + CImg buf(1,1,1,_size_c); + cimg_forXYZ(*this,x,y,z) { + cimg::fread(buf._data,_size_c,nfile); + if (invert_endianness) cimg::invert_endianness(buf._data,_size_c); + set_vector_at(buf,x,y,z); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image sequence from a YUV file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the frames. + \param size_y Height of the frames. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param yuv2rgb Tells if the YUV to RGB transform must be applied. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + **/ + CImg& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load image sequence from a YUV file \overloading. + CImg& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load 3D object from a .OFF file. + /** + \param[out] primitives Primitives data of the 3D object. + \param[out] colors Colors data of the 3D object. + \param filename Filename, as a C-string. + **/ + template + CImg& load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return _load_off(primitives,colors,0,filename); + } + + //! Load 3D object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return CImg().load_off(primitives,colors,filename); + } + + //! Load 3D object from a .OFF file \overloading. + template + CImg& load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return _load_off(primitives,colors,file,0); + } + + //! Load 3D object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return CImg().load_off(primitives,colors,file); + } + + template + CImg& _load_off(CImgList& primitives, CImgList& colors, + std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_off(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; + CImg line(256); *line = 0; + int err; + + // Skip comments, and read magic string OFF + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): OFF header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Invalid number of vertices or primitives specified in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read points data + assign(nb_points,3); + float X = 0, Y = 0, Z = 0; + cimg_forX(*this,l) { + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Failed to read vertex %u/%u in file '%s'.", + cimg_instance, + l + 1,nb_points,filename?filename:"(FILE*)"); + } + (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; + } + + // Read primitive data + primitives.assign(); + colors.assign(); + bool stop_flag = false; + while (!stop_flag) { + float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; + unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; + *line = 0; + if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true; + else { + ++nb_read; + switch (prim) { + case 1 : { + if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 2 : { + if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 3 : { + if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 4 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 5 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 6 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 7 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i4,i3,i1).move_to(primitives); + CImg::vector(i0,i6,i5,i4).move_to(primitives); + CImg::vector(i3,i2,i1).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + case 8 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line._data))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + CImg::vector(i0,i7,i6,i5).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + default : + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.", + cimg_instance, + nb_read,nb_primitives,prim,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } + } + } + if (!file) cimg::fclose(nfile); + if (primitives._width!=nb_primitives) + cimg::warn(_cimg_instance + "load_off(): Only %u/%u primitives read from file '%s'.", + cimg_instance, + primitives._width,nb_primitives,filename?filename:"(FILE*)"); + return *this; + } + + //! Load image sequence from a video file, using OpenCV library. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param axis Alignment axis. + \param align Appending alignment. + **/ + CImg& load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, + const char axis='z', const float align=0) { + return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this); + } + + //! Load image sequence from a video file, using OpenCV library \newinstance. + static CImg get_load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, + const char axis='z', const float align=0) { + return CImgList().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align); + } + + //! Load image sequence using custom's external tool 'custom'. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_custom_external(const char *const filename, const char axis='z', const float align=0) { + return get_load_custom_external(filename,axis,align).move_to(*this); + } + + //! Load image sequence using custom's external tool 'custom' \newinstance. + static CImg get_load_custom_external(const char *const filename, const char axis='z', const float align=0) { + return CImgList().load_custom_external(filename).get_append(axis,align); + } + + //! Load gif file, using Imagemagick or GraphicsMagicks's external tools. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return get_load_gif_external(filename,axis,align).move_to(*this); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance. + static CImg get_load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return CImgList().load_gif_external(filename).get_append(axis,align); + } + + //! Load image from a HEIC file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_heif(const char *const filename) { + return _load_heif(filename); + } + + //! Load image from a HEIC file \newinstance. + static CImg get_load_heif(const char *const filename) { + return CImg().load_heif(filename); + } + + CImg& _load_heif(const char *const filename) { +#ifndef cimg_use_heif + return load_other(filename); +#else + try { + heif::Context ctx; + ctx.read_from_file(filename); + + heif::ImageHandle handle = ctx.get_primary_image_handle(); + const heif::Image image = + handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA: + heif_chroma_interleaved_RGB); + const int + W = image.get_width(heif_channel_interleaved), + H = image.get_height(heif_channel_interleaved), + S = handle.has_alpha_channel()?4:3; + assign(W,H,1,S); + + int stride; + const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride); + T *ptr_r = _data, *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = S>3?data(0,0,0,3):0; + cimg_forY(*this,y) { + const unsigned char *ptrs = buffer + y*stride; + if (ptr_a) cimg_forX(*this,x) { // RGBA + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + else cimg_forX(*this,x) { // RGB + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } catch (const heif::Error& e) { + throw CImgInstanceException(_cimg_instance + "load_heif(): Unable to decode image: %s", + cimg_instance, + e.get_message().c_str()); + } catch (...) { + throw; + } + return *this; +#endif + } + + //! Load image using GraphicsMagick's external tool 'gm'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_graphicsmagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + if (!cimg::system("which gm")) { + cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-", + cimg::graphicsmagick_path(), + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' " + "with external command 'gm'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"", + cimg::graphicsmagick_path(), + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::graphicsmagick_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", + cimg_instance, + filename); + + } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else + load_pnm(filename_tmp); +#endif + std::remove(filename_tmp); + return *this; + } + + //! Load image using GraphicsMagick's external tool 'gm' \newinstance. + static CImg get_load_graphicsmagick_external(const char *const filename) { + return CImg().load_graphicsmagick_external(filename); + } + + //! Load gzipped image file, using external tool 'gunzip'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimg_instance + "load_gzip_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + const char + *const ext = cimg::split_filename(filename,body), + *const ext2 = cimg::split_filename(body,0); + + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load gzipped image file, using external tool 'gunzip' \newinstance. + static CImg get_load_gzip_external(const char *const filename) { + return CImg().load_gzip_external(filename); + } + + //! Load image using ImageMagick's external tool 'convert'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_imagemagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_imagemagick_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + if (!cimg::system("which convert")) { + cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::imagemagick_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + + } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else + load_pnm(filename_tmp); +#endif + std::remove(filename_tmp); + return *this; + } + + //! Load image using ImageMagick's external tool 'convert' \newinstance. + static CImg get_load_imagemagick_external(const char *const filename) { + return CImg().load_imagemagick_external(filename); + } + + //! Load image from a DICOM file, using Medcon's external tool 'medcon'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_medcon_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_medcon_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + cimg::fclose(cimg::fopen(filename,"r")); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::medcon_path()); + cimg::split_filename(filename_tmp,body); + + cimg_snprintf(command,command._width,"%s.hdr",body._data); + file = cimg::std_fopen(command,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"m000-%s.hdr",body._data); + file = cimg::std_fopen(command,"rb"); + if (!file) { + throw CImgIOException(_cimg_instance + "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + load_analyze(command); + std::remove(command); + cimg::split_filename(command,body); + cimg_snprintf(command,command._width,"%s.img",body._data); + std::remove(command); + return *this; + } + + //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance. + static CImg get_load_medcon_external(const char *const filename) { + return CImg().load_medcon_external(filename); + } + + //! Load image from a .pdf file. + /** + \param filename Filename, as a C-string. + \param resolution Image resolution. + **/ + CImg& load_pdf_external(const char *const filename, const unsigned int resolution=400) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_pdf_external(): Specified filename is (null).", + cimg_instance); + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"", + resolution,s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_pdf_external(): Failed to load file '%s' with external command 'gs'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"", + CImg::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data()); + cimg::system(command,"gs"); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_pdf_external(): Failed to load file '%s' with external command 'gs'.", + cimg_instance, + filename); + } else cimg::fclose(file); + load_pnm(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load image from a .pdf file \newinstance. + static CImg get_load_pdf_external(const char *const filename, const unsigned int resolution=400) { + return CImg().load_pdf_external(filename,resolution); + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_dcraw_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_dcraw_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"", + cimg::dcraw_path(),s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"", + cimg::dcraw_path(),s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::dcraw_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load_pnm(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance. + static CImg get_load_dcraw_external(const char *const filename) { + return CImg().load_dcraw_external(filename); + } + +#ifdef cimg_use_opencv + + // Convert a continuous cv::Mat to a CImg. + static CImg _cvmat2cimg(const cv::Mat &src) { + if (src.channels()==1) return CImg(src.ptr(),src.cols,src.rows,1,1); + else if (src.channels()==3) { // BGR + CImg res(src.cols,src.rows,1,src.channels()); + const unsigned char *ptrs = src.ptr(); + unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2); + cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); } + return res; + } + return CImg(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx"); + } + + // Convert a CImg to a cv::Mat. + cv::Mat _cimg2cvmat() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Instance image is empty.", + cimg_instance); + if (_spectrum==2) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').", + cimg_instance); + if (_depth!=1) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Invalid number of slices (should be '1').", + cimg_instance); + int mat_type = -1; + if (pixel_type()==cimg::type::string()) mat_type = CV_8UC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_8SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_16UC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_16SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_32SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_32FC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_64FC1; + if (mat_type<0) + throw CImgInstanceException(_cimg_instance + "_cvmat2cimg() : pixel type '%s' is not supported.", + cimg_instance,pixel_type()); + cv::Mat res; + std::vector channels(_spectrum); + if (_spectrum>1) { + cimg_forC(*this,c) + channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c)); + cv::merge(channels,res); + } else res = cv::Mat(_height,_width,mat_type,_data).clone(); + return res; + } + +#endif + + //! Load image from a camera stream, using OpenCV. + /** + \param index Index of the camera to capture images from (from 0 to 63). + \param capture_width Width of the desired image ('0' stands for default value). + \param capture_height Height of the desired image ('0' stands for default value). + \param skip_frames Number of frames to skip before the capture. + \param release_camera Tells if the camera resource must be released at the end of the method. + **/ + CImg& load_camera(const unsigned int camera_index=0, + const unsigned int capture_width=0, const unsigned int capture_height=0, + const unsigned int skip_frames=0, const bool release_camera=true) { +#ifdef cimg_use_opencv + if (camera_index>=64) + throw CImgArgumentException(_cimg_instance + "load_camera(): Invalid request for camera #%u " + "(no more than 100 cameras can be managed simultaneously).", + cimg_instance, + camera_index); + static cv::VideoCapture *captures[64] = { 0 }; + static unsigned int captures_w[64], captures_h[64]; + if (release_camera) { + cimg::mutex(9); + if (captures[camera_index]) captures[camera_index]->release(); + delete captures[camera_index]; + captures[camera_index] = 0; + captures_w[camera_index] = captures_h[camera_index] = 0; + cimg::mutex(9,0); + return *this; + } + if (!captures[camera_index]) { + cimg::mutex(9); + captures[camera_index] = new cv::VideoCapture(camera_index); + captures_w[camera_index] = captures_h[camera_index] = 0; + if (!captures[camera_index]->isOpened()) { + delete captures[camera_index]; + captures[camera_index] = 0; + cimg::mutex(9,0); + throw CImgIOException(_cimg_instance + "load_camera(): Failed to initialize camera #%u.", + cimg_instance, + camera_index); + } + cimg::mutex(9,0); + } + cimg::mutex(9); + if (capture_width!=captures_w[camera_index]) { + captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width); + captures_w[camera_index] = capture_width; + } + if (capture_height!=captures_h[camera_index]) { + captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height); + captures_h[camera_index] = capture_height; + } + for (unsigned int i = 0; igrab(); + cv::Mat cvimg; + captures[camera_index]->read(cvimg); + if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this); + cimg::mutex(9,0); + return *this; +#else + cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height); + throw CImgIOException(_cimg_instance + "load_camera(): This function requires features from the OpenCV library " + "('-Dcimg_use_opencv' must be defined).", + cimg_instance); +#endif + } + + //! Load image from a camera stream, using OpenCV \newinstance. + static CImg get_load_camera(const unsigned int camera_index=0, + const unsigned int capture_width=0, const unsigned int capture_height=0, + const unsigned int skip_frames=0, const bool release_camera=true) { + return CImg().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera); + } + + //! Load image using various non-native ways. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_other(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_other(): Specified filename is (null).", + cimg_instance); + + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_magick(filename); } + catch (CImgException&) { + try { load_imagemagick_external(filename); } + catch (CImgException&) { + try { load_graphicsmagick_external(filename); } + catch (CImgException&) { + try { load_cimg(filename); } + catch (CImgException&) { + try { + cimg::fclose(cimg::fopen(filename,"rb")); + } catch (CImgException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_other(): Failed to open file '%s'.", + cimg_instance, + filename); + } + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_other(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load image using various non-native ways \newinstance. + static CImg get_load_other(const char *const filename) { + return CImg().load_other(filename); + } + + //@} + //--------------------------- + // + //! \name Data Output + //@{ + //--------------------------- + + //! Display information about the image data. + /** + \param title Name for the considered image. + \param display_stats Tells to compute and display image statistics. + **/ + const CImg& print(const char *const title=0, const bool display_stats=true) const { + + int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; + CImg st; + if (!is_empty() && display_stats) { + st = get_stats(); + xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; + xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; + } + + const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1, + mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1; + + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type()); + + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum, + (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) + std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared"); + else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared"); + + if (!is_empty()) cimg_foroff(*this,off) { + std::fprintf(cimg::output(),"%g",(double)_data[off]); + if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" "); + if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } + } + if (!is_empty() && display_stats) + std::fprintf(cimg::output(), + " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), " + "%scoords_max%s = (%u,%u,%u,%u).\n", + cimg::t_bold,cimg::t_normal,st[0], + cimg::t_bold,cimg::t_normal,st[1], + cimg::t_bold,cimg::t_normal,st[2], + cimg::t_bold,cimg::t_normal,std::sqrt(st[3]), + cimg::t_bold,cimg::t_normal,xm,ym,zm,vm, + cimg::t_bold,cimg::t_normal,xM,yM,zM,vM); + else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" "); + std::fflush(cimg::output()); + return *this; + } + + //! Display image into a CImgDisplay window. + /** + \param disp Display window. + **/ + const CImg& display(CImgDisplay& disp) const { + disp.display(*this); + return *this; + } + + //! Display image into a CImgDisplay window, in an interactive way. + /** + \param disp Display window. + \param display_info Tells if image information are displayed on the standard output. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _display(disp,0,display_info,XYZ,exit_on_anykey,false); + } + + //! Display image into an interactive window. + /** + \param title Window title + \param display_info Tells if image information are displayed on the standard output. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display(disp,title,display_info,XYZ,exit_on_anykey,false); + } + + const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info, + unsigned int *const XYZ, const bool exit_on_anykey, + const bool exit_on_singleclick) const { + unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; + int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, + old_mouse_x = -1, old_mouse_y = -1; + + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + else disp.set_title("%s",title); + } else if (title) disp.set_title("%s",title); + disp.show().flush(); + + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(dtitle); + + CImg zoom; + for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) { + if (reset_view) { + if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; } + else { + _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2; + _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2; + _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2; + } + x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1; + disp.resize(cimg_fitscreen(_width,_height,_depth),false); + oldw = disp._width; oldh = disp._height; + resize_disp = true; + reset_view = false; + } + if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) { + if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign(); + } else zoom = get_crop(x0,y0,z0,x1,y1,z1); + + const CImg& visu = zoom?zoom:*this; + const unsigned int + dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0, + tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U); + if (!is_empty() && !disp.is_fullscreen() && resize_disp) { + const float + ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh, + dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height()); + const unsigned int + imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM); + disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false); + resize_disp = false; + } + oldw = tw; oldh = th; + + bool + go_up = false, go_down = false, go_left = false, go_right = false, + go_inc = false, go_dec = false, go_in = false, go_out = false, + go_in_center = false; + + disp.set_title("%s",dtitle._data); + if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0); + if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0); + if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0); + + disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; + CImg selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true); + old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; + is_first_select = false; + + if (disp.wheel()) { + if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) && + (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) { + go_left = !(go_right = disp.wheel()>0); + } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + go_down = !(go_up = disp.wheel()>0); + } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + go_out = !(go_in = disp.wheel()>0); go_in_center = false; + } + disp.set_wheel(); + } + + const int + sx0 = selection(0), sy0 = selection(1), sz0 = selection(2), + sx1 = selection(3), sy1 = selection(4), sz1 = selection(5); + if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { + x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; + x0+=sx0; y0+=sy0; z0+=sz0; + if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) { + if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true; + } + resize_disp = true; + } else switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break; + case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { + // Special mode: play stack of frames + const unsigned int + w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)), + h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0)); + float frame_timing = 5; + bool is_stopped = false; + disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0; + for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) { + if (disp.is_resized()) disp.resize(false); + if (!timer) { + visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2])); + (++_XYZ[2])%=visu._depth; + } + if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U; + if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); } + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break; + case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break; + case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; + (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false). + toggle_fullscreen().set_key(key,false); key = 0; + } break; + } + frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing); + disp.wait(20); + } + const unsigned int + w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width, + h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height; + disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel(); + key = 0; + } break; + case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break; + case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break; + case cimg::keyPADSUB : go_out = true; key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break; + case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break; + case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break; + case cimg::keyPAD7 : go_up = go_left = true; key = 0; break; + case cimg::keyPAD9 : go_up = go_right = true; key = 0; break; + case cimg::keyPAD1 : go_down = go_left = true; key = 0; break; + case cimg::keyPAD3 : go_down = go_right = true; key = 0; break; + case cimg::keyPAGEUP : go_inc = true; key = 0; break; + case cimg::keyPAGEDOWN : go_dec = true; key = 0; break; + } + if (go_in) { + const int + mx = go_in_center?disp.width()/2:disp.mouse_x(), + my = go_in_center?disp.height()/2:disp.mouse_y(), + mX = mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my*(height() + (depth()>1?depth():0))/disp.height(); + int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2]; + if (mX=height()) { + X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); + } + if (mX>=width() && mY4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; } + if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; } + if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; } + } + if (go_out) { + const int + delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8, + ndelta_x = delta_x?delta_x:(_width>1), + ndelta_y = delta_y?delta_y:(_height>1), + ndelta_z = delta_z?delta_z:(_depth>1); + x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z; + x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; } + if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; } + if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; } + if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; } + if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; } + if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; } + const float + ratio = (float)(x1-x0)/(y1-y0), + ratiow = (float)disp._width/disp._height, + sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow)); + if (sub>0.01) resize_disp = true; + } + if (go_left) { + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); + if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + } + if (go_right) { + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); + if (x1+ndelta1); + if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; } + else { y1-=y0; y0 = 0; } + } + if (go_down) { + const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1); + if (y1+ndelta1); + if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; } + else { z1-=z0; z0 = 0; } + } + if (go_dec) { + const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1); + if (z1+ndelta + const CImg& display_object3d(CImgDisplay& disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + template + const CImg& _display_object3d(CImgDisplay& disp, const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering, + const int render_static, const int render_motion, + const bool is_double_sided, const float focale, + const float light_x, const float light_y, const float light_z, + const float specular_lightness, const float specular_shininess, + const bool display_axes, float *const pose_matrix, + const bool exit_on_anykey) const { + typedef typename cimg::superset::type tpfloat; + + // Check input arguments + if (is_empty()) { + CImg background; + if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128); + else background.assign(1,2,1,3,32,64,32,116,64,96); + if (disp) background.resize(disp.width(),disp.height(),1,-100,3); + else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + CImgDisplay::screen_height()/2,1),1,-100,3); + return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } else { if (disp) disp.resize(*this,false); } + CImg error_message(1024); + if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgArgumentException(_cimg_instance + "display_object3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); + if (vertices._width && !primitives) { + CImgList nprimitives(vertices._width,1,1,1,1); + cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l; + return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); + if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)", + pixel_type(),vertices._width,primitives._width); + } else if (title) disp.set_title("%s",title); + + // Init 3D objects and compute object statistics + CImg + pose, + rotated_vertices(vertices._width,3), + bbox_vertices, rotated_bbox_vertices, + axes_vertices, rotated_axes_vertices, + bbox_opacities, axes_opacities; + CImgList bbox_primitives, axes_primitives; + CImgList reverse_primitives; + CImgList bbox_colors, bbox_colors2, axes_colors; + unsigned int ns_width = 0, ns_height = 0; + int _is_double_sided = (int)is_double_sided; + bool ndisplay_axes = display_axes; + const CImg + background_color(1,1,1,_spectrum,0), + foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type::max(),255)); + float + Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1, + xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0, + ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0, + zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0; + const float delta = cimg::max(xM - xm,yM - ym,zM - zm); + + rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1, + xm,xM,xM,xm,xm,xM,xM,xm, + ym,ym,yM,yM,ym,ym,yM,yM, + zm,zm,zm,zm,zM,zM,zM,zM); + bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6); + bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]); + bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]); + bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f); + + rotated_axes_vertices = axes_vertices.assign(7,3,1,1, + 0,20,0,0,22,-6,-6, + 0,0,20,0,-6,22,-6, + 0,0,0,20,0,0,22); + axes_opacities.assign(3,1,1,1,1); + axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]); + axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); + + // Begin user interaction loop + CImg visu0(*this,false), visu; + CImg zbuffer(visu0.width(),visu0.height(),1,1,0); + bool init_pose = true, clicked = false, redraw = true; + unsigned int key = 0, font_size = 32; + int + x0 = 0, y0 = 0, x1 = 0, y1 = 0, + nrender_static = render_static, + nrender_motion = render_motion; + disp.show().flush(); + + while (!disp.is_closed() && !key) { + + // Init object pose + if (init_pose) { + const float + ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1, + dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2; + if (centering) + CImg(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose); + else CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose); + if (pose_matrix) { + CImg pose0(pose_matrix,4,3,1,1,false); + pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0); + pose0(3,3) = pose(3,3) = 1; + (pose0*pose).get_crop(0,0,3,2).move_to(pose); + Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15]; + } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; } + init_pose = false; + redraw = true; + } + + // Rotate and draw 3D object + if (redraw) { + const float + r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), + r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), + r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); + if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) { + const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2); + float + *const prv0 = rotated_vertices.data(), + *const prv1 = rotated_vertices.data(0,1), + *const prv2 = rotated_vertices.data(0,2); + cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024)) + cimg_forX(vertices,l) { + const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l]; + prv0[l] = r00*x + r10*y + r20*z + r30; + prv1[l] = r01*x + r11*y + r21*z + r31; + prv2[l] = r02*x + r12*y + r22*z + r32; + } + } + else cimg_forX(bbox_vertices,l) { + const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2); + rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30; + rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31; + rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32; + } + + // Draw objects + const bool render_with_zbuffer = !clicked && nrender_static>0; + visu = visu0; + if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0)) + visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale). + draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale); + else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg::empty(), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale, + width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1,sprite_scale); + // Draw axes + if (ndisplay_axes) { + const float + n = 1e-8f + cimg::hypot(r00,r01,r02), + _r00 = r00/n, _r10 = r10/n, _r20 = r20/n, + _r01 = r01/n, _r11 = r11/n, _r21 = r21/n, + _r02 = r01/n, _r12 = r12/n, _r22 = r22/n, + Xaxes = 25, Yaxes = visu._height - 38.f; + cimg_forX(axes_vertices,l) { + const float + x = axes_vertices(l,0), + y = axes_vertices(l,1), + z = axes_vertices(l,2); + rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z; + rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z; + rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z; + } + axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f; + axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f; + axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f; + visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives, + axes_colors,axes_opacities,1,false,focale). + draw_text((int)(Xaxes + rotated_axes_vertices(4,0)), + (int)(Yaxes + rotated_axes_vertices(4,1)), + "X",axes_colors[0]._data,0,axes_opacities(0,0),13). + draw_text((int)(Xaxes + rotated_axes_vertices(5,0)), + (int)(Yaxes + rotated_axes_vertices(5,1)), + "Y",axes_colors[1]._data,0,axes_opacities(1,0),13). + draw_text((int)(Xaxes + rotated_axes_vertices(6,0)), + (int)(Yaxes + rotated_axes_vertices(6,1)), + "Z",axes_colors[2]._data,0,axes_opacities(2,0),13); + } + visu.display(disp); + if (!clicked || nrender_motion==nrender_static) redraw = false; + } + + // Handle user interaction + if (!redraw) disp.wait(); + if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) { + redraw = true; + if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; } + else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); } + const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT(); + if (disp.button()&1 && !is_keyCTRL) { + const float + R = 0.45f*std::min(disp.width(),disp.height()), + R2 = R*R, + u0 = (float)(x0 - disp.width()/2), + v0 = (float)(y0 - disp.height()/2), + u1 = (float)(x1 - disp.width()/2), + v1 = (float)(y1 - disp.height()/2), + n0 = cimg::hypot(u0,v0), + n1 = cimg::hypot(u1,v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), + u = nv0*nw1 - nw0*nv1, + v = nw0*nu1 - nu0*nw1, + w = nv0*nu1 - nu0*nv1, + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + (CImg::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose); + x0 = x1; y0 = y1; + } + if (disp.button()&2 && !is_keyCTRL) { + if (focale>0) Zoff-=(y0 - y1)*focale/400; + else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; } + x0 = x1; y0 = y1; + } + if (disp.wheel()) { + if (focale>0) Zoff-=disp.wheel()*focale/20; + else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; } + disp.set_wheel(); + } + if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) { + Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; + } + if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) { + init_pose = true; disp.set_button(); x0 = x1; y0 = y1; + pose = CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0); + } + } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } + + CImg filename(32); + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + if (!ns_width || !ns_height || + ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) { + ns_width = disp.screen_width()*3U/4; + ns_height = disp.screen_height()*3U/4; + } + if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false); + else { + ns_width = disp._width; ns_height = disp._height; + disp.resize(disp.screen_width(),disp.screen_height(),false); + } + disp.toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + // Switch single/double-sided primitives. + if (--_is_double_sided==-2) _is_double_sided = 1; + if (_is_double_sided>=0) reverse_primitives.assign(); + else primitives.get_reverse_object3d().move_to(reverse_primitives); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer + if (zbuffer) zbuffer.assign(); + else zbuffer.assign(visu0.width(),visu0.height(),1,1,0); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes + ndisplay_axes = !ndisplay_axes; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points + nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines + nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat + nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded + nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + // Set rendering mode to gouraud-shaded. + nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded + nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp); + visu.save(filename); + (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); + vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file + static unsigned int snap_number = 0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); + vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities). + save(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + +#ifdef cimg_use_board + case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1, + sprite_scale); + board.saveEPS(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1, + sprite_scale); + board.saveSVG(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; +#endif + } + if (disp.is_resized()) { + disp.resize(false); visu0 = get_resize(disp,1); + if (zbuffer) zbuffer.assign(disp.width(),disp.height()); + redraw = true; + } + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + if (pose_matrix) { + std::memcpy(pose_matrix,pose._data,12*sizeof(float)); + pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale; + } + disp.set_button().set_key(key); + return *this; + } + + //! Display 1D graph in an interactive window. + /** + \param disp Display window. + \param plot_type Plot type. Can be { 0=points | 1=segments | 2=splines | 3=bars }. + \param vertex_type Vertex type. + \param labelx Title for the horizontal axis, as a C-string. + \param xmin Minimum value along the X-axis. + \param xmax Maximum value along the X-axis. + \param labely Title for the vertical axis, as a C-string. + \param ymin Minimum value along the X-axis. + \param ymax Maximum value along the X-axis. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); + } + + //! Display 1D graph in an interactive window \overloading. + const CImg& display_graph(const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); + } + + const CImg& _display_graph(CImgDisplay &disp, const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "display_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). + set_title(title?"%s":"CImg<%s>",title?title:pixel_type()); + const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1); + const unsigned int old_normalization = disp.normalization(); + disp.show().flush()._normalization = 0; + + double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; + if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; } + int x0 = 0, x1 = width()*height()*depth() - 1, key = 0; + + for (bool reset_view = true; !key && !disp.is_closed(); ) { + if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; } + CImg zoom(x1 - x0 + 1,1,1,spectrum()); + cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true); + if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; } + if (y0==y1) { --y0; ++y1; } + + const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, + labelx, + nxmin + x0*(nxmax - nxmin)/siz1, + nxmin + x1*(nxmax - nxmin)/siz1, + labely,y0,y1,true); + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + if (selection[0]>=0) { + if (selection[2]<0) reset_view = true; + else { + x1 = x0 + selection[2]; x0+=selection[0]; + if (selection[1]>=0 && selection[3]>=0) { + y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32); + y1-=selection[1]*(y1 - y0)/(disp.height() - 32); + } + } + } else { + bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false; + switch (key = (int)disp.key()) { + case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break; + case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break; + case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break; + case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); + break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); + break; + case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break; + case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break; + case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break; + case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break; + } + if (disp.wheel()) { + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0); + else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); + else go_out = !(go_in = disp.wheel()>0); + key = 0; + } + + if (go_in) { + const int + xsiz = x1 - x0, + mx = (mouse_x - 16)*xsiz/(disp.width() - 32), + cx = x0 + cimg::cut(mx,0,xsiz); + if (x1 - x0>4) { + x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8; + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + const double + ysiz = y1 - y0, + my = (mouse_y - 16)*ysiz/(disp.height() - 32), + cy = y1 - cimg::cut(my,0.,ysiz); + y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8; + } else y0 = y1 = 0; + } + } + if (go_out) { + if (x0>0 || x1<(int)siz1) { + const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1); + const double ndelta_y = (y1 - y0)/8; + x0-=ndelta_x; x1+=ndelta_x; + y0-=ndelta_y; y1+=ndelta_y; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; } + if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; } + } + } + if (go_left) { + const int delta = (x1 - x0)/5, ndelta = delta?delta:1; + if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + go_left = false; + } + if (go_right) { + const int delta = (x1 - x0)/5, ndelta = delta?delta:1; + if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; } + else { x0+=(siz1 - x1); x1 = (int)siz1; } + go_right = false; + } + if (go_up) { + const double delta = (y1 - y0)/10, ndelta = delta?delta:1; + y0+=ndelta; y1+=ndelta; + go_up = false; + } + if (go_down) { + const double delta = (y1 - y0)/10, ndelta = delta?delta:1; + y0-=ndelta; y1-=ndelta; + go_down = false; + } + } + if (!exit_on_anykey && key && key!=(int)cimg::keyESC && + (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + key = 0; + } + } + disp._normalization = old_normalization; + return *this; + } + + //! Save image as a file. + /** + \param filename Filename, as a C-string. + \param number When positive, represents an index added to the filename. Otherwise, no number is added. + \param digits Number of digits used for adding the number to the filename. + \note + - The used file format is defined by the file extension in the filename \p filename. + - Parameter \p number can be used to add a 6-digit number to the filename before saving. + + **/ + const CImg& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save(): Specified filename is (null).", + cimg_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + CImg nfilename(1024); + const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename): + filename; + +#ifdef cimg_save_plugin + cimg_save_plugin(fn); +#endif +#ifdef cimg_save_plugin1 + cimg_save_plugin1(fn); +#endif +#ifdef cimg_save_plugin2 + cimg_save_plugin2(fn); +#endif +#ifdef cimg_save_plugin3 + cimg_save_plugin3(fn); +#endif +#ifdef cimg_save_plugin4 + cimg_save_plugin4(fn); +#endif +#ifdef cimg_save_plugin5 + cimg_save_plugin5(fn); +#endif +#ifdef cimg_save_plugin6 + cimg_save_plugin6(fn); +#endif +#ifdef cimg_save_plugin7 + cimg_save_plugin7(fn); +#endif +#ifdef cimg_save_plugin8 + cimg_save_plugin8(fn); +#endif + // Text formats + if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn); + else if (!cimg::strcasecmp(ext,"csv") || + !cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); + else if (!cimg::strcasecmp(ext,"cpp") || + !cimg::strcasecmp(ext,"hpp") || + !cimg::strcasecmp(ext,"h") || + !cimg::strcasecmp(ext,"c")) return save_cpp(fn); + + // 2D binary formats + else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn); + else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); + else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); + else if (!cimg::strcasecmp(ext,"png")) return save_png(fn); + else if (!cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); + else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn); + else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn); + else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); + + // 3D binary formats + else if (!*ext) { +#ifdef cimg_use_zlib + return save_cimg(fn,true); +#else + return save_cimg(fn,false); +#endif + } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); + else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); + else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn); + else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); + else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + + // Image sequences + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); + return save_other(fn); + } + + //! Save image as an ascii file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_ascii(const char *const filename) const { + return _save_ascii(0,filename); + } + + //! Save image as an Ascii file \overloading. + const CImg& save_ascii(std::FILE *const file) const { + return _save_ascii(file,0); + } + + const CImg& _save_ascii(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_ascii(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++)); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a .cpp source file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_cpp(const char *const filename) const { + return _save_cpp(0,filename); + } + + //! Save image as a .cpp source file \overloading. + const CImg& save_cpp(std::FILE *const file) const { + return _save_cpp(file,0); + } + + const CImg& _save_cpp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_cpp(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + CImg varname(1024); *varname = 0; + if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data); + if (!*varname) cimg_snprintf(varname,varname._width,"unnamed"); + std::fprintf(nfile, + "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" + "%s data_%s[] = { %s\n ", + varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data, + is_empty()?"};":""); + if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) { + std::fprintf(nfile,cimg::type::format(),cimg::type::format((*this)[off])); + if (off==siz) std::fprintf(nfile," };\n"); + else if (!((off + 1)%16)) std::fprintf(nfile,",\n "); + else std::fprintf(nfile,", "); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a DLM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_dlm(const char *const filename) const { + return _save_dlm(0,filename); + } + + //! Save image as a DLM file \overloading. + const CImg& save_dlm(std::FILE *const file) const { + return _save_dlm(file,0); + } + + const CImg& _save_dlm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_dlm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":","); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a BMP file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_bmp(const char *const filename) const { + return _save_bmp(0,filename); + } + + //! Save image as a BMP file \overloading. + const CImg& save_bmp(std::FILE *const file) const { + return _save_bmp(file,0); + } + + const CImg& _save_bmp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_bmp(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + CImg header(54,1,1,1,0); + unsigned char align_buf[4] = { 0 }; + const unsigned int + align = (4 - (3*_width)%4)%4, + buf_size = (3*_width + align)*height(), + file_size = 54 + buf_size; + header[0] = 'B'; header[1] = 'M'; + header[0x02] = file_size&0xFF; + header[0x03] = (file_size>>8)&0xFF; + header[0x04] = (file_size>>16)&0xFF; + header[0x05] = (file_size>>24)&0xFF; + header[0x0A] = 0x36; + header[0x0E] = 0x28; + header[0x12] = _width&0xFF; + header[0x13] = (_width>>8)&0xFF; + header[0x14] = (_width>>16)&0xFF; + header[0x15] = (_width>>24)&0xFF; + header[0x16] = _height&0xFF; + header[0x17] = (_height>>8)&0xFF; + header[0x18] = (_height>>16)&0xFF; + header[0x19] = (_height>>24)&0xFF; + header[0x1A] = 1; + header[0x1B] = 0; + header[0x1C] = 24; + header[0x1D] = 0; + header[0x22] = buf_size&0xFF; + header[0x23] = (buf_size>>8)&0xFF; + header[0x24] = (buf_size>>16)&0xFF; + header[0x25] = (buf_size>>24)&0xFF; + header[0x27] = 0x1; + header[0x2B] = 0x1; + cimg::fwrite(header._data,54,nfile); + + const T + *ptr_r = data(0,_height - 1,0,0), + *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0; + + switch (_spectrum) { + case 1 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + const unsigned char val = (unsigned char)*(ptr_r++); + std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; + } + } break; + case 2 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc(0,nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; + } + } break; + default : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc((unsigned char)(*(ptr_b++)),nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a JPEG file. + /** + \param filename Filename, as a C-string. + \param quality Image quality (in %) + **/ + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return _save_jpeg(0,filename,quality); + } + + //! Save image as a JPEG file \overloading. + const CImg& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { + return _save_jpeg(file,0,quality); + } + + const CImg& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_jpeg(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + +#ifndef cimg_use_jpeg + if (!file) return save_other(filename,quality); + else throw CImgIOException(_cimg_instance + "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.", + cimg_instance); +#else + unsigned int dimbuf = 0; + J_COLOR_SPACE colortype = JCS_RGB; + + switch (_spectrum) { + case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break; + case 2 : dimbuf = 3; colortype = JCS_RGB; break; + case 3 : dimbuf = 3; colortype = JCS_RGB; break; + default : dimbuf = 4; colortype = JCS_CMYK; break; + } + + // Call libjpeg functions + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + jpeg_stdio_dest(&cinfo,nfile); + cinfo.image_width = _width; + cinfo.image_height = _height; + cinfo.input_components = dimbuf; + cinfo.in_color_space = colortype; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); + jpeg_start_compress(&cinfo,TRUE); + + JSAMPROW row_pointer[1]; + CImg buffer(_width*dimbuf); + + while (cinfo.next_scanline& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_magick(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_magick + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_magick(): Instance is multispectral, only the three first channels will be " + "saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + Magick::Image image(Magick::Geometry(_width,_height),"black"); + image.type(Magick::TrueColorType); + image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8)); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = _spectrum>1?data(0,0,0,1):0, + *ptr_b = _spectrum>2?data(0,0,0,2):0; + Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height); + switch (_spectrum) { + case 1 : // Scalar images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++); + ++pixels; + } + break; + case 2 : // RG images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = 0; ++pixels; + } + break; + default : // RGB images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = (Magick::Quantum)*(ptr_b++); + ++pixels; + } + } + image.syncPixels(); + image.write(filename); + return *this; +#else + cimg::unused(bytes_per_pixel); + throw CImgIOException(_cimg_instance + "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + } + + //! Save image as a PNG file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible. + **/ + const CImg& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_png(0,filename,bytes_per_pixel); + } + + //! Save image as a PNG file \overloading. + const CImg& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_png(file,0,bytes_per_pixel); + } + + const CImg& _save_png(std::FILE *const file, const char *const filename, + const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_png(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + +#ifndef cimg_use_png + cimg::unused(bytes_per_pixel); + if (!file) return save_other(filename); + else throw CImgIOException(_cimg_instance + "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.", + cimg_instance); +#else + +#if defined __GNUC__ + const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); + volatile double stmin, stmax = (double)max_min(stmin); +#else + const char *nfilename = filename; + std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb"); + double stmin, stmax = (double)max_min(stmin); +#endif + + if (_depth>1) + cimg::warn(_cimg_instance + "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>4) + cimg::warn(_cimg_instance + "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + // Setup PNG structures for write + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, + user_warning_fn); + if (!png_ptr){ + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr,(png_infopp)0); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + + const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8); + + int color_type; + switch (spectrum()) { + case 1 : color_type = PNG_COLOR_TYPE_GRAY; break; + case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3 : color_type = PNG_COLOR_TYPE_RGB; break; + default : color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + const int interlace_type = PNG_INTERLACE_NONE; + const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; + const int filter_method = PNG_FILTER_TYPE_DEFAULT; + png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method); + png_write_info(png_ptr,info_ptr); + const int byte_depth = bit_depth>>3; + const int numChan = spectrum()>4?4:spectrum(); + const int pixel_bit_depth_flag = numChan * (bit_depth - 1); + + // Allocate Memory for Image Save and Fill pixel data + png_bytep *const imgData = new png_byte*[_height]; + for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width]; + const T *pC0 = data(0,0,0,0); + switch (pixel_bit_depth_flag) { + case 7 : { // Gray 8-bit + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++); + } + } break; + case 14 : { // Gray w/ Alpha 8-bit + const T *pC1 = data(0,0,0,1); + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) { + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + } + } + } break; + case 21 : { // RGB 8-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) { + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + *(ptrd++) = (unsigned char)*(pC2++); + } + } + } break; + case 28 : { // RGB x/ Alpha 8-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); + cimg_forY(*this,y){ + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x){ + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + *(ptrd++) = (unsigned char)*(pC2++); + *(ptrd++) = (unsigned char)*(pC3++); + } + } + } break; + case 15 : { // Gray 16-bit + cimg_forY(*this,y){ + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++); + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width); + } + } break; + case 30 : { // Gray w/ Alpha 16-bit + const T *pC1 = data(0,0,0,1); + cimg_forY(*this,y){ + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width); + } + } break; + case 45 : { // RGB 16-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); + cimg_forY(*this,y) { + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + *(ptrd++) = (unsigned short)*(pC2++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width); + } + } break; + case 60 : { // RGB w/ Alpha 16-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); + cimg_forY(*this,y) { + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + *(ptrd++) = (unsigned short)*(pC2++); + *(ptrd++) = (unsigned short)*(pC3++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width); + } + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_write_image(png_ptr,imgData); + png_write_end(png_ptr,info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + // Deallocate Image Write Memory + cimg_forY(*this,n) delete[] imgData[n]; + delete[] imgData; + + if (!file) cimg::fclose(nfile); + return *this; +#endif + } + + //! Save image as a PNM file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving. + **/ + const CImg& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(0,filename,bytes_per_pixel); + } + + //! Save image as a PNM file \overloading. + const CImg& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(file,0,bytes_per_pixel); + } + + const CImg& _save_pnm(std::FILE *const file, const char *const filename, + const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + stmin,stmax,filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL))); + + std::fprintf(nfile,"P%c\n%u %u\n%u\n", + (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535)); + + switch (_spectrum) { + case 1 : { // Scalar image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Binary PGM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + } break; + case 2 : { // RG image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = 0; + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } break; + default : { // RGB image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = (unsigned char)*(ptr_b++); + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = (unsigned short)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PNK file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pnk(const char *const filename) const { + return _save_pnk(0,filename); + } + + //! Save image as a PNK file \overloading. + const CImg& save_pnk(std::FILE *const file) const { + return _save_pnk(file,0); + } + + const CImg& _save_pnk(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnk(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T *ptr = data(0,0,0,0); + + if (!cimg::type::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file + _save_pnm(file,filename,0); + else if (!cimg::type::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D + std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else if (!cimg::type::is_float()) { // Save as P8: Binary int32-valued 3D + if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max()); + else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max()); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + int *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Save as P9: Binary float-valued 3D + if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max()); + else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max()); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PFM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pfm(const char *const filename) const { + get_mirror('y')._save_pfm(0,filename); + return *this; + } + + //! Save image as a PFM file \overloading. + const CImg& save_pfm(std::FILE *const file) const { + get_mirror('y')._save_pfm(file,0); + return *this; + } + + const CImg& _save_pfm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pfm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pfm(): image instance is multispectral, only the three first channels will be saved " + "in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); + + std::fprintf(nfile,"P%c\n%u %u\n1.0\n", + (_spectrum==1?'f':'F'),_width,_height); + + switch (_spectrum) { + case 1 : { // Scalar image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } break; + case 2 : { // RG image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const unsigned int N = std::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } break; + default : { // RGB image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const unsigned int N = std::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = (float)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a RGB file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_rgb(const char *const filename) const { + return _save_rgb(0,filename); + } + + //! Save image as a RGB file \overloading. + const CImg& save_rgb(std::FILE *const file) const { + return _save_rgb(file,0); + } + + const CImg& _save_rgb(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgb(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=3) + cimg::warn(_cimg_instance + "save_rgb(): image instance has not exactly 3 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT wh = (ulongT)_width*_height; + unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0; + switch (_spectrum) { + case 1 : { // Scalar image + for (ulongT k = 0; k& save_rgba(const char *const filename) const { + return _save_rgba(0,filename); + } + + //! Save image as a RGBA file \overloading. + const CImg& save_rgba(std::FILE *const file) const { + return _save_rgba(file,0); + } + + const CImg& _save_rgba(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgba(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=4) + cimg::warn(_cimg_instance + "save_rgba(): image instance has not exactly 4 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT wh = (ulongT)_width*_height; + unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0, + *ptr4 = _spectrum>3?data(0,0,0,3):0; + switch (_spectrum) { + case 1 : { // Scalar images + for (ulongT k = 0; k{ 0=None | 1=LZW | 2=JPEG }. + \param[out] voxel_size Voxel size, to be stored in the filename. + \param[out] description Description, to be stored in the filename. + \param use_bigtiff Allow to save big tiff files (>4Gb). + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tiff is not defined at compile time the + function uses CImg&save_other(const char*). + **/ + const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0, + + const float *const voxel_size=0, const char *const description=0, + const bool use_bigtiff=true) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_tiff(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_tiff + const bool + _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images + TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); + if (tif) { + cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description); + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "save_tiff(): Failed to open file '%s' for writing.", + cimg_instance, + filename); + return *this; +#else + cimg::unused(compression_type,voxel_size,description,use_bigtiff); + return save_other(filename); +#endif + } + +#ifdef cimg_use_tiff + +#define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \ + const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } + + // [internal] Save a plane into a tiff file + template + const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { + if (is_empty() || !tif || pixel_t) return *this; + const char *const filename = TIFFFileName(tif); + uint32 rowsperstrip = (uint32)-1; + uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; + if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; + else photometric = PHOTOMETRIC_MINISBLACK; + TIFFSetDirectory(tif,directory); + TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width); + TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height); + if (voxel_size) { + const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2]; + TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE); + TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx); + TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy); + CImg s_description(256); + cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz); + TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data()); + } + if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description); + TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); + TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); + if (cimg::type::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); + else if (cimg::type::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); + else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); + double valm, valM = max_min(valm); + TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm); + TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM); + TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); + TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); + TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG: + compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE); + rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); + TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); + TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); + TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); + + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + for (unsigned int row = 0; row<_height; row+=rowsperstrip) { + uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif,row,0); + tsize_t i = 0; + for (unsigned int rr = 0; rr& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { + _cimg_save_tiff("bool",unsigned char,compression_type); + _cimg_save_tiff("unsigned char",unsigned char,compression_type); + _cimg_save_tiff("char",char,compression_type); + _cimg_save_tiff("unsigned short",unsigned short,compression_type); + _cimg_save_tiff("short",short,compression_type); + _cimg_save_tiff("unsigned int",unsigned int,compression_type); + _cimg_save_tiff("int",int,compression_type); + _cimg_save_tiff("unsigned int64",unsigned int,compression_type); + _cimg_save_tiff("int64",int,compression_type); + _cimg_save_tiff("float",float,compression_type); + _cimg_save_tiff("double",float,compression_type); + const char *const filename = TIFFFileName(tif); + throw CImgInstanceException(_cimg_instance + "save_tiff(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + return *this; + } +#endif + + //! Save image as a MINC2 file. + /** + \param filename Filename, as a C-string. + \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from. + **/ + const CImg& save_minc2(const char *const filename, + const char *const imitate_file=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_minc2(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_minc2 + cimg::unused(imitate_file); + return save_other(filename); +#else + minc::minc_1_writer wtr; + if (imitate_file) + wtr.open(filename, imitate_file); + else { + minc::minc_info di; + if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X)); + if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y)); + if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z)); + if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME)); + wtr.open(filename,di,1,NC_FLOAT,0); + } + if (pixel_type()==cimg::type::string()) + wtr.setup_write_byte(); + else if (pixel_type()==cimg::type::string()) + wtr.setup_write_int(); + else if (pixel_type()==cimg::type::string()) + wtr.setup_write_double(); + else + wtr.setup_write_float(); + minc::save_standard_volume(wtr, this->_data); + return *this; +#endif + } + + //! Save image as an ANALYZE7.5 or NIFTI file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_analyze(const char *const filename, const float *const voxel_size=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_analyze(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + std::FILE *file; + CImg hname(1024), iname(1024); + const char *const ext = cimg::split_filename(filename); + short datatype = -1; + if (!*ext) { + cimg_snprintf(hname,hname._width,"%s.hdr",filename); + cimg_snprintf(iname,iname._width,"%s.img",filename); + } + if (!cimg::strncasecmp(ext,"hdr",3)) { + std::strcpy(hname,filename); + std::strncpy(iname,filename,iname._width - 1); + cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); + } + if (!cimg::strncasecmp(ext,"img",3)) { + std::strcpy(hname,filename); + std::strncpy(iname,filename,iname._width - 1); + cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); + } + if (!cimg::strncasecmp(ext,"nii",3)) { + std::strncpy(hname,filename,hname._width - 1); *iname = 0; + } + + CImg header(*iname?348:352,1,1,1,0); + int *const iheader = (int*)header._data; + *iheader = 348; + std::strcpy(header._data + 4,"CImg"); + std::strcpy(header._data + 14," "); + ((short*)&(header[36]))[0] = 4096; + ((char*)&(header[38]))[0] = 114; + ((short*)&(header[40]))[0] = 4; + ((short*)&(header[40]))[1] = (short)_width; + ((short*)&(header[40]))[2] = (short)_height; + ((short*)&(header[40]))[3] = (short)_depth; + ((short*)&(header[40]))[4] = (short)_spectrum; + if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; + if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; + if (datatype<0) + throw CImgIOException(_cimg_instance + "save_analyze(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename); + + ((short*)&(header[70]))[0] = datatype; + ((short*)&(header[72]))[0] = sizeof(T); + ((float*)&(header[108]))[0] = (float)(*iname?0:header.width()); + ((float*)&(header[112]))[0] = 1; + ((float*)&(header[76]))[0] = 0; + if (voxel_size) { + ((float*)&(header[76]))[1] = voxel_size[0]; + ((float*)&(header[76]))[2] = voxel_size[1]; + ((float*)&(header[76]))[3] = voxel_size[2]; + } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1; + file = cimg::fopen(hname,"wb"); + cimg::fwrite(header._data,header.width(),file); + if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } + cimg::fwrite(_data,size(),file); + cimg::fclose(file); + return *this; + } + + //! Save image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param is_compressed Tells if the file contains compressed image data. + **/ + const CImg& save_cimg(const char *const filename, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(filename,is_compressed); + return *this; + } + + //! Save image as a .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(file,is_compressed); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Index of the image inside the file. + \param x0 X-coordinate of the sub-image location. + \param y0 Y-coordinate of the sub-image location. + \param z0 Z-coordinate of the sub-image location. + \param c0 C-coordinate of the sub-image location. + **/ + const CImg& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(filename,n0,x0,y0,z0,c0); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,c0); + return *this; + } + + //! Save blank image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param dx Width of the image. + \param dy Height of the image. + \param dz Depth of the image. + \param dc Number of channels of the image. + \note + - All pixel values of the saved image are set to \c 0. + - Use this method to save large images without having to instantiate and allocate them. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dc); + } + + //! Save blank image as a .cimg file \overloading. + /** + Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int) + with a file stream argument instead of a filename string. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(file,1,dx,dy,dz,dc); + } + + //! Save image as an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_inr(const char *const filename, const float *const voxel_size=0) const { + return _save_inr(0,filename,voxel_size); + } + + //! Save image as an INRIMAGE-4 file \overloading. + const CImg& save_inr(std::FILE *const file, const float *const voxel_size=0) const { + return _save_inr(file,0,voxel_size); + } + + const CImg& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_inr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + int inrpixsize = -1; + const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { + inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; + } + if (!cimg::strcasecmp(pixel_type(),"char")) { + inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; + } + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { + inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; + } + if (!cimg::strcasecmp(pixel_type(),"short")) { + inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; + } + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { + inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"int")) { + inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"float")) { + inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"double")) { + inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; + } + if (inrpixsize<=0) + throw CImgIOException(_cimg_instance + "save_inr(): Unsupported pixel type '%s' for file '%s'", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + CImg header(257); + int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", + _width,_height,_depth,_spectrum); + if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", + voxel_size[0],voxel_size[1],voxel_size[2]); + err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + std::memset(header._data + err,'\n',252 - err); + std::memcpy(header._data + 252,"##}\n",4); + cimg::fwrite(header._data,256,nfile); + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as an OpenEXR file. + /** + \param filename Filename, as a C-string. + \note The OpenEXR file format is described here. + **/ + const CImg& save_exr(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_exr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + +#ifndef cimg_use_openexr + return save_other(filename); +#else + Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba; + switch (_spectrum) { + case 1 : { // Grayscale image + for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_rPandore file specifications + for more information). + **/ + const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { + return _save_pandore(0,filename,colorspace); + } + + //! Save image as a Pandore-5 file \overloading. + /** + Same as save_pandore(const char *,unsigned int) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const { + return _save_pandore(file,0,colorspace); + } + + unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { + unsigned int nbdims = 0; + if (id==2 || id==3 || id==4) { + dims[0] = 1; dims[1] = _width; nbdims = 2; + } + if (id==5 || id==6 || id==7) { + dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; + } + if (id==8 || id==9 || id==10) { + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + } + if (id==16 || id==17 || id==18) { + dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; + } + if (id==19 || id==20 || id==21) { + dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; + } + if (id==22 || id==23 || id==25) { + dims[0] = _spectrum; dims[1] = _width; nbdims = 2; + } + if (id==26 || id==27 || id==29) { + dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; + } + if (id==30 || id==31 || id==33) { + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + } + return nbdims; + } + + const CImg& _save_pandore(std::FILE *const file, const char *const filename, + const unsigned int colorspace) const { + +#define __cimg_save_pandore_case(dtype) \ + dtype *buffer = new dtype[size()]; \ + const T *ptrs = _data; \ + cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer + +#define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ + if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \ + (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \ + unsigned int *iheader = (unsigned int*)(header + 12); \ + nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ + cimg::fwrite(header,36,nfile); \ + if (sizeof(unsigned long)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else if (sizeof(unsigned int)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else if (sizeof(unsigned short)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + __cimg_save_pandore_case(unsigned char); \ + } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \ + else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \ + else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ + if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \ + else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pandore(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0,'C','I','m','g',0,0,0,0,0, + 'N','o',' ','d','a','t','e',0,0,0,0 }; + unsigned int nbdims, dims[5] = { 0 }; + bool saved = false; + _cimg_save_pandore_case(1,1,1,"unsigned char",2); + _cimg_save_pandore_case(1,1,1,"char",3); + _cimg_save_pandore_case(1,1,1,"unsigned short",3); + _cimg_save_pandore_case(1,1,1,"short",3); + _cimg_save_pandore_case(1,1,1,"unsigned int",3); + _cimg_save_pandore_case(1,1,1,"int",3); + _cimg_save_pandore_case(1,1,1,"unsigned int64",3); + _cimg_save_pandore_case(1,1,1,"int64",3); + _cimg_save_pandore_case(1,1,1,"float",4); + _cimg_save_pandore_case(1,1,1,"double",4); + + _cimg_save_pandore_case(0,1,1,"unsigned char",5); + _cimg_save_pandore_case(0,1,1,"char",6); + _cimg_save_pandore_case(0,1,1,"unsigned short",6); + _cimg_save_pandore_case(0,1,1,"short",6); + _cimg_save_pandore_case(0,1,1,"unsigned int",6); + _cimg_save_pandore_case(0,1,1,"int",6); + _cimg_save_pandore_case(0,1,1,"unsigned int64",6); + _cimg_save_pandore_case(0,1,1,"int64",6); + _cimg_save_pandore_case(0,1,1,"float",7); + _cimg_save_pandore_case(0,1,1,"double",7); + + _cimg_save_pandore_case(0,0,1,"unsigned char",8); + _cimg_save_pandore_case(0,0,1,"char",9); + _cimg_save_pandore_case(0,0,1,"unsigned short",9); + _cimg_save_pandore_case(0,0,1,"short",9); + _cimg_save_pandore_case(0,0,1,"unsigned int",9); + _cimg_save_pandore_case(0,0,1,"int",9); + _cimg_save_pandore_case(0,0,1,"unsigned int64",9); + _cimg_save_pandore_case(0,0,1,"int64",9); + _cimg_save_pandore_case(0,0,1,"float",10); + _cimg_save_pandore_case(0,0,1,"double",10); + + _cimg_save_pandore_case(0,1,3,"unsigned char",16); + _cimg_save_pandore_case(0,1,3,"char",17); + _cimg_save_pandore_case(0,1,3,"unsigned short",17); + _cimg_save_pandore_case(0,1,3,"short",17); + _cimg_save_pandore_case(0,1,3,"unsigned int",17); + _cimg_save_pandore_case(0,1,3,"int",17); + _cimg_save_pandore_case(0,1,3,"unsigned int64",17); + _cimg_save_pandore_case(0,1,3,"int64",17); + _cimg_save_pandore_case(0,1,3,"float",18); + _cimg_save_pandore_case(0,1,3,"double",18); + + _cimg_save_pandore_case(0,0,3,"unsigned char",19); + _cimg_save_pandore_case(0,0,3,"char",20); + _cimg_save_pandore_case(0,0,3,"unsigned short",20); + _cimg_save_pandore_case(0,0,3,"short",20); + _cimg_save_pandore_case(0,0,3,"unsigned int",20); + _cimg_save_pandore_case(0,0,3,"int",20); + _cimg_save_pandore_case(0,0,3,"unsigned int64",20); + _cimg_save_pandore_case(0,0,3,"int64",20); + _cimg_save_pandore_case(0,0,3,"float",21); + _cimg_save_pandore_case(0,0,3,"double",21); + + _cimg_save_pandore_case(1,1,0,"unsigned char",22); + _cimg_save_pandore_case(1,1,0,"char",23); + _cimg_save_pandore_case(1,1,0,"unsigned short",23); + _cimg_save_pandore_case(1,1,0,"short",23); + _cimg_save_pandore_case(1,1,0,"unsigned int",23); + _cimg_save_pandore_case(1,1,0,"int",23); + _cimg_save_pandore_case(1,1,0,"unsigned int64",23); + _cimg_save_pandore_case(1,1,0,"int64",23); + _cimg_save_pandore_case(1,1,0,"float",25); + _cimg_save_pandore_case(1,1,0,"double",25); + + _cimg_save_pandore_case(0,1,0,"unsigned char",26); + _cimg_save_pandore_case(0,1,0,"char",27); + _cimg_save_pandore_case(0,1,0,"unsigned short",27); + _cimg_save_pandore_case(0,1,0,"short",27); + _cimg_save_pandore_case(0,1,0,"unsigned int",27); + _cimg_save_pandore_case(0,1,0,"int",27); + _cimg_save_pandore_case(0,1,0,"unsigned int64",27); + _cimg_save_pandore_case(0,1,0,"int64",27); + _cimg_save_pandore_case(0,1,0,"float",29); + _cimg_save_pandore_case(0,1,0,"double",29); + + _cimg_save_pandore_case(0,0,0,"unsigned char",30); + _cimg_save_pandore_case(0,0,0,"char",31); + _cimg_save_pandore_case(0,0,0,"unsigned short",31); + _cimg_save_pandore_case(0,0,0,"short",31); + _cimg_save_pandore_case(0,0,0,"unsigned int",31); + _cimg_save_pandore_case(0,0,0,"int",31); + _cimg_save_pandore_case(0,0,0,"unsigned int64",31); + _cimg_save_pandore_case(0,0,0,"int64",31); + _cimg_save_pandore_case(0,0,0,"float",33); + _cimg_save_pandore_case(0,0,0,"double",33); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a raw data file. + /** + \param filename Filename, as a C-string. + \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false). + \note The .raw format does not store the image dimensions in the output file, + so you have to keep track of them somewhere to be able to read the file correctly afterwards. + **/ + const CImg& save_raw(const char *const filename, const bool is_multiplexed=false) const { + return _save_raw(0,filename,is_multiplexed); + } + + //! Save image as a raw data file \overloading. + /** + Same as save_raw(const char *,bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_raw(std::FILE *const file, const bool is_multiplexed=false) const { + return _save_raw(file,0,is_multiplexed); + } + + const CImg& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_raw(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (pixel_type()==cimg::type::string()) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = _bool2uchar(siz,is_multiplexed); + cimg::fwrite(buf,siz,nfile); + delete[] buf; + } else { // Non boolean data + if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed + else { // Multiplexed + CImg buf(_spectrum); + cimg_forXYZ(*this,x,y,z) { + cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c); + cimg::fwrite(buf._data,_spectrum,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + // Return unsigned char buffer that encodes data of a CImg instance bitwise. + // (buffer needs to be deallocated afterwards, with delete[]). + const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const { + const ulongT _siz = size(); + siz = _siz/8 + (_siz%8?1:0); + unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0; + + if (!is_multiplexed || _spectrum==1) // Non-multiplexed + cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }} + else // Multiplexed + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) { + (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; } + } + if (bit) *ptrd = val; + return buf; + } + + // Fill CImg instance from bitwise data encoded in an unsigned char buffer. + void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) { + const ulongT S = std::min(siz*8,size()); + const unsigned char *ptrs = buf; + unsigned char val = 0, mask = 0; + T *ptrd = _data; + if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed + for (ulongT off = 0; off>=1)) { val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?1:0); + } + else if (S) { // Multiplexed + ulongT off = 0; + for (int z = 0; z>=1)) { val = *(ptrs++); ++off; mask = 128; } + (*this)(x,y,z,c) = (T)((val&mask)?1:0); + } + } + } + + //! Save image as a .yuv video file. + /** + \param filename Filename, as a C-string. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false). + \note Each slice of the instance image is considered to be a single frame of the output video file. + **/ + const CImg& save_yuv(const char *const filename, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(filename,chroma_subsampling,is_rgb); + return *this; + } + + //! Save image as a .yuv video file \overloading. + /** + Same as save_yuv(const char*,const unsigned int,const bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(file,chroma_subsampling,is_rgb); + return *this; + } + + //! Save 3D object as an Object File Format (.off) file. + /** + \param filename Filename, as a C-string. + \param primitives List of 3D object primitives. + \param colors List of 3D object colors. + \note + - Instance image contains the vertices data of the 3D object. + - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format. + Such primitives will be lost or simplified during file saving. + - The .off file format is described here. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + const char *const filename) const { + return _save_off(primitives,colors,0,filename); + } + + //! Save 3D object as an Object File Format (.off) file \overloading. + /** + Same as save_off(const CImgList&,const CImgList&,const char*) const + with a file stream argument instead of a filename string. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file) const { + return _save_off(primitives,colors,file,0); + } + + template + const CImg& _save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_off(): Specified filename is (null).", + cimg_instance); + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "save_off(): Empty instance, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + CImgList opacities; + CImg error_message(1024); + if (!is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgInstanceException(_cimg_instance + "save_off(): Invalid specified 3D object, for file '%s' (%s).", + cimg_instance, + filename?filename:"(FILE*)",error_message.data()); + + const CImg default_color(1,3,1,1,(tc)std::min((int)cimg::type::max(),200)); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + unsigned int supported_primitives = 0; + cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives; + std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width); + cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n", + (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); + cimglist_for(primitives,l) { + const CImg& color = l1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f; + switch (psiz) { + case 1 : std::fprintf(nfile,"1 %u %f %f %f\n", + (unsigned int)primitives(l,0),r,g,b); break; + case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + (unsigned int)primitives(l,1),r,g,b); break; + case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; + case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 6 : { + const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 9 : { + const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + (unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 12 : { + const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save volumetric image as a video (using the OpenCV library when available). + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). + \param keep_open Tells if the video writer associated to the specified filename + must be kept open or not (to allow frames to be added in the same file afterwards). + **/ + const CImg& save_video(const char *const filename, const unsigned int fps=25, + const char *codec=0, const bool keep_open=false) const { + if (is_empty()) { CImgList().save_video(filename,fps,codec,keep_open); return *this; } + CImgList list; + get_split('z').move_to(list); + list.save_video(filename,fps,codec,keep_open); + return *this; + } + + //! Save volumetric image as a video, using custom external binary. + /** + \param filename Filename, as a C-string. + \param fps Video framerate. + \param codec Video codec, as a C-string. + \param bitrate Video bitrate. + \note + - Each slice of the instance image is considered to be a single frame of the output video file. + - This method uses \c custom, an external executable binary provided by + custom. + It must be installed for the method to succeed. + **/ + const CImg& save_custom_external(const char *const filename, const unsigned int fps=25, + const char *const codec=0, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_custom_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImgList list; + get_split('z').move_to(list); + list.save_custom_external(filename,fps,codec,bitrate); + return *this; + } + + //! Save image using gzip external binary. + /** + \param filename Filename, as a C-string. + \note This method uses \c gzip, an external executable binary provided by + gzip. + It must be installed for the method to succeed. + **/ + const CImg& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_gzip_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::gzip_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimg_instance, + filename); + + else cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image using GraphicsMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c gm, an external executable binary provided by + GraphicsMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "GraphicsMagick only writes the first image slice.", + cimg_instance,filename); + +#ifdef cimg_use_png +#define _cimg_sge_extension1 "png" +#define _cimg_sge_extension2 "png" +#else +#define _cimg_sge_extension1 "pgm" +#define _cimg_sge_extension2 "ppm" +#endif + CImg command(1024), filename_tmp(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(), + _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + +#ifdef cimg_use_png + save_png(filename_tmp); +#else + save_pnm(filename_tmp); +#endif + cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"", + cimg::graphicsmagick_path(),quality, + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::graphicsmagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image using ImageMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c convert, an external executable binary provided by + ImageMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_imagemagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "ImageMagick only writes the first image slice.", + cimg_instance,filename); +#ifdef cimg_use_png +#define _cimg_sie_extension1 "png" +#define _cimg_sie_extension2 "png" +#else +#define _cimg_sie_extension1 "pgm" +#define _cimg_sie_extension2 "ppm" +#endif + CImg command(1024), filename_tmp(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(), + cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); +#ifdef cimg_use_png + save_png(filename_tmp); +#else + save_pnm(filename_tmp); +#endif + cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"", + cimg::imagemagick_path(),quality, + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::imagemagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_imagemagick_external(): Failed to save file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image as a Dicom file. + /** + \param filename Filename, as a C-string. + \note This method uses \c medcon, an external executable binary provided by + (X)Medcon. + It must be installed for the method to succeed. + **/ + const CImg& save_medcon_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_medcon_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImg command(1024), filename_tmp(256), body(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save_analyze(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::medcon_path()); + std::remove(filename_tmp); + cimg::split_filename(filename_tmp,body); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data); + std::remove(filename_tmp); + + file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"m000-%s",filename); + file = cimg::std_fopen(command,"rb"); + if (!file) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + std::rename(command,filename); + return *this; + } + + // Save image for non natively supported formats. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note + - The filename extension tells about the desired file format. + - This method tries to save the instance image as a file, using external tools from + ImageMagick or + GraphicsMagick. + At least one of these tool must be installed for the method to succeed. + - It is recommended to use the generic method save(const char*, int) const instead, + as it can handle some file formats natively. + **/ + const CImg& save_other(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_other(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "ImageMagick or GraphicsMagick only writes the first image slice.", + cimg_instance,filename); + + const unsigned int omode = cimg::exception_mode(); + bool is_saved = true; + cimg::exception_mode(0); + try { save_magick(filename); } + catch (CImgException&) { + try { save_imagemagick_external(filename,quality); } + catch (CImgException&) { + try { save_graphicsmagick_external(filename,quality); } + catch (CImgException&) { + is_saved = false; + } + } + } + cimg::exception_mode(omode); + if (!is_saved) + throw CImgIOException(_cimg_instance + "save_other(): Failed to save file '%s'. Format is not natively supported, " + "and no external commands succeeded.", + cimg_instance, + filename); + return *this; + } + + //! Serialize a CImg instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { + return CImgList(*this,true).get_serialize(is_compressed); + } + + // [internal] Return a 40x38 color logo of a 'danger' item. + static CImg _logo40x38() { + CImg res(40,38,1,3); + const unsigned char *ptrs = cimg::logo40x38; + T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2); + for (ulongT off = 0; off<(ulongT)res._width*res._height;) { + const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++); + for (unsigned int l = 0; l structure + # + # + # + #------------------------------------------ + */ + //! Represent a list of images CImg. + template + struct CImgList { + unsigned int _width, _allocated_width; + CImg *_data; + + //! Simple iterator type, to loop through each image of a list. + /** + \note + - The \c CImgList::iterator type is defined as a CImg*. + - You may use it like this: + \code + CImgList<> list; // Assuming this image list is not empty + for (CImgList<>::iterator it = list.begin(); it* iterator; + + //! Simple const iterator type, to loop through each image of a \c const list instance. + /** + \note + - The \c CImgList::const_iterator type is defined to be a const CImg*. + - Similar to CImgList::iterator, but for constant list instances. + **/ + typedef const CImg* const_iterator; + + //! Pixel value type. + /** + Refer to the pixels value type of the images in the list. + \note + - The \c CImgList::value_type type of a \c CImgList is defined to be a \c T. + It is then similar to CImg::value_type. + - \c CImgList::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimglist_plugin +#include cimglist_plugin +#endif +#ifdef cimglist_plugin1 +#include cimglist_plugin1 +#endif +#ifdef cimglist_plugin2 +#include cimglist_plugin2 +#endif +#ifdef cimglist_plugin3 +#include cimglist_plugin3 +#endif +#ifdef cimglist_plugin4 +#include cimglist_plugin4 +#endif +#ifdef cimglist_plugin5 +#include cimglist_plugin5 +#endif +#ifdef cimglist_plugin6 +#include cimglist_plugin6 +#endif +#ifdef cimglist_plugin7 +#include cimglist_plugin7 +#endif +#ifdef cimglist_plugin8 +#include cimglist_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + Destroy current list instance. + \note + - Any allocated buffer is deallocated. + - Destroying an empty list does nothing actually. + **/ + ~CImgList() { + delete[] _data; + } + + //! Default constructor. + /** + Construct a new empty list instance. + \note + - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its + image buffer pointer data(). + - An empty list may be reassigned afterwards, with the family of the assign() methods. + In all cases, the type of pixels stays \c T. + **/ + CImgList(): + _width(0),_allocated_width(0),_data(0) {} + + //! Construct list containing empty images. + /** + \param n Number of empty images. + \note Useful when you know by advance the number of images you want to manage, as + it will allocate the right amount of memory for the list, without needs for reallocation + (that may occur when starting from an empty list and inserting several images in it). + **/ + explicit CImgList(const unsigned int n):_width(n) { + if (n) _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + else { _allocated_width = 0; _data = 0; } + } + + //! Construct list containing images of specified size. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \note Pixel values are not initialized and may probably contain garbage. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + } + + //! Construct list containing images of specified size, and initialize pixel values. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val Initialization value for images pixels. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T& val): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + } + + //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val0 First value of the initializing integers sequence. + \param val1 Second value of the initializing integers sequence. + \warning You must specify at least width*height*depth*spectrum values in your argument list, + or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...): + _width(0),_allocated_width(0),_data(0) { +#define _CImgList_stdarg(t) { \ + assign(n,width,height,depth,spectrum); \ + const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \ + T *ptrd = _data->_data; \ + va_list ap; \ + va_start(ap,val1); \ + for (ulongT l = 0, s = 0, i = 0; iwidth*height*depth*spectrum values in your argument list, + or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...): + _width(0),_allocated_width(0),_data(0) { + _CImgList_stdarg(double); + } + + //! Construct list containing copies of an input image. + /** + \param n Number of images. + \param img Input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img. + **/ + template + CImgList(const unsigned int n, const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + } + + //! Construct list from one image. + /** + \param img Input image to copy in the constructed list. + \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img. + **/ + template + explicit CImgList(const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(1); + _data[0].assign(img,is_shared); + } + + //! Construct list from two images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + } + + //! Construct list from three images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + } + + //! Construct list from four images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); + } + + //! Construct list from five images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); + } + + //! Construct list from six images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + } + + //! Construct list from seven images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); + } + + //! Construct list from eight images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param img8 Eighth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + } + + //! Construct list copy. + /** + \param list Input list to copy. + \note The shared state of each element of the constructed list is kept the same as in \c list. + **/ + template + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + } + + //! Construct list copy \specialization. + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared); + } + + //! Construct list copy, and force the shared state of the list elements. + /** + \param list Input list to copy. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImgList& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],is_shared); + } + + //! Construct list by reading the content of a file. + /** + \param filename Filename, as a C-string. + **/ + explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) { + assign(filename); + } + + //! Construct list from the content of a display window. + /** + \param disp Display window to get content from. + \note Constructed list contains a single image only. + **/ + explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) { + assign(disp); + } + + //! Return a list with elements being shared copies of images in the list instance. + /** + \note list2 = list1.get_shared() is equivalent to list2.assign(list1,true). + **/ + CImgList get_shared() { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Return a list with elements being shared copies of images in the list instance \const. + const CImgList get_shared() const { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Destructor \inplace. + /** + \see CImgList(). + **/ + CImgList& assign() { + delete[] _data; + _width = _allocated_width = 0; + _data = 0; + return *this; + } + + //! Destructor \inplace. + /** + Equivalent to assign(). + \note Only here for compatibility with STL naming conventions. + **/ + CImgList& clear() { + return assign(); + } + + //! Construct list containing empty images \inplace. + /** + \see CImgList(unsigned int). + **/ + CImgList& assign(const unsigned int n) { + if (!n) return assign(); + if (_allocated_width(n<<2)) { + delete[] _data; + _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + } + _width = n; + return *this; + } + + //! Construct list containing images of specified size \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + return *this; + } + + //! Construct list containing images of specified size, and initialize pixel values \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T& val) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + return *this; + } + + //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) { + _CImgList_stdarg(int); + return *this; + } + + //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace. + /** + \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, + const double val0, const double val1, ...) { + _CImgList_stdarg(double); + return *this; + } + + //! Construct list containing copies of an input image \inplace. + /** + \see CImgList(unsigned int, const CImg&, bool). + **/ + template + CImgList& assign(const unsigned int n, const CImg& img, const bool is_shared=false) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + return *this; + } + + //! Construct list from one image \inplace. + /** + \see CImgList(const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img, const bool is_shared=false) { + assign(1); + _data[0].assign(img,is_shared); + return *this; + } + + //! Construct list from two images \inplace. + /** + \see CImgList(const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const bool is_shared=false) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + return *this; + } + + //! Construct list from three images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + return *this; + } + + //! Construct list from four images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); + return *this; + } + + //! Construct list from five images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); + return *this; + } + + //! Construct list from six images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + return *this; + } + + //! Construct list from seven images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); + return *this; + } + + //! Construct list from eight images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + return *this; + } + + //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace. + /** + \see CImgList(const CImgList&, bool is_shared). + **/ + template + CImgList& assign(const CImgList& list, const bool is_shared=false) { + cimg::unused(is_shared); + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + return *this; + } + + //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization. + CImgList& assign(const CImgList& list, const bool is_shared=false) { + if (this==&list) return *this; + CImgList res(list._width); + cimglist_for(res,l) res[l].assign(list[l],is_shared); + return res.move_to(*this); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& assign(const char *const filename) { + return load(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& assign(const CImgDisplay &disp) { + return assign(CImg(disp)); + } + + //! Transfer the content of the list instance to another list. + /** + \param list Destination list. + \note When returning, the current list instance is empty and the initial content of \c list is destroyed. + **/ + template + CImgList& move_to(CImgList& list) { + list.assign(_width); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[l]); + assign(); + return list; + } + + //! Transfer the content of the list instance at a specified position in another list. + /** + \param list Destination list. + \param pos Index of the insertion in the list. + \note When returning, the list instance is empty and the initial content of \c list is preserved + (only images indexes may be modified). + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos) { + if (is_empty()) return list; + const unsigned int npos = pos>list._width?list._width:pos; + list.insert(_width,npos); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[npos + l]); + assign(); + return list; + } + + //! Swap all fields between two list instances. + /** + \param list List to swap fields with. + \note Can be used to exchange the content of two lists in a fast way. + **/ + CImgList& swap(CImgList& list) { + cimg::swap(_width,list._width,_allocated_width,list._allocated_width); + cimg::swap(_data,list._data); + return list; + } + + //! Return a reference to an empty list. + /** + \note Can be used to define default values in a function taking a CImgList as an argument. + \code + void f(const CImgList& list=CImgList::empty()); + \endcode + **/ + static CImgList& empty() { + static CImgList _empty; + return _empty.assign(); + } + + //! Return a reference to an empty list \const. + static const CImgList& const_empty() { + static const CImgList _empty; + return _empty; + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Return a reference to one image element of the list. + /** + \param pos Index of the image element. + **/ + CImg& operator()(const unsigned int pos) { +#if cimg_verbosity>=3 + if (pos>=_width) { + cimg::warn(_cimglist_instance + "operator(): Invalid image request, at position [%u].", + cimglist_instance, + pos); + return *_data; + } +#endif + return _data[pos]; + } + + //! Return a reference to one image of the list. + /** + \param pos Index of the image element. + **/ + const CImg& operator()(const unsigned int pos) const { + return const_cast*>(this)->operator()(pos); + } + + //! Return a reference to one pixel value of one image of the list. + /** + \param pos Index of the image element. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list(n,x,y,z,c) is equivalent to list[n](x,y,z,c). + **/ + T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + return (*this)[pos](x,y,z,c); + } + + //! Return a reference to one pixel value of one image of the list \const. + const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return (*this)[pos](x,y,z,c); + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + operator CImg*() { + return _data; + } + + //! Return pointer to the first image of the list \const. + operator const CImg*() const { + return _data; + } + + //! Construct list from one image \inplace. + /** + \param img Input image to copy in the constructed list. + \note list = img; is equivalent to list.assign(img);. + **/ + template + CImgList& operator=(const CImg& img) { + return assign(img); + } + + //! Construct list from another list. + /** + \param list Input list to copy. + \note list1 = list2 is equivalent to list1.assign(list2);. + **/ + template + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list from another list \specialization. + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& operator=(const char *const filename) { + return assign(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return a non-shared copy of a list. + /** + \note +list is equivalent to CImgList(list,false). + It forces the copy to have non-shared elements. + **/ + CImgList operator+() const { + return CImgList(*this,false); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end. + /** + \param img Image inserted at the end of the instance copy. + \note Define a convenient way to create temporary lists of images, as in the following code: + \code + (img1,img2,img3,img4).display("My four images"); + \endcode + **/ + template + CImgList& operator,(const CImg& img) { + return insert(img); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end \const. + template + CImgList operator,(const CImg& img) const { + return (+*this).insert(img); + } + + //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end. + /** + \param list List inserted at the end of the instance copy. + **/ + template + CImgList& operator,(const CImgList& list) { + return insert(list); + } + + //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const. + template + CImgList& operator,(const CImgList& list) const { + return (+*this).insert(list); + } + + //! Return image corresponding to the appending of all images of the instance list along specified axis. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \note list>'x' is equivalent to list.get_append('x'). + **/ + CImg operator>(const char axis) const { + return get_append(axis,0); + } + + //! Return list corresponding to the splitting of all images of the instance list along specified axis. + /** + \param axis Axis used for image splitting. + \note list<'x' is equivalent to list.get_split('x'). + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to size() but returns result as a (signed) integer. + **/ + int width() const { + return (int)_width; + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to width() but returns result as an unsigned integer. + **/ + unsigned int size() const { + return _width; + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + CImg *data() { + return _data; + } + + //! Return pointer to the first image of the list \const. + const CImg *data() const { + return _data; + } + + //! Return pointer to the pos-th image of the list. + /** + \param pos Index of the image element to access. + \note list.data(n); is equivalent to list.data + n;. + **/ +#if cimg_verbosity>=3 + CImg *data(const unsigned int pos) { + if (pos>=size()) + cimg::warn(_cimglist_instance + "data(): Invalid pointer request, at position [%u].", + cimglist_instance, + pos); + return _data + pos; + } + + const CImg *data(const unsigned int l) const { + return const_cast*>(this)->data(l); + } +#else + CImg *data(const unsigned int l) { + return _data + l; + } + + //! Return pointer to the pos-th image of the list \const. + const CImg *data(const unsigned int l) const { + return _data + l; + } +#endif + + //! Return iterator to the first image of the list. + /** + **/ + iterator begin() { + return _data; + } + + //! Return iterator to the first image of the list \const. + const_iterator begin() const { + return _data; + } + + //! Return iterator to one position after the last image of the list. + /** + **/ + iterator end() { + return _data + _width; + } + + //! Return iterator to one position after the last image of the list \const. + const_iterator end() const { + return _data + _width; + } + + //! Return reference to the first image of the list. + /** + **/ + CImg& front() { + return *_data; + } + + //! Return reference to the first image of the list \const. + const CImg& front() const { + return *_data; + } + + //! Return a reference to the last image of the list. + /** + **/ + const CImg& back() const { + return *(_data + _width - 1); + } + + //! Return a reference to the last image of the list \const. + CImg& back() { + return *(_data + _width - 1); + } + + //! Return pos-th image of the list. + /** + \param pos Index of the image element to access. + **/ + CImg& at(const int pos) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "at(): Empty instance.", + cimglist_instance); + + return _data[cimg::cut(pos,0,width() - 1)]; + } + + //! Access to pixel value with Dirichlet boundary conditions. + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions. + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); + } + + T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); + } + + //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); + } + + T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); + } + + T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const. + T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const. + T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); + } + + T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const. + T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const. + T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); + } + + T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Return \c true if list is empty. + /** + **/ + bool is_empty() const { + return (!_data || !_width); + } + + //! Test if number of image elements is equal to specified value. + /** + \param size_n Number of image elements to test. + **/ + bool is_sameN(const unsigned int size_n) const { + return _width==size_n; + } + + //! Test if number of image elements is equal between two images lists. + /** + \param list Input list to compare with. + **/ + template + bool is_sameN(const CImgList& list) const { + return is_sameN(list._width); + } + + // Define useful functions to check list dimensions. + // (cannot be documented because macro-generated). +#define _cimglist_def_is_same1(axis) \ + bool is_same##axis(const unsigned int val) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \ + return res; \ + } \ + bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \ + return is_sameN(n) && is_same##axis(val); \ + } \ + +#define _cimglist_def_is_same2(axis1,axis2) \ + bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \ + return res; \ + } \ + bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \ + return is_sameN(n) && is_same##axis1##axis2(val1,val2); \ + } \ + +#define _cimglist_def_is_same3(axis1,axis2,axis3) \ + bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \ + const unsigned int val3) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \ + return res; \ + } \ + bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \ + const unsigned int val2, const unsigned int val3) const { \ + return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \ + } \ + +#define _cimglist_def_is_same(axis) \ + template bool is_same##axis(const CImg& img) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \ + return res; \ + } \ + template bool is_same##axis(const CImgList& list) const { \ + const unsigned int lmin = std::min(_width,list._width); \ + bool res = true; \ + for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ + return (is_sameN(n) && is_same##axis(img)); \ + } \ + template bool is_sameN##axis(const CImgList& list) const { \ + return (is_sameN(list) && is_same##axis(list)); \ + } + + _cimglist_def_is_same(XY) + _cimglist_def_is_same(XZ) + _cimglist_def_is_same(XC) + _cimglist_def_is_same(YZ) + _cimglist_def_is_same(YC) + _cimglist_def_is_same(XYZ) + _cimglist_def_is_same(XYC) + _cimglist_def_is_same(YZC) + _cimglist_def_is_same(XYZC) + _cimglist_def_is_same1(X) + _cimglist_def_is_same1(Y) + _cimglist_def_is_same1(Z) + _cimglist_def_is_same1(C) + _cimglist_def_is_same2(X,Y) + _cimglist_def_is_same2(X,Z) + _cimglist_def_is_same2(X,C) + _cimglist_def_is_same2(Y,Z) + _cimglist_def_is_same2(Y,C) + _cimglist_def_is_same2(Z,C) + _cimglist_def_is_same3(X,Y,Z) + _cimglist_def_is_same3(X,Y,C) + _cimglist_def_is_same3(X,Z,C) + _cimglist_def_is_same3(Y,Z,C) + + //! Test if dimensions of each image of the list match specified arguments. + /** + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameXYZC(const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) const { + bool res = true; + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc); + return res; + } + + //! Test if list dimensions match specified arguments. + /** + \param n Number of images in the list. + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameNXYZC(const unsigned int n, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) const { + return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc); + } + + //! Test if list contains one particular pixel location. + /** + \param n Index of the image whom checked pixel value belong to. + \param x X-coordinate of the checked pixel value. + \param y Y-coordinate of the checked pixel value. + \param z Z-coordinate of the checked pixel value. + \param c C-coordinate of the checked pixel value. + **/ + bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) return false; + return n>=0 && n=0 && x<_data[n].width() && y>=0 && y<_data[n].height() && + z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum(); + } + + //! Test if list contains image with specified index. + /** + \param n Index of the checked image. + **/ + bool containsN(const int n) const { + if (is_empty()) return false; + return n>=0 && n + bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const { + if (is_empty()) return false; + cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; } + return false; + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \param[out] z Z-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y,z). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { + t c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y) const { + t z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x). + **/ + template + bool contains(const T& pixel, t& n, t& x) const { + t y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \note If true, set coordinates (n). + **/ + template + bool contains(const T& pixel, t& n) const { + t x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + **/ + bool contains(const T& pixel) const { + unsigned int n, x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if the list contains the image 'img'. + /** + \param img Reference to image to test. + \param[out] n Index of image in the list, if test succeeds. + \note If true, returns the position (n) of the image in the list. + **/ + template + bool contains(const CImg& img, t& n) const { + if (is_empty()) return false; + const CImg *const ptr = &img; + cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; } + return false; + } + + //! Test if the list contains the image img. + /** + \param img Reference to image to test. + **/ + bool contains(const CImg& img) const { + unsigned int n; + return contains(img,n); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + //! Return a reference to the minimum pixel value of the instance list. + /** + **/ + T& min() { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the maximum pixel value of the instance list \const. + const T& max() const { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T max_value = *ptr_max; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + T& min_max(t& max_val) { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min_max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + const T& min_max(t& max_val) const { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min_max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well. + /** + \param[out] min_val Value of the minimum value found. + **/ + template + T& max_min(t& min_val) { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max_min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max_min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " + "at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + if (is_shared) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified shared image " + "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).", + cimglist_instance, + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos); + + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list + _data = new_data; + *_data = img; + } else { + if (new_data) { // Insert with re-allocation + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); + delete[] _data; + _data = new_data; + } else if (npos!=_width - 1) // Insert without re-allocation + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; + _data[npos] = img; + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization. + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " + "at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list + _data = new_data; + if (is_shared && img) { + _data->_width = img._width; + _data->_height = img._height; + _data->_depth = img._depth; + _data->_spectrum = img._spectrum; + _data->_is_shared = true; + _data->_data = img._data; + } else *_data = img; + } + else { + if (new_data) { // Insert with re-allocation + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + if (is_shared && img) { + new_data[npos]._width = img._width; + new_data[npos]._height = img._height; + new_data[npos]._depth = img._depth; + new_data[npos]._spectrum = img._spectrum; + new_data[npos]._is_shared = true; + new_data[npos]._data = img._data; + } else { + new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; + new_data[npos]._data = 0; + new_data[npos] = img; + } + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); + delete[] _data; + _data = new_data; + } else { // Insert without re-allocation + if (npos!=_width - 1) + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + if (is_shared && img) { + _data[npos]._width = img._width; + _data[npos]._height = img._height; + _data[npos]._depth = img._depth; + _data[npos]._spectrum = img._spectrum; + _data[npos]._is_shared = true; + _data[npos]._data = img._data; + } else { + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; + _data[npos] = img; + } + } + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance. + template + CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(img,pos,is_shared); + } + + //! Insert n empty images img into the current image list, at position \p pos. + /** + \param n Number of empty images to insert. + \param pos Index of the insertion. + **/ + CImgList& insert(const unsigned int n, const unsigned int pos=~0U) { + CImg empty; + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i get_insert(const unsigned int n, const unsigned int pos=~0U) const { + return (+*this).insert(n,pos); + } + + //! Insert \c n copies of the image \c img into the current image list, at position \c pos. + /** + \param n Number of image copies to insert. + \param img Image to insert by copy. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of \c img or not. + **/ + template + CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, + const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + insert(img,npos,is_shared); + for (unsigned int i = 1; i + CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,img,pos,is_shared); + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos. + /** + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared); + else insert(CImgList(list),npos,is_shared); + return *this; + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance. + template + CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(list,pos,is_shared); + } + + //! Insert n copies of the list \c list at position \c pos of the current list. + /** + \param n Number of list copies to insert. + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i + CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,list,pos,is_shared); + } + + //! Remove all images between from indexes. + /** + \param pos1 Starting index of the removal. + \param pos2 Ending index of the removal. + **/ + CImgList& remove(const unsigned int pos1, const unsigned int pos2) { + const unsigned int + npos1 = pos1=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + else { + if (tpos2>=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + + for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign(); + const unsigned int nb = 1 + npos2 - npos1; + if (!(_width-=nb)) return assign(); + if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation + if (npos1!=_width) + std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + std::memset((void*)(_data + _width),0,sizeof(CImg)*nb); + } else { // Removing items with reallocation + _allocated_width>>=4; + while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; + CImg *const new_data = new CImg[_allocated_width]; + if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos1); + if (npos1!=_width) + std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + if (_width!=_allocated_width) + std::memset((void*)(new_data + _width),0,sizeof(CImg)*(_allocated_width - _width)); + std::memset((void*)_data,0,sizeof(CImg)*(_width + nb)); + delete[] _data; + _data = new_data; + } + } + return *this; + } + + //! Remove all images between from indexes \newinstance. + CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { + return (+*this).remove(pos1,pos2); + } + + //! Remove image at index \c pos from the image list. + /** + \param pos Index of the image to remove. + **/ + CImgList& remove(const unsigned int pos) { + return remove(pos,pos); + } + + //! Remove image at index \c pos from the image list \newinstance. + CImgList get_remove(const unsigned int pos) const { + return (+*this).remove(pos); + } + + //! Remove last image. + /** + **/ + CImgList& remove() { + return remove(_width - 1); + } + + //! Remove last image \newinstance. + CImgList get_remove() const { + return (+*this).remove(); + } + + //! Reverse list order. + CImgList& reverse() { + for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]); + return *this; + } + + //! Reverse list order \newinstance. + CImgList get_reverse() const { + return (+*this).reverse(); + } + + //! Return a sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList& images(const unsigned int pos0, const unsigned int pos1) { + return get_images(pos0,pos1).move_to(*this); + } + + //! Return a sublist \newinstance. + CImgList get_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l]); + return res; + } + + //! Return a shared sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); + return res; + } + + //! Return a shared sublist \newinstance. + const CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); + return res; + } + + //! Return a single image which is the appending of all images of the current CImgList instance. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg get_append(const char axis, const float align=0) const { + if (is_empty()) return CImg(); + if (_width==1) return +((*this)[0]); + unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0; + CImg res; + switch (cimg::lowercase(axis)) { + case 'x' : { // Along the X-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx+=img._width; + dy = std::max(dy,img._height); + dz = std::max(dz,img._depth); + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._height==1 && img._depth==1 && img._spectrum==1 && + res._height==1 && res._depth==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._width); + else + res.draw_image(pos, + (int)(align*(dy - img._height)), + (int)(align*(dz - img._depth)), + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._width; + } + } break; + case 'y' : { // Along the Y-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy+=img._height; + dz = std::max(dz,img._depth); + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._depth==1 && img._spectrum==1 && + res._width==1 && res._depth==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._height); + else + res.draw_image((int)(align*(dx - img._width)), + pos, + (int)(align*(dz - img._depth)), + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._height; + } + } break; + case 'z' : { // Along the Z-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy = std::max(dy,img._height); + dz+=img._depth; + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._height==1 && img._spectrum==1 && + res._width==1 && res._height==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._depth); + else + res.draw_image((int)(align*(dx - img._width)), + (int)(align*(dy - img._height)), + pos, + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._depth; + } + } break; + default : { // Along the C-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy = std::max(dy,img._height); + dz = std::max(dz,img._depth); + dc+=img._spectrum; + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._height==1 && img._depth==1 && + res._width==1 && res._height==1 && res._depth==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum); + else + res.draw_image((int)(align*(dx - img._width)), + (int)(align*(dy - img._height)), + (int)(align*(dz - img._depth)), + pos, + img); + } + pos+=img._spectrum; + } + } + } + return res; + } + + //! Return a list where each image has been split along the specified axis. + /** + \param axis Axis to split images along. + \param nb Number of split parts for each image. + **/ + CImgList& split(const char axis, const int nb=-1) { + return get_split(axis,nb).move_to(*this); + } + + //! Return a list where each image has been split along the specified axis \newinstance. + CImgList get_split(const char axis, const int nb=-1) const { + CImgList res; + cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U); + return res; + } + + //! Insert image at the end of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_back(const CImg& img) { + return insert(img); + } + + //! Insert image at the front of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_front(const CImg& img) { + return insert(img,0); + } + + //! Insert list at the end of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_back(const CImgList& list) { + return insert(list); + } + + //! Insert list at the front of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_front(const CImgList& list) { + return insert(list,0); + } + + //! Remove last image. + /** + **/ + CImgList& pop_back() { + return remove(_width - 1); + } + + //! Remove first image. + /** + **/ + CImgList& pop_front() { + return remove(0); + } + + //! Remove image pointed by iterator. + /** + \param iter Iterator pointing to the image to remove. + **/ + CImgList& erase(const iterator iter) { + return remove(iter - _data); + } + + //@} + //---------------------------------- + // + //! \name Data Input + //@{ + //---------------------------------- + + //! Display a simple interactive interface to select images or sublists. + /** + \param disp Window instance to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \param exit_on_anykey Exit function when any key is pressed. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(CImgDisplay &disp, const bool feature_type=true, + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false); + } + + //! Display a simple interactive interface to select images or sublists. + /** + \param title Title of a new window used to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \param exit_on_anykey Exit function when any key is pressed. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(const char *const title, const bool feature_type=true, + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); + } + + CImg _select(CImgDisplay &disp, const char *const title, const bool feature_type, + const char axis, const float align, const bool exit_on_anykey, + const unsigned int orig, const bool resize_disp, + const bool exit_on_rightbutton, const bool exit_on_wheel) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "select(): Empty instance.", + cimglist_instance); + + // Create image correspondence table and get list dimensions for visualization. + CImgList _indices; + unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + if (h>max_height) max_height = h; + sum_width+=w; sum_height+=h; + if (axis=='x') CImg(w,1,1,1,(unsigned int)l).move_to(_indices); + else CImg(h,1,1,1,(unsigned int)l).move_to(_indices); + } + const CImg indices0 = _indices>'x'; + + // Create display window. + if (!disp) { + if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); + else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); + if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else { + if (title) disp.set_title("%s",title); + disp.move_inside_screen(); + } + if (resize_disp) { + if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false); + else disp.resize(cimg_fitscreen(max_width,sum_height,1),false); + } + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).show_mouse(); + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + + // Enter event loop. + CImg visu0, visu; + CImg indices; + CImg positions(_width,4,1,1,-1); + int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1; + bool is_clicked = false, is_selected = false, text_down = false, update_display = true; + unsigned int key = 0, font_size = 32; + + while (!is_selected && !disp.is_closed() && !key) { + + // Create background image. + if (!visu0) { + visu0.assign(disp._width,disp._height,1,3,0); visu.assign(); + (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices); + unsigned int _ind = 0; + const CImg onexone(1,1,1,1,(T)0); + if (axis=='x') + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) + cimglist_for(*this,ind) { + unsigned int x0 = 0; + while (x0 &src = _data[ind]?_data[ind]:onexone; + CImg res; + src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2). + move_to(res); + const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true); + res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)x0; + positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height())); + positions(ind,2)+=res._width; + positions(ind,3)+=res._height - 1; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) + cimglist_for(*this,ind) { + unsigned int y0 = 0; + while (y0 &src = _data[ind]?_data[ind]:onexone; + CImg res; + src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2). + move_to(res); + const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false); + res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width())); + positions(ind,1) = positions(ind,3) = (int)y0; + positions(ind,2)+=res._width - 1; + positions(ind,3)+=res._height; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + if (axis=='x') --positions(_ind,2); else --positions(_ind,3); + update_display = true; + } + + if (!visu || oindex0!=index0 || oindex1!=index1) { + if (index0>=0 && index1>=0) { + visu.assign(visu0,false); + const int indm = std::min(index0,index1), indM = std::max(index0,index1); + for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) { + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + background_color,0.2f); + if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) || + (axis!='x' && positions(ind,3) - positions(ind,1)>=8)) + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + foreground_color,0.9f,0xAAAAAAAA); + } + if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down, + orig + indm,orig + indM,indM - indm + 1); + else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down, + orig + index0, + _data[index0]._width, + _data[index0]._height, + _data[index0]._depth, + _data[index0]._spectrum); + update_display = true; + } else visu.assign(); + } + if (!visu) { visu.assign(visu0,true); update_display = true; } + if (update_display) { visu.display(disp); update_display = false; } + disp.wait(); + + // Manage user events. + const int xm = disp.mouse_x(), ym = disp.mouse_y(); + int index = -1; + + if (xm>=0) { + index = (int)indices(axis=='x'?xm:ym); + if (disp.button()&1) { + if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; } + oindex1 = index1; index1 = index; + if (!feature_type) is_selected = true; + } else { + if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; } + else is_selected = true; + } + } else { + if (is_clicked) { + if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; } + else index1 = -1; + } else index0 = index1 = -1; + } + + if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; } + if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; } + if (disp.wheel() && exit_on_wheel) is_selected = true; + + CImg filename(32); + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp); + visu0.save(filename); + (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + } + disp.set_key(key,false).wait(); key = 0; + } break; + case cimg::keyO : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp); + save(filename); + (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + disp.set_key(key,false).wait(); key = 0; + } break; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }} + else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }} + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + CImg res(1,2,1,1,-1); + if (is_selected) { + if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1)); + else res.fill(index0); + } + if (!(disp.button()&2)) disp.set_button(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + disp.set_key(key); + return res; + } + + //! Load a list from a file. + /** + \param filename Filename to read data from. + **/ + CImgList& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load(): Specified filename is (null).", + cimglist_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg filename_local(256); + load(cimg::load_network(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + bool is_loaded = true; + try { +#ifdef cimglist_load_plugin + cimglist_load_plugin(filename); +#endif +#ifdef cimglist_load_plugin1 + cimglist_load_plugin1(filename); +#endif +#ifdef cimglist_load_plugin2 + cimglist_load_plugin2(filename); +#endif +#ifdef cimglist_load_plugin3 + cimglist_load_plugin3(filename); +#endif +#ifdef cimglist_load_plugin4 + cimglist_load_plugin4(filename); +#endif +#ifdef cimglist_load_plugin5 + cimglist_load_plugin5(filename); +#endif +#ifdef cimglist_load_plugin6 + cimglist_load_plugin6(filename); +#endif +#ifdef cimglist_load_plugin7 + cimglist_load_plugin7(filename); +#endif +#ifdef cimglist_load_plugin8 + cimglist_load_plugin8(filename); +#endif + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) load_cimg(filename); + else if (!cimg::strcasecmp(ext,"rec") || + !cimg::strcasecmp(ext,"par")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + + // If nothing loaded, try to guess file format from magic number in file. + if (!is_loaded && !is_stdin) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to open file '%s'.", + cimglist_instance, + filename); + } + + const char *const f_type = cimg::ftype(file,filename); + cimg::fclose(file); + is_loaded = true; + try { + if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + } + + // If nothing loaded, try to load file as a single image. + if (!is_loaded) { + assign(1); + try { + _data->load(filename); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to recognize format of file '%s'.", + cimglist_instance, + filename); + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load a list from a file \newinstance. + static CImgList get_load(const char *const filename) { + return CImgList().load(filename); + } + + //! Load a list from a .cimg file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_cimg(const char *const filename) { + return _load_cimg(0,filename); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename) { + return CImgList().load_cimg(filename); + } + + //! Load a list from a .cimg file. + /** + \param file File to read data from. + **/ + CImgList& load_cimg(std::FILE *const file) { + return _load_cimg(file,0); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file) { + return CImgList().load_cimg(file); + } + + CImgList& _load_cimg(std::FILE *const file, const char *const filename) { +#ifdef cimg_use_zlib +#define _cimgz_load_cimg_case(Tss) { \ + Bytef *const cbuf = new Bytef[csiz]; \ + cimg::fread(cbuf,(size_t)csiz,nfile); \ + if (is_bool) { \ + CImg raw(W*H*D*C/8); \ + uLongf destlen = (uLongf)raw.size(); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ + img.assign(W,H,D,C); \ + img._uchar2bool(raw,raw.size(),false); \ + } else { \ + CImg raw(W,H,D,C); \ + uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } \ + delete[] cbuf; \ +} +#else +#define _cimgz_load_cimg_case(Tss) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \ + cimglist_instance, \ + filename?filename:"(FILE*)"); +#endif + +#define _cimg_load_cimg_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + const bool is_bool = cimg::type::string()==cimg::type::string(); \ + for (unsigned int l = 0; l=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; csiz = 0; \ + if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:("(FILE*)")); \ + if (W*H*D*C>0) { \ + CImg &img = _data[l]; \ + if (err==5) _cimgz_load_cimg_case(Tss) \ + else { \ + img.assign(W,H,D,C); \ + T *ptrd = img._data; \ + if (is_bool) { \ + CImg raw; \ + for (ulongT to_read = img.size(); to_read; ) { \ + raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + CImg(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\ + _uchar2bool(raw,raw._width,false); \ + to_read-=raw._width; \ + } \ + } else { \ + CImg raw; \ + for (ulongT to_read = img.size(); to_read; ) { \ + raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + const Tss *ptrs = raw._data; \ + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + to_read-=raw._width; \ + } \ + } \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + + const ulongT cimg_iobuffer = (ulongT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N = 0, W, H, D, C; + cimg_uint64 csiz; + int i, err; + do { + j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; + } while (*tmp=='#' && i>=0); + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + assign(N); + _cimg_load_cimg_case("bool",bool); + _cimg_load_cimg_case("unsigned_char",unsigned char); + _cimg_load_cimg_case("uchar",unsigned char); + _cimg_load_cimg_case("char",char); + _cimg_load_cimg_case("unsigned_short",unsigned short); + _cimg_load_cimg_case("ushort",unsigned short); + _cimg_load_cimg_case("short",short); + _cimg_load_cimg_case("unsigned_int",unsigned int); + _cimg_load_cimg_case("uint",unsigned int); + _cimg_load_cimg_case("int",int); + _cimg_load_cimg_case("unsigned_long",ulongT); + _cimg_load_cimg_case("ulong",ulongT); + _cimg_load_cimg_case("long",longT); + _cimg_load_cimg_case("unsigned_int64",uint64T); + _cimg_load_cimg_case("uint64",uint64T); + _cimg_load_cimg_case("int64",int64T); + _cimg_load_cimg_case("float",float); + _cimg_load_cimg_case("double",double); + + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype._data,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a sublist list from a (non compressed) .cimg file. + /** + \param filename Filename to read data from. + \param n0 Starting index of images to read (~0U for max). + \param n1 Ending index of images to read (~0U for max). + \param x0 Starting X-coordinates of image regions to read. + \param y0 Starting Y-coordinates of image regions to read. + \param z0 Starting Z-coordinates of image regions to read. + \param c0 Starting C-coordinates of image regions to read. + \param x1 Ending X-coordinates of image regions to read (~0U for max). + \param y1 Ending Y-coordinates of image regions to read (~0U for max). + \param z1 Ending Z-coordinates of image regions to read (~0U for max). + \param c1 Ending C-coordinates of image regions to read (~0U for max). + **/ + CImgList& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sublist list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \overloading. + CImgList& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + CImgList& _load_cimg(std::FILE *const file, const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { +#define _cimg_load_cimg_case2(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l<=nn1; ++l) { \ + j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; \ + if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:"(FILE*)"); \ + if (W*H*D*C>0) { \ + if (l=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const unsigned int \ + _nx1 = nx1==~0U?W - 1:nx1, \ + _ny1 = ny1==~0U?H - 1:ny1, \ + _nz1 = nz1==~0U?D - 1:nz1, \ + _nc1 = nc1==~0U?C - 1:nc1; \ + if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \ + throw CImgArgumentException(_cimglist_instance \ + "load_cimg(): Invalid specified coordinates " \ + "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \ + "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \ + cimglist_instance, \ + n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \ + CImg raw(1 + _nx1 - nx0); \ + CImg &img = _data[l - nn0]; \ + img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \ + T *ptrd = img._data; \ + ulongT skipvb = nc0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \ + const ulongT skipzb = nz0*W*H*sizeof(Tss); \ + if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \ + const ulongT skipyb = ny0*W*sizeof(Tss); \ + if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \ + const ulongT skipxb = nx0*sizeof(Tss); \ + if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \ + const Tss *ptrs = raw._data; \ + for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + unsigned int + nn0 = std::min(n0,n1), nn1 = std::max(n0,n1), + nx0 = std::min(x0,x1), nx1 = std::max(x0,x1), + ny0 = std::min(y0,y1), ny1 = std::max(y0,y1), + nz0 = std::min(z0,z1), nz1 = std::max(z0,z1), + nc0 = std::min(c0,c1), nc1 = std::max(c0,c1); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N, W, H, D, C; + int i, err; + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + nn1 = n1==~0U?N - 1:n1; + if (nn1>=N) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " + "because file '%s' contains only %u images.", + cimglist_instance, + n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N); + assign(1 + nn1 - n0); + _cimg_load_cimg_case2("bool",bool); + _cimg_load_cimg_case2("unsigned_char",unsigned char); + _cimg_load_cimg_case2("uchar",unsigned char); + _cimg_load_cimg_case2("char",char); + _cimg_load_cimg_case2("unsigned_short",unsigned short); + _cimg_load_cimg_case2("ushort",unsigned short); + _cimg_load_cimg_case2("short",short); + _cimg_load_cimg_case2("unsigned_int",unsigned int); + _cimg_load_cimg_case2("uint",unsigned int); + _cimg_load_cimg_case2("int",int); + _cimg_load_cimg_case2("unsigned_long",ulongT); + _cimg_load_cimg_case2("ulong",ulongT); + _cimg_load_cimg_case2("long",longT); + _cimg_load_cimg_case2("unsigned_int64",uint64T); + _cimg_load_cimg_case2("uint64",uint64T); + _cimg_load_cimg_case2("int64",int64T); + _cimg_load_cimg_case2("float",float); + _cimg_load_cimg_case2("double",double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype._data,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_parrec(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_parrec(): Specified filename is (null).", + cimglist_instance); + + CImg body(1024), filenamepar(1024), filenamerec(1024); + *body = *filenamepar = *filenamerec = 0; + const char *const ext = cimg::split_filename(filename,body); + if (!std::strcmp(ext,"par")) { + std::strncpy(filenamepar,filename,filenamepar._width - 1); + cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data); + } + if (!std::strcmp(ext,"PAR")) { + std::strncpy(filenamepar,filename,filenamepar._width - 1); + cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data); + } + if (!std::strcmp(ext,"rec")) { + std::strncpy(filenamerec,filename,filenamerec._width - 1); + cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data); + } + if (!std::strcmp(ext,"REC")) { + std::strncpy(filenamerec,filename,filenamerec._width - 1); + cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data); + } + std::FILE *file = cimg::fopen(filenamepar,"r"); + + // Parse header file + CImgList st_slices; + CImgList st_global; + CImg line(256); *line = 0; + int err; + do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.')); + do { + unsigned int sn,size_x,size_y,pixsize; + float rs,ri,ss; + err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss); + if (err==7) { + CImg::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices); + unsigned int i; for (i = 0; i::vector(size_x,size_y,sn).move_to(st_global); + else { + CImg &vec = st_global[i]; + if (size_x>vec[0]) vec[0] = size_x; + if (size_y>vec[1]) vec[1] = size_y; + vec[2] = sn; + } + st_slices[st_slices._width - 1][7] = (float)i; + } + } while (err==7); + + // Read data + std::FILE *file2 = cimg::fopen(filenamerec,"rb"); + cimglist_for(st_global,l) { + const CImg& vec = st_global[l]; + CImg(vec[0],vec[1],vec[2]).move_to(*this); + } + + cimglist_for(st_slices,l) { + const CImg& vec = st_slices[l]; + const unsigned int + sn = (unsigned int)vec[0] - 1, + pixsize = (unsigned int)vec[1], + size_x = (unsigned int)vec[2], + size_y = (unsigned int)vec[3], + imn = (unsigned int)vec[7]; + const float ri = vec[4], rs = vec[5], ss = vec[6]; + switch (pixsize) { + case 8 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 16 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 32 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + default : + cimg::fclose(file); + cimg::fclose(file2); + throw CImgIOException(_cimglist_instance + "load_parrec(): Unsupported %d-bits pixel type for file '%s'.", + cimglist_instance, + pixsize,filename); + } + } + cimg::fclose(file); + cimg::fclose(file2); + if (!_width) + throw CImgIOException(_cimglist_instance + "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.", + cimglist_instance, + filename); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file \newinstance. + static CImgList get_load_parrec(const char *const filename) { + return CImgList().load_parrec(filename); + } + + //! Load a list from a YUV image sequence file. + /** + \param filename Filename to read data from. + \param size_x Width of the images. + \param size_y Height of the images. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param yuv2rgb Apply YUV to RGB transformation during reading. + **/ + CImgList& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(0,filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from a YUV image sequence file \newinstance. + static CImgList get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \overloading. + CImgList& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(file,0,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \newinstance. + static CImgList get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + CImgList& _load_yuv(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling, + const unsigned int first_frame, const unsigned int last_frame, + const unsigned int step_frame, const bool yuv2rgb) { + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + nfirst_frame = first_frame YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2); + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool stop_flag = false; + int err; + if (nfirst_frame) { + err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR); + if (err) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_yuv(): File '%s' doesn't contain frame number %u.", + cimglist_instance, + filename?filename:"(FILE*)",nfirst_frame); + } + } + unsigned int frame; + for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) { + YUV.get_shared_channel(0).fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile); + if (err!=(int)(YUV._width*YUV._height)) { + stop_flag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + UV.fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile); + if (err!=(int)(UV.size())) { + stop_flag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1); + ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2); + const unsigned int wd = YUV._width; + switch (chroma_subsampling) { + case 420 : + cimg_forY(UV,y) { + cimg_forX(UV,x) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd2[wd] = V; *(ptrd2)++ = V; + ptrd2[wd] = V; *(ptrd2)++ = V; + } + ptrd1+=wd; ptrd2+=wd; + } + break; + case 422 : + cimg_forXY(UV,x,y) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + *(ptrd1++) = U; *(ptrd1++) = U; + *(ptrd2++) = V; *(ptrd2++) = V; + } + break; + default : + YUV.draw_image(0,0,0,1,UV); + } + if (yuv2rgb) YUV.YCbCrtoRGB(); + insert(YUV); + if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + } + } + } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_yuv() : Missing data in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame) + cimg::warn(_cimglist_instance + "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", + cimglist_instance, + nlast_frame,frame - 1,filename?filename:"(FILE*)"); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a video file, using OpenCV library. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U'). + \param step_frame Step value for frame reading. + \note If step_frame==0, the current video stream is forced to be released (without any frames read). + **/ + CImgList& load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { +#ifndef cimg_use_opencv + if (first_frame || last_frame!=~0U || step_frame>1) + throw CImgArgumentException(_cimglist_instance + "load_video() : File '%s', arguments 'first_frame', 'last_frame' " + "and 'step_frame' requires features from the OpenCV library " + "('-Dcimg_use_opencv' must be defined).", + cimglist_instance,filename); + return load_custom_external(filename); +#else + static cv::VideoCapture *captures[32] = { 0 }; + static CImgList filenames(32); + static CImg positions(32,1,1,1,0); + static int last_used_index = -1; + + // Detect if a video capture already exists for the specified filename. + cimg::mutex(9); + int index = -1; + if (filename) { + if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { + index = last_used_index; + } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { + index = l; break; + } + } else index = last_used_index; + cimg::mutex(9,0); + + // Release stream if needed. + if (!step_frame || (index>=0 && positions[index]>first_frame)) { + if (index>=0) { + cimg::mutex(9); + captures[index]->release(); + delete captures[index]; + captures[index] = 0; + positions[index] = 0; + filenames[index].assign(); + if (last_used_index==index) last_used_index = -1; + index = -1; + cimg::mutex(9,0); + } else + if (filename) + cimg::warn(_cimglist_instance + "load_video() : File '%s', no opened video stream associated with filename found.", + cimglist_instance,filename); + else + cimg::warn(_cimglist_instance + "load_video() : No opened video stream found.", + cimglist_instance,filename); + if (!step_frame) return *this; + } + + // Find empty slot for capturing video stream. + if (index<0) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_video(): No already open video reader found. You must specify a " + "non-(null) filename argument for the first call.", + cimglist_instance); + else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } + if (index<0) + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', no video reader slots available. " + "You have to release some of your previously opened videos.", + cimglist_instance,filename); + cimg::mutex(9); + captures[index] = new cv::VideoCapture(filename); + positions[index] = 0; + if (!captures[index]->isOpened()) { + delete captures[index]; + captures[index] = 0; + cimg::mutex(9,0); + cimg::fclose(cimg::fopen(filename,"rb")); // Check file availability + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', unable to detect format of video file.", + cimglist_instance,filename); + } + CImg::string(filename).move_to(filenames[index]); + cimg::mutex(9,0); + } + + cimg::mutex(9); + const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count)); + cimg::mutex(9,0); + assign(); + + // Skip frames if requested. + bool go_on = true; + unsigned int &pos = positions[index]; + while (posgrab()) { cimg::mutex(9,0); go_on = false; break; } + cimg::mutex(9,0); + ++pos; + } + + // Read and convert frames. + const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame); + while (go_on && pos<=_last_frame) { + cv::Mat cvimg; + cimg::mutex(9); + if (captures[index]->read(cvimg)) { CImg::_cvmat2cimg(cvimg).move_to(*this); ++pos; } + else go_on = false; + cimg::mutex(9,0); + if (go_on) + for (unsigned int i = 1; go_on && igrab()) go_on = false; + cimg::mutex(9,0); + } + } + + if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary + cimg::mutex(9); + captures[index]->release(); + delete captures[index]; + captures[index] = 0; + filenames[index].assign(); + positions[index] = 0; + index = -1; + cimg::mutex(9,0); + } + + cimg::mutex(9); + last_used_index = index; + cimg::mutex(9,0); + + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', unable to locate frame %u.", + cimglist_instance,filename,first_frame); + return *this; +#endif + } + + //! Load an image from a video file, using OpenCV library \newinstance. + static CImgList get_load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImgList().load_video(filename,first_frame,last_frame,step_frame); + } + + //! Load an image from a video file using the external tool 'custom'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_custom_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_custom_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), filename_tmp2(256); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); + cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"", + cimg::custom_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp2)._system_strescape().data()); + cimg::system(command, cimg::custom_path()); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + assign(); + unsigned int i = 1; + for (bool stop_flag = false; !stop_flag; ++i) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i); + CImg img; + try { img.load_pnm(filename_tmp2); } + catch (CImgException&) { stop_flag = true; } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + } + cimg::exception_mode(omode); + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_custom_external(): Failed to open file '%s' with external command 'custom'.", + cimglist_instance, + filename); + return *this; + } + + //! Load an image from a video file using the external tool 'custom' \newinstance. + static CImgList get_load_custom_external(const char *const filename) { + return CImgList().load_custom_external(filename); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gif_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_gif_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + if (!_load_gif_external(filename,false)) + if (!_load_gif_external(filename,true)) + try { assign(CImg().load_other(filename)); } catch (CImgException&) { assign(); } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_gif_external(): Failed to open file '%s'.", + cimglist_instance,filename); + return *this; + } + + CImgList& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data); + else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::imagemagick_path()); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + assign(); + + // Try to read a single frame gif. + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data); + CImg img; + try { img.load_png(filename_tmp2); } + catch (CImgException&) { } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + else { // Try to read animated gif + unsigned int i = 0; + for (bool stop_flag = false; !stop_flag; ++i) { + if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i); + else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i); + try { img.load_png(filename_tmp2); } + catch (CImgException&) { stop_flag = true; } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance. + static CImgList get_load_gif_external(const char *const filename) { + return CImgList().load_gif_external(filename); + } + + //! Load a gzipped list, using external tool 'gunzip'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::gunzip_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Failed to open file '%s'.", + cimglist_instance, + filename); + + } else cimg::fclose(file); + load(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load a gzipped list, using external tool 'gunzip' \newinstance. + static CImgList get_load_gzip_external(const char *const filename) { + return CImgList().load_gzip_external(filename); + } + + //! Load images from a TIFF file. + /** + \param filename Filename to read data from. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + \param[out] voxel_size Voxel size, as stored in the filename. + \param[out] description Description, as stored in the filename. + **/ + CImgList& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + const unsigned int + nfirst_frame = first_frame::get_load_tiff(filename)); +#else +#if cimg_verbosity<3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn(_cimglist_instance + "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since " + "file '%s' contains %u image(s).", + cimglist_instance, + nfirst_frame,nlast_frame,nstep_frame,filename,nb_images); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + assign(1 + (nlast_frame - nfirst_frame)/nstep_frame); + TIFFSetDirectory(tif,0); + cimglist_for(*this,l) + _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description); + TIFFClose(tif); + } else throw CImgIOException(_cimglist_instance + "load_tiff(): Failed to open file '%s'.", + cimglist_instance, + filename); + return *this; +#endif + } + + //! Load a multi-page TIFF file \newinstance. + static CImgList get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); + } + + //@} + //---------------------------------- + // + //! \name Data Output + //@{ + //---------------------------------- + + //! Print information about the list on the standard output. + /** + \param title Label set to the information displayed. + \param display_stats Tells if image statistics must be computed and displayed. + **/ + const CImgList& print(const char *const title=0, const bool display_stats=true) const { + unsigned int msiz = 0; + cimglist_for(*this,l) msiz+=_data[l].size(); + msiz*=sizeof(T); + const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type()); + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_allocated_width, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1)); + else std::fprintf(cimg::output(),".\n"); + + char tmp[16] = { 0 }; + cimglist_for(*this,ll) { + cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll); + std::fprintf(cimg::output()," "); + _data[ll].print(tmp,display_stats); + if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); } + } + std::fflush(cimg::output()); + return *this; + } + + //! Display the current CImgList instance in an existing CImgDisplay window (by reference). + /** + \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + \note This function displays the list images of the current CImgList instance into an existing + CImgDisplay window. + Images of the list are appended in a single temporary image for visualization purposes. + The function returns immediately. + **/ + const CImgList& display(CImgDisplay &disp, const char axis='x', const float align=0) const { + disp.display(*this,axis,align); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + \param disp Display window. + \param display_info Tells if image information are displayed on the standard output. + \param axis Alignment axis for images viewing. + \param align Appending alignment. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + \note This function opens a new window with a specific title and displays the list images of the + current CImgList instance into it. + Images of the list are appended in a single temporary image for visualization purposes. + The function returns when a key is pressed or the display window is closed by the user. + **/ + const CImgList& display(CImgDisplay &disp, const bool display_info, + const char axis='x', const float align=0, + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { + bool is_exit = false; + return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); + } + + //! Display the current CImgList instance in a new display window. + /** + \param title Title of the opening display window. + \param display_info Tells if list information must be written on standard output. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImgList& display(const char *const title=0, const bool display_info=true, + const char axis='x', const float align=0, + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { + CImgDisplay disp; + bool is_exit = false; + return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); + } + + const CImgList& _display(CImgDisplay &disp, const char *const title, const CImgList *const titles, + const bool display_info, const char axis, const float align, unsigned int *const XYZ, + const bool exit_on_anykey, const unsigned int orig, const bool is_first_call, + bool &is_exit) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "display(): Empty instance.", + cimglist_instance); + if (!disp) { + if (axis=='x') { + unsigned int sum_width = 0, max_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + sum_width+=w; + if (h>max_height) max_height = h; + } + disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1); + } else { + unsigned int max_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + sum_height+=h; + } + disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1); + } + if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else if (title) disp.set_title("%s",title); + else if (titles) disp.set_title("%s",titles->__display()._data); + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(disp.title()); + disp.show().flush(); + + if (_width==1) { + const unsigned int dw = disp._width, dh = disp._height; + if (!is_first_call) + disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false); + disp.set_title("%s (%ux%ux%ux%u)", + dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum); + _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call); + if (disp.key()) is_exit = true; + disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); + } else { + bool disp_resize = !is_first_call; + while (!disp.is_closed() && !is_exit) { + const CImg s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true); + disp_resize = true; + if (s[0]<0 && !disp.wheel()) { // No selections done + if (disp.button()&2) { disp.flush(); break; } + is_exit = true; + } else if (disp.wheel()) { // Zoom in/out + const int wheel = disp.wheel(); + disp.set_wheel(); + if (!is_first_call && wheel<0) break; + if (wheel>0 && _width>=4) { + const unsigned int + delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)), + ind0 = (unsigned int)std::max(0,s[0] - (int)delta), + ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta); + if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) { + const CImgList sublist = get_shared_images(ind0,ind1); + CImgList t_sublist; + if (titles) t_sublist = titles->get_shared_images(ind0,ind1); + sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, + orig + ind0,false,is_exit); + } + } + } else if (s[0]!=0 || s[1]!=width() - 1) { + const CImgList sublist = get_shared_images(s[0],s[1]); + CImgList t_sublist; + if (titles) t_sublist = titles->get_shared_images(s[0],s[1]); + sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, + orig + s[0],false,is_exit); + } + disp.set_title("%s",dtitle.data()); + } + } + return *this; + } + + // [internal] Return string to describe display title. + CImg __display() const { + CImg res, str; + cimglist_for(*this,l) { + CImg::string((char*)_data[l]).move_to(str); + if (l!=width() - 1) { + str.resize(str._width + 1,1,1,1,0); + str[str._width - 2] = ','; + str[str._width - 1] = ' '; + } + res.append(str,'x'); + } + if (!res) return CImg(1,1,1,1,0).move_to(res); + cimg::strellipsize(res,128,false); + if (_width>1) { + const unsigned int l = (unsigned int)std::strlen(res); + if (res._width<=l + 16) res.resize(l + 16,1,1,1,0); + cimg_snprintf(res._data + l,16," (#%u)",_width); + } + return res; + } + + //! Save list into a file. + /** + \param filename Filename to write data to. + \param number When positive, represents an index added to the filename. Otherwise, no number is added. + \param digits Number of digits used for adding the number to the filename. + **/ + const CImgList& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save(): Specified filename is (null).", + cimglist_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + CImg nfilename(1024); + const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename): + filename; + +#ifdef cimglist_save_plugin + cimglist_save_plugin(fn); +#endif +#ifdef cimglist_save_plugin1 + cimglist_save_plugin1(fn); +#endif +#ifdef cimglist_save_plugin2 + cimglist_save_plugin2(fn); +#endif +#ifdef cimglist_save_plugin3 + cimglist_save_plugin3(fn); +#endif +#ifdef cimglist_save_plugin4 + cimglist_save_plugin4(fn); +#endif +#ifdef cimglist_save_plugin5 + cimglist_save_plugin5(fn); +#endif +#ifdef cimglist_save_plugin6 + cimglist_save_plugin6(fn); +#endif +#ifdef cimglist_save_plugin7 + cimglist_save_plugin7(fn); +#endif +#ifdef cimglist_save_plugin8 + cimglist_save_plugin8(fn); +#endif + if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); +#ifdef cimg_use_tiff + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); +#endif + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + else { + if (_width==1) _data[0].save(fn,-1); + else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); } + } + return *this; + } + + //! Tell if an image list can be saved as one single file. + /** + \param filename Filename, as a C-string. + \return \c true if the file format supports multiple images, \c false otherwise. + **/ + static bool is_saveable(const char *const filename) { + const char *const ext = cimg::split_filename(filename); + if (!cimg::strcasecmp(ext,"cimgz") || +#ifdef cimg_use_tiff + !cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff") || +#endif + !cimg::strcasecmp(ext,"yuv") || + !cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return true; + return false; + } + + //! Save image sequence as a GIF animated file. + /** + \param filename Filename to write data to. + \param fps Number of desired frames per second. + \param nb_loops Number of loops (\c 0 for infinite looping). + **/ + const CImgList& save_gif_external(const char *const filename, const float fps=25, + const unsigned int nb_loops=0) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; + +#ifdef cimg_use_png +#define _cimg_save_gif_extension "png" +#else +#define _cimg_save_gif_extension "ppm" +#endif + + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1); + CImg::string(filename_tmp2).move_to(filenames); + if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); + else _data[l].save(filename_tmp2); + } + cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops); + CImg::string(command).move_to(filenames,0); + cimg_snprintf(command,command._width,"\"%s\"", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); + CImg _command = filenames>'x'; + cimg_for(_command,p,char) if (!*p) *p = ' '; + _command.back() = 0; + + cimg::system(_command, cimg::imagemagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]); + return *this; + } + + //! Save list as a YUV image sequence file. + /** + \param filename Filename to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(const char *const filename=0, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(0,filename,chroma_subsampling,is_rgb); + } + + //! Save image sequence into a YUV file. + /** + \param file File to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(file,0,chroma_subsampling,is_rgb); + } + + const CImgList& _save_yuv(std::FILE *const file, const char *const filename, + const unsigned int chroma_subsampling, + const bool is_rgb) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + w0 = (*this)[0]._width, h0 = (*this)[0]._height, + width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + const CImg &frame = (*this)[l]; + cimg_forZ(frame,z) { + CImg YUV; + if (sizeof(T)==1 && !is_rgb && + frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3) + YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true); + else { + YUV = frame.get_slice(z); + if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0); + if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0); + if (is_rgb) YUV.RGBtoYCbCr(); + } + if (chroma_subsampling==444) + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile); + else { + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile); + CImg UV = YUV.get_channels(1,2); + UV.resize(UV._width/cfx,UV._height/cfy,1,2,2); + cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param filename Filename to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(const char *const filename, const bool is_compressed=false) const { + return _save_cimg(0,filename,is_compressed); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, " + "saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); +#endif + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const bool is_bool = ptype==cimg::type::string(); + if (!is_bool && std::strstr(ptype,"unsigned")==ptype) + std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); + else + std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); + + cimglist_for(*this,l) { + const CImg& img = _data[l]; + std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + Bytef *cbuf = 0; + uLongf csiz = 0; + + if (is_bool) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = ref._bool2uchar(siz,false); + csiz = siz + siz/100 + 16; + cbuf = new Bytef[csiz]; + failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz); + if (!failed_to_compress) { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + } + delete[] buf; + } else { // Non-boolean data + const ulongT siz = sizeof(T)*ref.size(); + csiz = siz + siz/100 + 16; + cbuf = new Bytef[csiz]; + failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz); + if (!failed_to_compress) { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + } + } + if (failed_to_compress) + cimg::warn(_cimglist_instance + "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); + delete[] cbuf; +#endif + } + if (failed_to_compress) { // Write non-compressed + std::fputc('\n',nfile); + if (is_bool) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = ref._bool2uchar(siz,false); + cimg::fwrite(buf,siz,nfile); + delete[] buf; + } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data + } + } else std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param file File to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(std::FILE *file, const bool is_compressed=false) const { + return _save_cimg(file,0,is_compressed); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { +#define _cimg_save_cimg_case(Ts,Tss) \ + if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l0) { \ + if (l=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const CImg& img = (*this)[l - n0]; \ + const T *ptrs = img._data; \ + const unsigned int \ + x1 = x0 + img._width - 1, \ + y1 = y0 + img._height - 1, \ + z1 = z0 + img._depth - 1, \ + c1 = c0 + img._spectrum - 1, \ + nx1 = x1>=W?W - 1:x1, \ + ny1 = y1>=H?H - 1:y1, \ + nz1 = z1>=D?D - 1:z1, \ + nc1 = c1>=C?C - 1:c1; \ + CImg raw(1 + nx1 - x0); \ + const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v = 1 + nc1 - c0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z = 1 + nz1 - z0; z; --z) { \ + const unsigned int skipyb = y0*W*sizeof(Tss); \ + if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + ny1 - y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ + raw.assign(ptrs, raw._width); \ + ptrs+=img._width; \ + if (endian) cimg::invert_endianness(raw._data,raw._width); \ + cimg::fwrite(raw._data,raw._width,nfile); \ + const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_cimg(): Empty instance, for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); + bool saved = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N, W, H, D, C; + int i, err; + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "save_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + const unsigned int lmax = std::min(N,n0 + _width); + _cimg_save_cimg_case("bool",bool); + _cimg_save_cimg_case("unsigned_char",unsigned char); + _cimg_save_cimg_case("uchar",unsigned char); + _cimg_save_cimg_case("char",char); + _cimg_save_cimg_case("unsigned_short",unsigned short); + _cimg_save_cimg_case("ushort",unsigned short); + _cimg_save_cimg_case("short",short); + _cimg_save_cimg_case("unsigned_int",unsigned int); + _cimg_save_cimg_case("uint",unsigned int); + _cimg_save_cimg_case("int",int); + _cimg_save_cimg_case("unsigned_int64",uint64T); + _cimg_save_cimg_case("uint64",uint64T); + _cimg_save_cimg_case("int64",int64T); + _cimg_save_cimg_case("float",float); + _cimg_save_cimg_case("double",double); + if (!saved) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "save_cimg(): Unsupported data type '%s' for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)",str_pixeltype._data); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param filename Filename to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(0,filename,n0,x0,y0,z0,c0); + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param file File to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(file,0,n0,x0,y0,z0,c0); + } + + static void _save_empty_cimg(std::FILE *const file, const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T); + std::fprintf(nfile,"%u %s\n",nb,pixel_type()); + for (unsigned int i=nb; i; --i) { + std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc); + for (ulongT off = siz; off; --off) std::fputc(0,nfile); + } + if (!file) cimg::fclose(nfile); + } + + //! Save empty (non-compressed) .cimg file with specified dimensions. + /** + \param filename Filename to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc); + } + + //! Save empty .cimg file with specified dimensions. + /** + \param file File to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(file,0,nb,dx,dy,dz,dc); + } + + //! Save list as a TIFF file. + /** + \param filename Filename to write data to. + \param compression_type Compression mode used to write data. + \param voxel_size Voxel size, to be stored in the filename. + \param description Description, to be stored in the filename. + \param use_bigtiff Allow to save big tiff files (>4Gb). + **/ + const CImgList& save_tiff(const char *const filename, const unsigned int compression_type=0, + const float *const voxel_size=0, const char *const description=0, + const bool use_bigtiff=true) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_tiff(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_tiff + if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff); + else cimglist_for(*this,l) { + CImg nfilename(1024); + cimg::number_filename(filename,l,6,nfilename); + _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff); + } +#else + ulongT siz = 0; + cimglist_for(*this,l) siz+=_data[l].size(); + const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images + TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); + if (tif) { + for (unsigned int dir = 0, l = 0; l<_width; ++l) { + const CImg& img = (*this)[l]; + cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); + } + TIFFClose(tif); + } else + throw CImgIOException(_cimglist_instance + "save_tiff(): Failed to open stream for file '%s'.", + cimglist_instance, + filename); +#endif + return *this; + } + + //! Save list as a gzipped file, using external tool 'gzip'. + /** + \param filename Filename to write data to. + **/ + const CImgList& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Specified filename is (null).", + cimglist_instance); + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + + if (is_saveable(body)) { + save(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::gzip_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimglist_instance, + filename); + else cimg::fclose(file); + std::remove(filename_tmp); + } else { + CImg nfilename(1024); + cimglist_for(*this,l) { + cimg::number_filename(body,l,6,nfilename); + if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); + _data[l].save_gzip_external(nfilename); + } + } + return *this; + } + + //! Save image sequence (using the OpenCV library when available). + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). + \param keep_open Tells if the video writer associated to the specified filename + must be kept open or not (to allow frames to be added in the same file afterwards). + **/ + const CImgList& save_video(const char *const filename, const unsigned int fps=25, + const char *codec=0, const bool keep_open=false) const { +#ifndef cimg_use_opencv + cimg::unused(codec,keep_open); + return save_custom_external(filename,fps); +#else + try { + static cv::VideoWriter *writers[32] = { 0 }; + static CImgList filenames(32); + static CImg sizes(32,2,1,1,0); + static int last_used_index = -1; + + // Detect if a video writer already exists for the specified filename. + cimg::mutex(9); + int index = -1; + if (filename) { + if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { + index = last_used_index; + } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { + index = l; break; + } + } else index = last_used_index; + cimg::mutex(9,0); + + // Find empty slot for capturing video stream. + if (index<0) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_video(): No already open video writer found. You must specify a " + "non-(null) filename argument for the first call.", + cimglist_instance); + else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } + if (index<0) + throw CImgIOException(_cimglist_instance + "save_video(): File '%s', no video writer slots available. " + "You have to release some of your previously opened videos.", + cimglist_instance,filename); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_video(): Instance list is empty.", + cimglist_instance); + const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0; + if (!W || !H) + throw CImgInstanceException(_cimglist_instance + "save_video(): Frame [0] is an empty image.", + cimglist_instance); + const char + *const _codec = codec && *codec?codec:"h264", + codec0 = cimg::uppercase(_codec[0]), + codec1 = _codec[0]?cimg::uppercase(_codec[1]):0, + codec2 = _codec[1]?cimg::uppercase(_codec[2]):0, + codec3 = _codec[2]?cimg::uppercase(_codec[3]):0; + cimg::mutex(9); + writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H)); + if (!writers[index]->isOpened()) { + delete writers[index]; + writers[index] = 0; + cimg::mutex(9,0); + throw CImgIOException(_cimglist_instance + "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.", + cimglist_instance,filename, + codec0,codec1,codec2,codec3); + } + CImg::string(filename).move_to(filenames[index]); + sizes(index,0) = W; + sizes(index,1) = H; + cimg::mutex(9,0); + } + + if (!is_empty()) { + const unsigned int W = sizes(index,0), H = sizes(index,1); + cimg::mutex(9); + cimglist_for(*this,l) { + CImg &src = _data[l]; + if (src.is_empty()) + cimg::warn(_cimglist_instance + "save_video(): Skip empty frame %d for file '%s'.", + cimglist_instance,l,filename); + if (src._depth>1 || src._spectrum>3) + cimg::warn(_cimglist_instance + "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). " + "Some image data may be ignored when writing frame into video file '%s'.", + cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename); + if (src._width==W && src._height==H && src._spectrum==3) + writers[index]->write(CImg(src)._cimg2cvmat()); + else { + CImg _src(src,false); + _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H); + _src.resize(W,H,1,3,_src._spectrum==1); + writers[index]->write(_src._cimg2cvmat()); + } + } + cimg::mutex(9,0); + } + + cimg::mutex(9); + if (!keep_open) { + delete writers[index]; + writers[index] = 0; + filenames[index].assign(); + sizes(index,0) = sizes(index,1) = 0; + last_used_index = -1; + } else last_used_index = index; + cimg::mutex(9,0); + } catch (CImgIOException &e) { + if (!keep_open) return save_custom_external(filename,fps); + throw e; + } + return *this; +#endif + } + + //! Save image sequence, using the external tool 'custom'. + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression. + \param bitrate Output bitrate + **/ + const CImgList& save_custom_external(const char *const filename, const unsigned int fps=25, + const char *const codec=0, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_custom_external(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + const char + *const ext = cimg::split_filename(filename), + *const _codec = codec?codec: + !cimg::strcasecmp(ext,"flv")?"flv": + !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video"; + + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; + cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0])) + throw CImgInstanceException(_cimglist_instance + "save_custom_external(): Invalid instance dimensions for file '%s'.", + cimglist_instance, + filename); + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); + CImg::string(filename_tmp2).move_to(filenames); + CImg tmp = _data[l].get_shared(); + if (tmp._width%2 || tmp._height%2) // Force output to have an even number of columns and rows + tmp.assign(tmp.get_resize(tmp._width + (tmp._width%2),tmp._height + (tmp._height%2),1,-100,0),false); + if (tmp._depth>1 || tmp._spectrum!=3) // Force output to be one slice, in color + tmp.assign(tmp.get_resize(-100,-100,1,3),false); + tmp.save_pnm(filename_tmp2); + } + cimg_snprintf(command,command._width, + "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"", + cimg::custom_path(), + CImg::string(filename_tmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::custom_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_custom_external(): Failed to save file '%s' with external command 'custom'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for(*this,l) std::remove(filenames[l]); + return *this; + } + + //! Serialize a CImgList instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "get_serialize(): Unable to compress data unless zlib is enabled, " + "storing them uncompressed.", + cimglist_instance); +#endif + CImgList stream; + CImg tmpstr(128); + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + if (std::strstr(ptype,"unsigned")==ptype) + cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); + else + cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); + CImg::string(tmpstr,false).move_to(stream); + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + CImg::string(tmpstr,false).move_to(stream); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + const ulongT siz = sizeof(T)*ref.size(); + uLongf csiz = (ulongT)compressBound(siz); + Bytef *const cbuf = new Bytef[csiz]; + if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) + cimg::warn(_cimglist_instance + "get_serialize(): Failed to save compressed data, saving them uncompressed.", + cimglist_instance); + else { + cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz); + CImg::string(tmpstr,false).move_to(stream); + CImg(cbuf,csiz).move_to(stream); + delete[] cbuf; + failed_to_compress = false; + } +#endif + } + if (failed_to_compress) { // Write in a non-compressed way + CImg::string("\n",false).move_to(stream); + stream.insert(1); + stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true); + } + } else CImg::string("\n",false).move_to(stream); + } + cimglist_apply(stream,unroll)('y'); + return stream>'y'; + } + + //! Unserialize a CImg serialized buffer into a CImgList list. + template + static CImgList get_unserialize(const CImg& buffer) { +#ifdef cimg_use_zlib +#define _cimgz_unserialize_case(Tss) { \ + Bytef *cbuf = 0; \ + if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type::string()) { \ + cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ + for (ulongT k = 0; k::get_unserialize(): Unable to unserialize compressed data " \ + "unless zlib is enabled.", \ + pixel_type()); +#endif + +#define _cimg_unserialize_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \ + "image #%u in serialized buffer.", \ + pixel_type(),W,H,D,C,l); \ + if (W*H*D*C>0) { \ + CImg raw; \ + CImg &img = res._data[l]; \ + if (err==5) _cimgz_unserialize_case(Tss) \ + else { \ + raw.assign(W,H,D,C); \ + CImg _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \ + if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \ + else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \ + } \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } \ + } \ + loaded = true; \ + } + + if (buffer.is_empty()) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).", + pixel_type()); + CImgList res; + const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); + bool loaded = false, endian = cimg::endianness(), is_bytef = false; + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N = 0, W, H, D, C; + uint64T csiz; + int i, err; + cimg::unused(is_bytef); + do { + j = 0; while ((i=(int)*stream)!='\n' && stream::get_unserialize(): CImg header not found in serialized buffer.", + pixel_type()); + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + res.assign(N); + _cimg_unserialize_case("bool",bool); + _cimg_unserialize_case("unsigned_char",unsigned char); + _cimg_unserialize_case("uchar",unsigned char); + _cimg_unserialize_case("char",char); + _cimg_unserialize_case("unsigned_short",unsigned short); + _cimg_unserialize_case("ushort",unsigned short); + _cimg_unserialize_case("short",short); + _cimg_unserialize_case("unsigned_int",unsigned int); + _cimg_unserialize_case("uint",unsigned int); + _cimg_unserialize_case("int",int); + _cimg_unserialize_case("unsigned_int64",uint64T); + _cimg_unserialize_case("uint64",uint64T); + _cimg_unserialize_case("int64",int64T); + _cimg_unserialize_case("float",float); + _cimg_unserialize_case("double",double); + if (!loaded) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined " + "in serialized buffer.", + pixel_type(),str_pixeltype._data); + return res; + } + + //@} + //---------------------------------- + // + //! \name Others + //@{ + //---------------------------------- + + //! Return a CImg pre-defined font with requested height. + /** + \param font_height Height of the desired font (exact match for 13,23,53,103). + \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width. + **/ + static const CImgList& font(const unsigned int requested_height, const bool is_variable_width=true) { + if (!requested_height) return CImgList::const_empty(); + cimg::mutex(11); + static const unsigned char font_resizemap[] = { + 0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30, + 32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52, + 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165, + 166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179, + 180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192, + 193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205, + 206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218, + 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231, + 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, + 244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 }; + static const char *const *font_data[] = { + cimg::data_font_small, + cimg::data_font_normal, + cimg::data_font_large, + cimg::data_font_huge }; + static const unsigned int + font_width[] = { 10,26,52,104 }, + font_height[] = { 13,32,64,128 }, + font_M[] = { 86,91,91,47 }, + font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*), + sizeof(cimg::data_font_normal)/sizeof(char*), + sizeof(cimg::data_font_large)/sizeof(char*), + sizeof(cimg::data_font_huge)/sizeof(char*) }; + static const unsigned char font_is_binary[] = { 1,0,0,1 }; + static CImg font_base[4]; + + unsigned int ind = + requested_height<=font_height[0]?0U: + requested_height<=font_height[1]?1U: + requested_height<=font_height[2]?2U:3U; + + // Decompress nearest base font data if needed. + CImg &basef = font_base[ind]; + if (!basef) { + basef.assign(256*font_width[ind],font_height[ind]); + + unsigned char *ptrd = basef; + const unsigned char *const ptrde = basef.end(); + + // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb). + CImg dataf; + for (unsigned int k = 0; k::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x'); + + // Uncompress font data (decode RLE). + const unsigned int M = font_M[ind]; + if (font_is_binary[ind]) + for (const char *ptrs = dataf; *ptrs; ++ptrs) { + const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n; + if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } + else { std::memset(ptrd,v,ptrde - ptrd); break; } + } + else + for (const char *ptrs = dataf; *ptrs; ++ptrs) { + int n = (int)*ptrs - M - 32, v = 0; + if (n>=0) { v = 85*n; n = 1; } + else { + n = -n; + v = (int)*(++ptrs) - M - 32; + if (v<0) { v = 0; --ptrs; } else v*=85; + } + if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } + else { std::memset(ptrd,v,ptrde - ptrd); break; } + } + } + + // Find optimal font cache location to return. + static CImgList fonts[16]; + static bool is_variable_widths[16] = { 0 }; + ind = ~0U; + for (int i = 0; i<16; ++i) + if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) { + ind = (unsigned int)i; break; // Found empty slot or cached font + } + if (ind==~0U) { // No empty slots nor existing font in cache + fonts->assign(); + std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList)); + std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool)); + std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList)); // Free a slot in cache for new font + } + CImgList &font = fonts[ind]; + + // Render requested font. + if (!font) { + is_variable_widths[ind] = is_variable_width; + basef.get_split('x',256).move_to(font); + +// cimg::tic(); + + if (requested_height!=font[0]._height) + cimglist_for(font,l) { + font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5); + cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr]; + } + +// cimg::toc(); +// std::exit(0); + + if (is_variable_width) { // Crop font + cimglist_for(font,l) { + CImg& letter = font[l]; + int xmin = letter.width(), xmax = 0; + cimg_forX(letter,x) { // Find xmin + cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; } + if (xmin!=letter.width()) break; + } + cimg_rofX(letter,x) { // Find xmax + cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; } + if (xmax) break; + } + if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1); + } + font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0); + if (' ' + 256& FFT(const char axis, const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + CImg::FFT(_data[0],_data[1],axis,invert); + return *this; + } + + //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance. + CImgList get_FFT(const char axis, const bool invert=false) const { + return CImgList(*this,false).FFT(axis,invert); + } + + //! Compute n-D Fast Fourier Transform. + /** + \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. + **/ + CImgList& FFT(const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + + CImg::FFT(_data[0],_data[1],invert); + return *this; + } + + //! Compute n-D Fast Fourier Transform \newinstance. + CImgList get_FFT(const bool invert=false) const { + return CImgList(*this,false).FFT(invert); + } + + //! Reverse primitives orientations of a 3D object. + /** + **/ + CImgList& reverse_object3d() { + cimglist_for(*this,l) { + CImg& p = _data[l]; + switch (p.size()) { + case 2 : case 3: cimg::swap(p[0],p[1]); break; + case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break; + case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break; + case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break; + case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break; + } + } + return *this; + } + + //! Reverse primitives orientations of a 3D object \newinstance. + CImgList get_reverse_object3d() const { + return (+*this).reverse_object3d(); + } + + //@} + }; // struct CImgList { ... + + // Completion of previously declared functions + //-------------------------------------------- + namespace cimg { + + // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. + // (throw a CImgIOException when macro 'cimg_use_r' is defined). + inline FILE* _stdin(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stdin; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + inline FILE* _stdout(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stdout; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + inline FILE* _stderr(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stderr; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + // Open a file (similar to std:: fopen(), but with wide character support on Windows). + inline std::FILE *std_fopen(const char *const path, const char *const mode) { + std::FILE *const res = std::fopen(path,mode); + if (res) return res; +#if cimg_OS==2 + // Try alternative method, with wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (err) { + CImg wpath((unsigned int)err); + err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err); + if (err) { // Convert 'mode' to a wide-character string + err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0); + if (err) { + CImg wmode((unsigned int)err); + if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err)) + return _wfopen(wpath,wmode); + } + } + } +#endif + return 0; + } + + //! Search path of an executable (Windows only). +#if cimg_OS==2 + inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) { + char *ptr = 0; + DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr); + return err!=0; + } +#endif + + //! Get the file or directory attributes with support for UTF-8 paths (Windows only). +#if cimg_OS==2 + inline DWORD win_getfileattributes(const char *const path) { + DWORD res = GetFileAttributesA(path); + if (res==INVALID_FILE_ATTRIBUTES) { + // Try alternative method, with wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (err) { + CImg wpath((unsigned int)err); + if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath); + } + } + return res; + } +#endif + + //! Get/set path to the Program Files/ directory (Windows only). + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the program files. + **/ +#if cimg_OS==2 + inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(MAX_PATH); + *s_path = 0; + // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). +#if !defined(__INTEL_COMPILER) + if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) { + const char *const pfPath = std::getenv("PROGRAMFILES"); + if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1); + else std::strcpy(s_path,"C:\\PROGRA~1"); + } +#else + std::strcpy(s_path,"C:\\PROGRA~1"); +#endif + } + cimg::mutex(7,0); + return s_path; + } +#endif + + //! Get/set path to the \c curl binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c curl binary. + **/ + inline const char *curl_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\curl.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"curl.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./curl"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"curl"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c dcraw binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c dcraw binary. + **/ + inline const char *dcraw_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\dcraw.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"dcraw.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./dcraw"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"dcraw"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the custom's \c custom binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c custom binary. + **/ + inline const char *custom_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("custom.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\custom.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"custom.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./custom"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"custom"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the GraphicsMagick's \c gm binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gm binary. + **/ + inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\gm.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gm.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gm"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gm"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c gunzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gunzip binary. + **/ + inline const char *gunzip_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\gunzip.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gunzip.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gunzip"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gunzip"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c gzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gzip binary. + **/ + inline const char *gzip_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\gzip.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gzip.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gzip"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gzip"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the ImageMagick's \c convert binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c convert binary. + **/ + inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + for (int l = 0; l<2 && !path_found; ++l) { + const char *const s_exe = l?"convert":"magick"; + cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe); + } +#else + std::strcpy(s_path,"./magick"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + if (!path_found) { + std::strcpy(s_path,"./convert"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"convert"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the Medcon's \c medcon binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c medcon binary. + **/ + inline const char* medcon_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\medcon.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"medcon.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./medcon"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"medcon"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to store temporary files. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path where temporary files can be saved. + **/ + inline const char* temporary_path(const char *const user_path, const bool reinit_path) { +#define _cimg_test_temporary_path(p) \ + if (!path_found) { \ + cimg_snprintf(s_path,s_path._width,"%s",p); \ + cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ + if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ + } + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + CImg tmp(1024), filename_tmp(256); + std::FILE *file = 0; + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand()); + char *tmpPath = std::getenv("TMP"); + if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); } + if (tmpPath) _cimg_test_temporary_path(tmpPath); +#if cimg_OS==2 + _cimg_test_temporary_path("C:\\WINNT\\Temp"); + _cimg_test_temporary_path("C:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("C:\\Temp"); + _cimg_test_temporary_path("C:"); + _cimg_test_temporary_path("D:\\WINNT\\Temp"); + _cimg_test_temporary_path("D:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("D:\\Temp"); + _cimg_test_temporary_path("D:"); +#else + _cimg_test_temporary_path("/tmp"); + _cimg_test_temporary_path("/var/tmp"); +#endif + if (!path_found) { + *s_path = 0; + std::strncpy(tmp,filename_tmp,tmp._width - 1); + if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } + } + if (!path_found) { + cimg::mutex(7,0); + throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n"); + } + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c wget binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c wget binary. + **/ + inline const char *wget_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\wget.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"wget.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./wget"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"wget"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + + // [internal] Sorting function, used by cimg::files(). + inline int _sort_files(const void* a, const void* b) { + const CImg &sa = *(CImg*)a, &sb = *(CImg*)b; + return std::strcmp(sa._data,sb._data); + } + + //! Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, + const unsigned int digits, char *const str) { + if (!filename) { if (str) *str = 0; return 0; } + const unsigned int siz = (unsigned int)std::strlen(filename); + CImg format(16), body(siz + 32); + const char *const ext = cimg::split_filename(filename,body); + if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits); + else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits); + cimg_snprintf(str,1024,format._data,body._data,number,ext); + return str; + } + + //! Return list of files/directories in specified directory. + /** + \param path Path to the directory. Set to 0 for current directory. + \param is_pattern Tell if specified path has a matching pattern in it. + \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }. + \param include_path Tell if \c path must be included in resulting filenames. + \return A list of filenames. + **/ + inline CImgList files(const char *const path, const bool is_pattern=false, + const unsigned int mode=2, const bool include_path=false) { + if (!path || !*path) return files("*",true,mode,include_path); + CImgList res; + + // If path is a valid folder name, ignore argument 'is_pattern'. + const bool _is_pattern = is_pattern && !cimg::is_directory(path); + bool is_root = false, is_current = false; + cimg::unused(is_root,is_current); + + // Clean format of input path. + CImg pattern, _path = CImg::string(path); +#if cimg_OS==2 + for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/'; +#endif + char *pd = _path; + for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; } + *pd = 0; + unsigned int lp = (unsigned int)std::strlen(_path); + if (!_is_pattern && lp && _path[lp - 1]=='/') { + _path[lp - 1] = 0; --lp; +#if cimg_OS!=2 + is_root = !*_path; +#endif + } + + // Separate folder path and matching pattern. + if (_is_pattern) { + const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data()); + CImg::string(_path).move_to(pattern); + if (bpos) { + _path[bpos - 1] = 0; // End 'path' at last slash +#if cimg_OS!=2 + is_root = !*_path; +#endif + } else { // No path to folder specified, assuming current folder + is_current = true; *_path = 0; + } + lp = (unsigned int)std::strlen(_path); + } + + // Windows version. +#if cimg_OS==2 + if (!_is_pattern) { + pattern.assign(lp + 3); + std::memcpy(pattern,_path,lp); + pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0; + } + WIN32_FIND_DATAA file_data; + const HANDLE dir = FindFirstFileA(pattern.data(),&file_data); + if (dir==INVALID_HANDLE_VALUE) return CImgList::const_empty(); + do { + const char *const filename = file_data.cFileName; + if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { + const unsigned int lf = (unsigned int)std::strlen(filename); + const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0; + if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) { + if (include_path) { + CImg full_filename((lp?lp+1:0) + lf + 1); + if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; } + std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1); + full_filename.move_to(res); + } else CImg(filename,lf + 1).move_to(res); + } + } + } while (FindNextFileA(dir,&file_data)); + FindClose(dir); + + // Unix version (posix). +#elif cimg_OS == 1 + DIR *const dir = opendir(is_root?"/":is_current?".":_path.data()); + if (!dir) return CImgList::const_empty(); + struct dirent *ent; + while ((ent=readdir(dir))!=0) { + const char *const filename = ent->d_name; + if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { + const unsigned int lf = (unsigned int)std::strlen(filename); + CImg full_filename(lp + lf + 2); + + if (!is_current) { + full_filename.assign(lp + lf + 2); + if (lp) std::memcpy(full_filename,_path,lp); + full_filename[lp] = '/'; + std::memcpy(full_filename._data + lp + 1,filename,lf + 1); + } else full_filename.assign(filename,lf + 1); + + struct stat st; + if (stat(full_filename,&st)==-1) continue; + const bool is_directory = (st.st_mode & S_IFDIR)!=0; + if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) { + if (include_path) { + if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) + full_filename.move_to(res); + } else { + if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) + CImg(filename,lf + 1).move_to(res); + } + } + } + } + closedir(dir); +#endif + + // Sort resulting list by lexicographic order. + if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg),_sort_files); + + return res; + } + + //! Try to guess format from an image file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + \return C-string containing the guessed file format, or \c 0 if nothing has been guessed. + **/ + inline const char *ftype(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::ftype(): Specified filename is (null)."); + static const char + *const _pnm = "pnm", + *const _pfm = "pfm", + *const _bmp = "bmp", + *const _gif = "gif", + *const _jpg = "jpg", + *const _off = "off", + *const _pan = "pan", + *const _png = "png", + *const _tif = "tif", + *const _inr = "inr", + *const _dcm = "dcm"; + const char *f_type = 0; + CImg header; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + header._load_raw(file,filename,512,1,1,1,false,false,0); + const unsigned char *const uheader = (unsigned char*)header._data; + if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF + else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE + f_type = _inr; + else if (!std::strncmp(header,"PANDORE",7)) // PANDORE + f_type = _pan; + else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM + f_type = _dcm; + else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG + f_type = _jpg; + else if (header[0]=='B' && header[1]=='M') // BMP + f_type = _bmp; + else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && + (header[4]=='7' || header[4]=='9')) // GIF + f_type = _gif; + else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && + uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG + f_type = _png; + else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) // TIFF + f_type = _tif; + else { // PNM or PFM + CImgList _header = header.get_split(CImg::vector('\n'),0,false); + cimglist_for(_header,l) { + if (_header(l,0)=='#') continue; + if (_header[l]._width==2 && _header(l,0)=='P') { + const char c = _header(l,1); + if (c=='f' || c=='F') { f_type = _pfm; break; } + if (c>='1' && c<='9') { f_type = _pnm; break; } + } + f_type = 0; break; + } + } + } catch (CImgIOException&) { } + cimg::exception_mode(omode); + return f_type; + } + + //! Load file from network as a local temporary file. + /** + \param url URL of the filename, as a C-string. + \param[out] filename_local C-string containing the path to a local copy of \c filename. + \param timeout Maximum time (in seconds) authorized for downloading the file from the URL. + \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure. + \param referer Referer used, as a C-string. + \return Value of \c filename_local. + \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download. + **/ + inline char *load_network(const char *const url, char *const filename_local, + const unsigned int timeout, const bool try_fallback, + const char *const referer) { + if (!url) + throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); + if (!filename_local) + throw CImgArgumentException("cimg::load_network(): Specified destination string is (null)."); + if (!network_mode()) + throw CImgIOException("cimg::load_network(): Loading files from network is disabled."); + + const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext; + CImg ext = CImg::string(_ext); + std::FILE *file = 0; + *filename_local = 0; + if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0; + else cimg::strwindows_reserved(ext); + do { + cimg_snprintf(filename_local,256,"%s%c%s%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data); + if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file); + } while (file); + +#ifdef cimg_use_curl + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + CURL *curl = 0; + CURLcode res; + curl = curl_easy_init(); + if (curl) { + file = cimg::fopen(filename_local,"wb"); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,file); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L); + curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); + if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout); + if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L); + if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + cimg::fseek(file,0,SEEK_END); // Check if file size is 0 + const cimg_ulong siz = cimg::ftell(file); + cimg::fclose(file); + if (siz>0 && res==CURLE_OK) { + cimg::exception_mode(omode); + return filename_local; + } else std::remove(filename_local); + } + } catch (...) { } + cimg::exception_mode(omode); + if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url); +#endif + + CImg command((unsigned int)std::strlen(url) + 64); + cimg::unused(try_fallback); + + // Try with 'curl' first. + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,timeout,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),timeout,filename_local, + CImg::string(url)._system_strescape().data()); + } else { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),filename_local, + CImg::string(url)._system_strescape().data()); + } + cimg::system(command, cimg::curl_path()); + + if (!(file=cimg::std_fopen(filename_local,"rb"))) { + + // Try with 'wget' otherwise. + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,timeout,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),timeout,filename_local, + CImg::string(url)._system_strescape().data()); + } else { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),filename_local, + CImg::string(url)._system_strescape().data()); + } + cimg::system(command, cimg::wget_path()); + + if (!(file=cimg::std_fopen(filename_local,"rb"))) + throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands " + "'wget' or 'curl'.",url); + cimg::fclose(file); + + // Try gunzip it. + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(filename_local,command); + cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"", + gunzip_path(),filename_local); + cimg::system(command, gunzip_path()); + file = cimg::std_fopen(filename_local,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(command,filename_local); + file = cimg::std_fopen(filename_local,"rb"); + } + } + cimg::fseek(file,0,SEEK_END); // Check if file size is 0 + if (std::ftell(file)<=0) + throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands " + "'wget' or 'curl'.",url); + cimg::fclose(file); + return filename_local; + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline cimg_uint64 tictoc(const bool is_tic) { + cimg::mutex(2); + static CImg times(64); + static unsigned int pos = 0; + const cimg_uint64 t1 = cimg::time(); + if (is_tic) { + // Tic + times[pos++] = t1; + if (pos>=times._width) + throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'."); + cimg::mutex(2,0); + return t1; + } + + // Toc + if (!pos) + throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made."); + const cimg_uint64 + t0 = times[--pos], + dt = t1>=t0?(t1 - t0):cimg::type::max(); + const unsigned int + edays = (unsigned int)(dt/86400000.), + ehours = (unsigned int)((dt - edays*86400000.)/3600000.), + emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.), + esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.), + ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.); + if (!edays && !ehours && !emin && !esec) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n", + cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal); + else { + if (!edays && !ehours && !emin) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal); + else { + if (!edays && !ehours) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal); + else{ + if (!edays) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal); + else{ + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal); + } + } + } + } + cimg::mutex(2,0); + return dt; + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const cimg_ulong size) { + static CImg res(256); + cimg::mutex(5); + if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":""); + else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); } + else if (size<1024*1024*1024LU) { + const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize); + } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); } + cimg::mutex(5,0); + return res; + } + + //! Display a simple dialog box, and wait for the user's response. + /** + \param title Title of the dialog window. + \param msg Main message displayed inside the dialog window. + \param button1_label Label of the 1st button. + \param button2_label Label of the 2nd button (\c 0 to hide button). + \param button3_label Label of the 3rd button (\c 0 to hide button). + \param button4_label Label of the 4th button (\c 0 to hide button). + \param button5_label Label of the 5th button (\c 0 to hide button). + \param button6_label Label of the 6th button (\c 0 to hide button). + \param logo Image logo displayed at the left of the main message. + \param is_centered Tells if the dialog window must be centered on the screen. + \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user. + \note + - Up to 6 buttons can be defined in the dialog window. + - The function returns when a user clicked one of the button or closed the dialog window. + - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box. + At least one button must be specified. + **/ + template + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const CImg& logo, const bool is_centered=false) { +#if cimg_display==0 + cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + logo._data,is_centered); + throw CImgIOException("cimg::dialog(): No display available."); +#else + static const unsigned char + black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; + + // Create buttons and canvas graphics + CImgList buttons, cbuttons, sbuttons; + if (button1_label) { + CImg().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons); + if (button2_label) { + CImg().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons); + if (button3_label) { + CImg().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons); + if (button4_label) { + CImg().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons); + if (button5_label) { + CImg().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons); + if (button6_label) { + CImg().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons); + }}}}}} + if (!buttons._width) + throw CImgArgumentException("cimg::dialog(): No buttons have been defined."); + cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3); + + unsigned int bw = 0, bh = 0; + cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); } + bw+=8; bh+=8; + if (bw<64) bw = 64; + if (bw>128) bw = 128; + if (bh<24) bh = 24; + if (bh>48) bh = 48; + + CImg button(bw,bh,1,3); + button.draw_rectangle(0,0,bw - 1,bh - 1,gray); + button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white); + button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black); + button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2); + CImg sbutton(bw,bh,1,3); + sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray); + sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black); + sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black); + sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white); + sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black); + sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2); + sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true). + draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); + sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false). + draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); + CImg cbutton(bw,bh,1,3); + cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2). + draw_rectangle(2,2,bw - 3,bh - 3,gray); + cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true). + draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); + cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false). + draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); + + cimglist_for(buttons,ll) { + CImg(cbutton). + draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]). + move_to(cbuttons); + CImg(sbutton). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). + move_to(sbuttons); + CImg(button). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). + move_to(buttons[ll]); + } + + CImg canvas; + if (msg) + ((CImg().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas); + + const unsigned int + bwall = (buttons._width - 1)*(12 + bw) + bw, + w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall), + h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh), + lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)), + ly = (h - 12 - bh - logo._height)/2, + tx = lx + logo._width + 12, + ty = (h - 12 - bh - canvas._height)/2, + bx = (w - bwall)/2, + by = h - 12 - bh; + + if (canvas._data) + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w - 1,h - 1,gray). + draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). + draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black). + draw_image(tx,ty,canvas); + else + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w - 1,h - 1,gray). + draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). + draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black); + if (logo._data) canvas.draw_image(lx,ly,logo); + + unsigned int xbuttons[6] = { 0 }; + cimglist_for(buttons,lll) { + xbuttons[lll] = bx + (bw + 12)*lll; + canvas.draw_image(xbuttons[lll],by,buttons[lll]); + } + + // Open window and enter events loop + CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false); + if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2, + (CImgDisplay::screen_height() - disp.height())/2); + bool stop_flag = false, refresh = false; + int oselected = -1, oclicked = -1, selected = -1, clicked = -1; + while (!disp.is_closed() && !stop_flag) { + if (refresh) { + if (clicked>=0) + CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); + else { + if (selected>=0) + CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); + else canvas.display(disp); + } + refresh = false; + } + disp.wait(15); + if (disp.is_resized()) disp.resize(disp,false); + + if (disp.button()&1) { + oclicked = clicked; + clicked = -1; + cimglist_for(buttons,l) + if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) && + disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) { + clicked = selected = l; + refresh = true; + } + if (clicked!=oclicked) refresh = true; + } else if (clicked>=0) stop_flag = true; + + if (disp.key()) { + oselected = selected; + switch (disp.key()) { + case cimg::keyESC : selected = -1; stop_flag = true; break; + case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break; + case cimg::keyTAB : + case cimg::keyARROWRIGHT : + case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break; + case cimg::keyARROWLEFT : + case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break; + } + disp.set_key(); + if (selected!=oselected) refresh = true; + } + } + if (!disp) selected = -1; + return selected; +#endif + } + + //! Display a simple dialog box, and wait for the user's response \specialization. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const bool is_centered) { + return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + CImg::_logo40x38(),is_centered); + } + + //! Evaluate math expression. + /** + \param expression C-string describing the formula to evaluate. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \return Result of the formula evaluation. + \note Set \c expression to \c 0 to keep evaluating the last specified \c expression. + \par Example + \code + const double + res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1' + res2 = cimg::eval(0,1,1); // will return '1' too + \endcode + **/ + inline double eval(const char *const expression, const double x, const double y, const double z, const double c) { + static const CImg empty; + return empty.eval(expression,x,y,z,c); + } + + template + inline CImg::type> eval(const char *const expression, const CImg& xyzc) { + static const CImg empty; + return empty.eval(expression,xyzc); + } + + } // namespace cimg { ... +} // namespace cimg_library { ... + +//! Short alias name. +namespace cil = cimg_library_suffixed; + +#ifdef _cimg_redefine_False +#define False 0 +#endif +#ifdef _cimg_redefine_True +#define True 1 +#endif +#ifdef _cimg_redefine_Status +#define Status int +#endif +#ifdef _cimg_redefine_Success +#define Success 0 +#endif +#ifdef _cimg_redefine_min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_PI +#define PI 3.141592653589793238462643383 +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +// Local Variables: +// mode: c++ +// End: diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/demo.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/demo.h new file mode 100644 index 0000000..0aafc91 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/demo.h @@ -0,0 +1,136 @@ +/* + + */ + +#ifndef DEMO_H_ +#define DEMO_H_ +//ڴ˴ͷļ +#include "stdio.h" +#include "string.h" +#include "stdlib.h" +#include "math.h" + +#include "demo_define.h" +#include "tool.h" +#include "inital_alg_params_ynr.h" +#include "inital_alg_params_gic.h" +#include "inital_alg_params_lsc.h" +#include "inital_alg_params_lsc2.h" +#include "inital_alg_params_rk_shapren_HW.h" +#include "inital_alg_params_rk_edgefilter.h" + +#include "initial_alg_params_bayernr.h" + +#include "inital_alg_params_rkuvnr.h" +#include "inital_alg_params_rk_cnr.h" + +#include "inital_alg_params_mfnr.h" +#include "rk_aiq_awb_algo_v200.h" +#define FILE_RAW_EXT ".raw" +#define FILE_YUV_EXT ".yuv" +#define FILE_DAT_EXT ".dat" + +typedef enum YUV_FILE_FMT +{ + F_YUV_420SP = 0x00, + F_YUV_420P = 0x01, + F_YUV_422I = 0x02, + F_YUV_422SP = 0x03, + F_YUV_422P = 0x04, + F_YUV_444I = 0x05, + + F_YUV_MAX = 0x10, +}YUV_FILE_FMT_t; + +typedef enum INPUT_FILE_FMT +{ + F_IN_FMT_RAW = 0x00, + F_IN_FMT_YUV, + + + F_IN_FMT_MAX = 0x10, +}INPUT_FILE_FMT_t; + + + +//˴ +typedef struct tag_config_com +{ + int exp_info_en ; + int framenum ; + int rawwid ; + int rawhgt ; + int rawbit ; + int bayerfmt ; + int yuvbit ; + int yuvfmt ; +}tag_config_com; + +typedef struct tag_config_txt +{ + tag_config_com config_com; + + int framecnt ; + int iso ; + int exptime[3] ; + int expgain[3] ; + int rgain ; + int bgain ; + int grgain ; + int gbgain ; + int dGain ; + int lux ; +}tag_config_txt; + +typedef struct tag_ST_DEMO_INPUT_PARAMS +{ + int width; //rawͼ + int height; //rawͼ + int bayerPattern; //bayer patternʽ:0--BGGR,1--GBRG,2--GRBG,3--RGGB + int yuvFmt; //yuv file ʽ: YUV_FILE_FMT_t + int bitValue; //rawλ + int hdr_framenum; + float expGain[MAX_HDR_FRM_NUM]; // + float expTime[MAX_HDR_FRM_NUM]; //عʱ + int rGain; //wb rgain + int bGain; //wb bgain + int grGain; //wb grgain + int gbGain; //wb gbgain + int dGain; //wb gbgain + int fileFmt; //input file format:INPUT_FILE_FMT_t + int width_full; //rawͼ + int height_full; //rawͼ + int crop_width; + int crop_height; + int crop_xoffset; + int crop_yoffset; + + char pathFileCfg[256];//configļ· + char pathRawData[256];//rawͼ· + char nameRawData[256];//rawͼ + char pathExpInfo[256];//exp_infoļ· + char pathReslut[256];//ļ· + char suffix[256]; // ļ׺ַ + char pathRtlin[256]; //rtl in path + int skip_num; + int frame_end; + + int hdr_proc_mode; + int out_mode; + + + char dbgFlg[1024]; // must > ISP_CAP_MAX + int config_full; + + int exp_info_en; + int file_info_en; + FILE *fp_exp_info; +}ST_DEMO_INPUT_PARAMS; + + + +//˴ + + + +#endif // DEMO_H_ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/demo_define.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/demo_define.h new file mode 100644 index 0000000..da7d26e --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/demo_define.h @@ -0,0 +1,44 @@ +/* + + */ + +#ifndef DEFINE_H_ +#define DEFINE_H_ +//ڴ˴ͷļ + + + +#define S7_EDGE 1 +#define GZ 2 +#define DVR 3 +/* +#define ISP_BAYER_NR 1 +#define ISP_DPC 2 +#define ISP_BLC 3 +#define ISP_STAT_3A 4 +#define ISP_LSC 5 +#define ISP_AWBG 6 +#define ISP_VHDM 7 +#define ISP_GAMMA 8 +#define ISP_CSM 9 +#define ISP_DRC 10 +#define ISP_RGB2YUV 11 +#define ISP_LCE 12 +#define ISP_Y_NR 13 +#define ISP_SHARPEN 14 +#define ISP_SCALING 15 +#define ISP_GIC 16 +#define ISP_UV_NR 17 +#define ISP_HDR_MERGE 18 +#define ISP_HDR_TMO 19 +#define ISP_DPN 20 +#define ISP_CTK 21 +#define ISP_WDR 22 +*/ +#define SAVE_RESULT 1 + + +//#define GET_LSC_CALIBRATION_DATA //ȡlens shadingУ궨 + + +#endif // DEFINE_H_ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/amdgpu_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/amdgpu_drm.h new file mode 100644 index 0000000..4fe35d6 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/amdgpu_drm.h @@ -0,0 +1,1067 @@ +/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2014 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __AMDGPU_DRM_H__ +#define __AMDGPU_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_AMDGPU_GEM_CREATE 0x00 +#define DRM_AMDGPU_GEM_MMAP 0x01 +#define DRM_AMDGPU_CTX 0x02 +#define DRM_AMDGPU_BO_LIST 0x03 +#define DRM_AMDGPU_CS 0x04 +#define DRM_AMDGPU_INFO 0x05 +#define DRM_AMDGPU_GEM_METADATA 0x06 +#define DRM_AMDGPU_GEM_WAIT_IDLE 0x07 +#define DRM_AMDGPU_GEM_VA 0x08 +#define DRM_AMDGPU_WAIT_CS 0x09 +#define DRM_AMDGPU_GEM_OP 0x10 +#define DRM_AMDGPU_GEM_USERPTR 0x11 +#define DRM_AMDGPU_WAIT_FENCES 0x12 +#define DRM_AMDGPU_VM 0x13 +#define DRM_AMDGPU_FENCE_TO_HANDLE 0x14 +#define DRM_AMDGPU_SCHED 0x15 + +#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) +#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) +#define DRM_IOCTL_AMDGPU_CTX DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx) +#define DRM_IOCTL_AMDGPU_BO_LIST DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list) +#define DRM_IOCTL_AMDGPU_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs) +#define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info) +#define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata) +#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle) +#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, struct drm_amdgpu_gem_va) +#define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs) +#define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op) +#define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) +#define DRM_IOCTL_AMDGPU_WAIT_FENCES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences) +#define DRM_IOCTL_AMDGPU_VM DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm) +#define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle) +#define DRM_IOCTL_AMDGPU_SCHED DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_SCHED, union drm_amdgpu_sched) + +/** + * DOC: memory domains + * + * %AMDGPU_GEM_DOMAIN_CPU System memory that is not GPU accessible. + * Memory in this pool could be swapped out to disk if there is pressure. + * + * %AMDGPU_GEM_DOMAIN_GTT GPU accessible system memory, mapped into the + * GPU's virtual address space via gart. Gart memory linearizes non-contiguous + * pages of system memory, allows GPU access system memory in a linezrized + * fashion. + * + * %AMDGPU_GEM_DOMAIN_VRAM Local video memory. For APUs, it is memory + * carved out by the BIOS. + * + * %AMDGPU_GEM_DOMAIN_GDS Global on-chip data storage used to share data + * across shader threads. + * + * %AMDGPU_GEM_DOMAIN_GWS Global wave sync, used to synchronize the + * execution of all the waves on a device. + * + * %AMDGPU_GEM_DOMAIN_OA Ordered append, used by 3D or Compute engines + * for appending data. + */ +#define AMDGPU_GEM_DOMAIN_CPU 0x1 +#define AMDGPU_GEM_DOMAIN_GTT 0x2 +#define AMDGPU_GEM_DOMAIN_VRAM 0x4 +#define AMDGPU_GEM_DOMAIN_GDS 0x8 +#define AMDGPU_GEM_DOMAIN_GWS 0x10 +#define AMDGPU_GEM_DOMAIN_OA 0x20 +#define AMDGPU_GEM_DOMAIN_MASK (AMDGPU_GEM_DOMAIN_CPU | \ + AMDGPU_GEM_DOMAIN_GTT | \ + AMDGPU_GEM_DOMAIN_VRAM | \ + AMDGPU_GEM_DOMAIN_GDS | \ + AMDGPU_GEM_DOMAIN_GWS | \ + AMDGPU_GEM_DOMAIN_OA) + +/* Flag that CPU access will be required for the case of VRAM domain */ +#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) +/* Flag that CPU access will not work, this VRAM domain is invisible */ +#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) +/* Flag that USWC attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2) +/* Flag that the memory should be in VRAM and cleared */ +#define AMDGPU_GEM_CREATE_VRAM_CLEARED (1 << 3) +/* Flag that create shadow bo(GTT) while allocating vram bo */ +#define AMDGPU_GEM_CREATE_SHADOW (1 << 4) +/* Flag that allocating the BO should use linear VRAM */ +#define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 5) +/* Flag that BO is always valid in this VM */ +#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 6) +/* Flag that BO sharing will be explicitly synchronized */ +#define AMDGPU_GEM_CREATE_EXPLICIT_SYNC (1 << 7) +/* Flag that indicates allocating MQD gart on GFX9, where the mtype + * for the second page onward should be set to NC. + */ +#define AMDGPU_GEM_CREATE_MQD_GFX9 (1 << 8) +/* Flag that BO may contain sensitive data that must be wiped before + * releasing the memory + */ +#define AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE (1 << 9) + +struct drm_amdgpu_gem_create_in { + /** the requested memory size */ + __u64 bo_size; + /** physical start_addr alignment in bytes for some HW requirements */ + __u64 alignment; + /** the requested memory domains */ + __u64 domains; + /** allocation flags */ + __u64 domain_flags; +}; + +struct drm_amdgpu_gem_create_out { + /** returned GEM object handle */ + __u32 handle; + __u32 _pad; +}; + +union drm_amdgpu_gem_create { + struct drm_amdgpu_gem_create_in in; + struct drm_amdgpu_gem_create_out out; +}; + +/** Opcode to create new residency list. */ +#define AMDGPU_BO_LIST_OP_CREATE 0 +/** Opcode to destroy previously created residency list */ +#define AMDGPU_BO_LIST_OP_DESTROY 1 +/** Opcode to update resource information in the list */ +#define AMDGPU_BO_LIST_OP_UPDATE 2 + +struct drm_amdgpu_bo_list_in { + /** Type of operation */ + __u32 operation; + /** Handle of list or 0 if we want to create one */ + __u32 list_handle; + /** Number of BOs in list */ + __u32 bo_number; + /** Size of each element describing BO */ + __u32 bo_info_size; + /** Pointer to array describing BOs */ + __u64 bo_info_ptr; +}; + +struct drm_amdgpu_bo_list_entry { + /** Handle of BO */ + __u32 bo_handle; + /** New (if specified) BO priority to be used during migration */ + __u32 bo_priority; +}; + +struct drm_amdgpu_bo_list_out { + /** Handle of resource list */ + __u32 list_handle; + __u32 _pad; +}; + +union drm_amdgpu_bo_list { + struct drm_amdgpu_bo_list_in in; + struct drm_amdgpu_bo_list_out out; +}; + +/* context related */ +#define AMDGPU_CTX_OP_ALLOC_CTX 1 +#define AMDGPU_CTX_OP_FREE_CTX 2 +#define AMDGPU_CTX_OP_QUERY_STATE 3 +#define AMDGPU_CTX_OP_QUERY_STATE2 4 + +/* GPU reset status */ +#define AMDGPU_CTX_NO_RESET 0 +/* this the context caused it */ +#define AMDGPU_CTX_GUILTY_RESET 1 +/* some other context caused it */ +#define AMDGPU_CTX_INNOCENT_RESET 2 +/* unknown cause */ +#define AMDGPU_CTX_UNKNOWN_RESET 3 + +/* indicate gpu reset occured after ctx created */ +#define AMDGPU_CTX_QUERY2_FLAGS_RESET (1<<0) +/* indicate vram lost occured after ctx created */ +#define AMDGPU_CTX_QUERY2_FLAGS_VRAMLOST (1<<1) +/* indicate some job from this context once cause gpu hang */ +#define AMDGPU_CTX_QUERY2_FLAGS_GUILTY (1<<2) +/* indicate some errors are detected by RAS */ +#define AMDGPU_CTX_QUERY2_FLAGS_RAS_CE (1<<3) +#define AMDGPU_CTX_QUERY2_FLAGS_RAS_UE (1<<4) + +/* Context priority level */ +#define AMDGPU_CTX_PRIORITY_UNSET -2048 +#define AMDGPU_CTX_PRIORITY_VERY_LOW -1023 +#define AMDGPU_CTX_PRIORITY_LOW -512 +#define AMDGPU_CTX_PRIORITY_NORMAL 0 +/* + * When used in struct drm_amdgpu_ctx_in, a priority above NORMAL requires + * CAP_SYS_NICE or DRM_MASTER +*/ +#define AMDGPU_CTX_PRIORITY_HIGH 512 +#define AMDGPU_CTX_PRIORITY_VERY_HIGH 1023 + +struct drm_amdgpu_ctx_in { + /** AMDGPU_CTX_OP_* */ + __u32 op; + /** For future use, no flags defined so far */ + __u32 flags; + __u32 ctx_id; + /** AMDGPU_CTX_PRIORITY_* */ + __s32 priority; +}; + +union drm_amdgpu_ctx_out { + struct { + __u32 ctx_id; + __u32 _pad; + } alloc; + + struct { + /** For future use, no flags defined so far */ + __u64 flags; + /** Number of resets caused by this context so far. */ + __u32 hangs; + /** Reset status since the last call of the ioctl. */ + __u32 reset_status; + } state; +}; + +union drm_amdgpu_ctx { + struct drm_amdgpu_ctx_in in; + union drm_amdgpu_ctx_out out; +}; + +/* vm ioctl */ +#define AMDGPU_VM_OP_RESERVE_VMID 1 +#define AMDGPU_VM_OP_UNRESERVE_VMID 2 + +struct drm_amdgpu_vm_in { + /** AMDGPU_VM_OP_* */ + __u32 op; + __u32 flags; +}; + +struct drm_amdgpu_vm_out { + /** For future use, no flags defined so far */ + __u64 flags; +}; + +union drm_amdgpu_vm { + struct drm_amdgpu_vm_in in; + struct drm_amdgpu_vm_out out; +}; + +/* sched ioctl */ +#define AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE 1 +#define AMDGPU_SCHED_OP_CONTEXT_PRIORITY_OVERRIDE 2 + +struct drm_amdgpu_sched_in { + /* AMDGPU_SCHED_OP_* */ + __u32 op; + __u32 fd; + /** AMDGPU_CTX_PRIORITY_* */ + __s32 priority; + __u32 ctx_id; +}; + +union drm_amdgpu_sched { + struct drm_amdgpu_sched_in in; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define AMDGPU_GEM_USERPTR_READONLY (1 << 0) +#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1) +#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2) +#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_amdgpu_gem_userptr { + __u64 addr; + __u64 size; + /* AMDGPU_GEM_USERPTR_* */ + __u32 flags; + /* Resulting GEM handle */ + __u32 handle; +}; + +/* SI-CI-VI: */ +/* same meaning as the GB_TILE_MODE and GL_MACRO_TILE_MODE fields */ +#define AMDGPU_TILING_ARRAY_MODE_SHIFT 0 +#define AMDGPU_TILING_ARRAY_MODE_MASK 0xf +#define AMDGPU_TILING_PIPE_CONFIG_SHIFT 4 +#define AMDGPU_TILING_PIPE_CONFIG_MASK 0x1f +#define AMDGPU_TILING_TILE_SPLIT_SHIFT 9 +#define AMDGPU_TILING_TILE_SPLIT_MASK 0x7 +#define AMDGPU_TILING_MICRO_TILE_MODE_SHIFT 12 +#define AMDGPU_TILING_MICRO_TILE_MODE_MASK 0x7 +#define AMDGPU_TILING_BANK_WIDTH_SHIFT 15 +#define AMDGPU_TILING_BANK_WIDTH_MASK 0x3 +#define AMDGPU_TILING_BANK_HEIGHT_SHIFT 17 +#define AMDGPU_TILING_BANK_HEIGHT_MASK 0x3 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_SHIFT 19 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_MASK 0x3 +#define AMDGPU_TILING_NUM_BANKS_SHIFT 21 +#define AMDGPU_TILING_NUM_BANKS_MASK 0x3 + +/* GFX9 and later: */ +#define AMDGPU_TILING_SWIZZLE_MODE_SHIFT 0 +#define AMDGPU_TILING_SWIZZLE_MODE_MASK 0x1f +#define AMDGPU_TILING_DCC_OFFSET_256B_SHIFT 5 +#define AMDGPU_TILING_DCC_OFFSET_256B_MASK 0xFFFFFF +#define AMDGPU_TILING_DCC_PITCH_MAX_SHIFT 29 +#define AMDGPU_TILING_DCC_PITCH_MAX_MASK 0x3FFF +#define AMDGPU_TILING_DCC_INDEPENDENT_64B_SHIFT 43 +#define AMDGPU_TILING_DCC_INDEPENDENT_64B_MASK 0x1 + +/* Set/Get helpers for tiling flags. */ +#define AMDGPU_TILING_SET(field, value) \ + (((__u64)(value) & AMDGPU_TILING_##field##_MASK) << AMDGPU_TILING_##field##_SHIFT) +#define AMDGPU_TILING_GET(value, field) \ + (((__u64)(value) >> AMDGPU_TILING_##field##_SHIFT) & AMDGPU_TILING_##field##_MASK) + +#define AMDGPU_GEM_METADATA_OP_SET_METADATA 1 +#define AMDGPU_GEM_METADATA_OP_GET_METADATA 2 + +/** The same structure is shared for input/output */ +struct drm_amdgpu_gem_metadata { + /** GEM Object handle */ + __u32 handle; + /** Do we want get or set metadata */ + __u32 op; + struct { + /** For future use, no flags defined so far */ + __u64 flags; + /** family specific tiling info */ + __u64 tiling_info; + __u32 data_size_bytes; + __u32 data[64]; + } data; +}; + +struct drm_amdgpu_gem_mmap_in { + /** the GEM object handle */ + __u32 handle; + __u32 _pad; +}; + +struct drm_amdgpu_gem_mmap_out { + /** mmap offset from the vma offset manager */ + __u64 addr_ptr; +}; + +union drm_amdgpu_gem_mmap { + struct drm_amdgpu_gem_mmap_in in; + struct drm_amdgpu_gem_mmap_out out; +}; + +struct drm_amdgpu_gem_wait_idle_in { + /** GEM object handle */ + __u32 handle; + /** For future use, no flags defined so far */ + __u32 flags; + /** Absolute timeout to wait */ + __u64 timeout; +}; + +struct drm_amdgpu_gem_wait_idle_out { + /** BO status: 0 - BO is idle, 1 - BO is busy */ + __u32 status; + /** Returned current memory domain */ + __u32 domain; +}; + +union drm_amdgpu_gem_wait_idle { + struct drm_amdgpu_gem_wait_idle_in in; + struct drm_amdgpu_gem_wait_idle_out out; +}; + +struct drm_amdgpu_wait_cs_in { + /* Command submission handle + * handle equals 0 means none to wait for + * handle equals ~0ull means wait for the latest sequence number + */ + __u64 handle; + /** Absolute timeout to wait */ + __u64 timeout; + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u32 ctx_id; +}; + +struct drm_amdgpu_wait_cs_out { + /** CS status: 0 - CS completed, 1 - CS still busy */ + __u64 status; +}; + +union drm_amdgpu_wait_cs { + struct drm_amdgpu_wait_cs_in in; + struct drm_amdgpu_wait_cs_out out; +}; + +struct drm_amdgpu_fence { + __u32 ctx_id; + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u64 seq_no; +}; + +struct drm_amdgpu_wait_fences_in { + /** This points to uint64_t * which points to fences */ + __u64 fences; + __u32 fence_count; + __u32 wait_all; + __u64 timeout_ns; +}; + +struct drm_amdgpu_wait_fences_out { + __u32 status; + __u32 first_signaled; +}; + +union drm_amdgpu_wait_fences { + struct drm_amdgpu_wait_fences_in in; + struct drm_amdgpu_wait_fences_out out; +}; + +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_PLACEMENT 1 + +/* Sets or returns a value associated with a buffer. */ +struct drm_amdgpu_gem_op { + /** GEM object handle */ + __u32 handle; + /** AMDGPU_GEM_OP_* */ + __u32 op; + /** Input or return value */ + __u64 value; +}; + +#define AMDGPU_VA_OP_MAP 1 +#define AMDGPU_VA_OP_UNMAP 2 +#define AMDGPU_VA_OP_CLEAR 3 +#define AMDGPU_VA_OP_REPLACE 4 + +/* Delay the page table update till the next CS */ +#define AMDGPU_VM_DELAY_UPDATE (1 << 0) + +/* Mapping flags */ +/* readable mapping */ +#define AMDGPU_VM_PAGE_READABLE (1 << 1) +/* writable mapping */ +#define AMDGPU_VM_PAGE_WRITEABLE (1 << 2) +/* executable mapping, new for VI */ +#define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) +/* partially resident texture */ +#define AMDGPU_VM_PAGE_PRT (1 << 4) +/* MTYPE flags use bit 5 to 8 */ +#define AMDGPU_VM_MTYPE_MASK (0xf << 5) +/* Default MTYPE. Pre-AI must use this. Recommended for newer ASICs. */ +#define AMDGPU_VM_MTYPE_DEFAULT (0 << 5) +/* Use NC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_NC (1 << 5) +/* Use WC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_WC (2 << 5) +/* Use CC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_CC (3 << 5) +/* Use UC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_UC (4 << 5) + +struct drm_amdgpu_gem_va { + /** GEM object handle */ + __u32 handle; + __u32 _pad; + /** AMDGPU_VA_OP_* */ + __u32 operation; + /** AMDGPU_VM_PAGE_* */ + __u32 flags; + /** va address to assign . Must be correctly aligned.*/ + __u64 va_address; + /** Specify offset inside of BO to assign. Must be correctly aligned.*/ + __u64 offset_in_bo; + /** Specify mapping size. Must be correctly aligned. */ + __u64 map_size; +}; + +#define AMDGPU_HW_IP_GFX 0 +#define AMDGPU_HW_IP_COMPUTE 1 +#define AMDGPU_HW_IP_DMA 2 +#define AMDGPU_HW_IP_UVD 3 +#define AMDGPU_HW_IP_VCE 4 +#define AMDGPU_HW_IP_UVD_ENC 5 +#define AMDGPU_HW_IP_VCN_DEC 6 +#define AMDGPU_HW_IP_VCN_ENC 7 +#define AMDGPU_HW_IP_VCN_JPEG 8 +#define AMDGPU_HW_IP_NUM 9 + +#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1 + +#define AMDGPU_CHUNK_ID_IB 0x01 +#define AMDGPU_CHUNK_ID_FENCE 0x02 +#define AMDGPU_CHUNK_ID_DEPENDENCIES 0x03 +#define AMDGPU_CHUNK_ID_SYNCOBJ_IN 0x04 +#define AMDGPU_CHUNK_ID_SYNCOBJ_OUT 0x05 +#define AMDGPU_CHUNK_ID_BO_HANDLES 0x06 +#define AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES 0x07 +#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT 0x08 +#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL 0x09 + +struct drm_amdgpu_cs_chunk { + __u32 chunk_id; + __u32 length_dw; + __u64 chunk_data; +}; + +struct drm_amdgpu_cs_in { + /** Rendering context id */ + __u32 ctx_id; + /** Handle of resource list associated with CS */ + __u32 bo_list_handle; + __u32 num_chunks; + __u32 _pad; + /** this points to __u64 * which point to cs chunks */ + __u64 chunks; +}; + +struct drm_amdgpu_cs_out { + __u64 handle; +}; + +union drm_amdgpu_cs { + struct drm_amdgpu_cs_in in; + struct drm_amdgpu_cs_out out; +}; + +/* Specify flags to be used for IB */ + +/* This IB should be submitted to CE */ +#define AMDGPU_IB_FLAG_CE (1<<0) + +/* Preamble flag, which means the IB could be dropped if no context switch */ +#define AMDGPU_IB_FLAG_PREAMBLE (1<<1) + +/* Preempt flag, IB should set Pre_enb bit if PREEMPT flag detected */ +#define AMDGPU_IB_FLAG_PREEMPT (1<<2) + +/* The IB fence should do the L2 writeback but not invalidate any shader + * caches (L2/vL1/sL1/I$). */ +#define AMDGPU_IB_FLAG_TC_WB_NOT_INVALIDATE (1 << 3) + +/* Set GDS_COMPUTE_MAX_WAVE_ID = DEFAULT before PACKET3_INDIRECT_BUFFER. + * This will reset wave ID counters for the IB. + */ +#define AMDGPU_IB_FLAG_RESET_GDS_MAX_WAVE_ID (1 << 4) + +struct drm_amdgpu_cs_chunk_ib { + __u32 _pad; + /** AMDGPU_IB_FLAG_* */ + __u32 flags; + /** Virtual address to begin IB execution */ + __u64 va_start; + /** Size of submission */ + __u32 ib_bytes; + /** HW IP to submit to */ + __u32 ip_type; + /** HW IP index of the same type to submit to */ + __u32 ip_instance; + /** Ring index to submit to */ + __u32 ring; +}; + +struct drm_amdgpu_cs_chunk_dep { + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u32 ctx_id; + __u64 handle; +}; + +struct drm_amdgpu_cs_chunk_fence { + __u32 handle; + __u32 offset; +}; + +struct drm_amdgpu_cs_chunk_sem { + __u32 handle; +}; + +struct drm_amdgpu_cs_chunk_syncobj { + __u32 handle; + __u32 flags; + __u64 point; +}; + +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ 0 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD 1 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNC_FILE_FD 2 + +union drm_amdgpu_fence_to_handle { + struct { + struct drm_amdgpu_fence fence; + __u32 what; + __u32 pad; + } in; + struct { + __u32 handle; + } out; +}; + +struct drm_amdgpu_cs_chunk_data { + union { + struct drm_amdgpu_cs_chunk_ib ib_data; + struct drm_amdgpu_cs_chunk_fence fence_data; + }; +}; + +/** + * Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU + * + */ +#define AMDGPU_IDS_FLAGS_FUSION 0x1 +#define AMDGPU_IDS_FLAGS_PREEMPTION 0x2 + +/* indicate if acceleration can be working */ +#define AMDGPU_INFO_ACCEL_WORKING 0x00 +/* get the crtc_id from the mode object id? */ +#define AMDGPU_INFO_CRTC_FROM_ID 0x01 +/* query hw IP info */ +#define AMDGPU_INFO_HW_IP_INFO 0x02 +/* query hw IP instance count for the specified type */ +#define AMDGPU_INFO_HW_IP_COUNT 0x03 +/* timestamp for GL_ARB_timer_query */ +#define AMDGPU_INFO_TIMESTAMP 0x05 +/* Query the firmware version */ +#define AMDGPU_INFO_FW_VERSION 0x0e + /* Subquery id: Query VCE firmware version */ + #define AMDGPU_INFO_FW_VCE 0x1 + /* Subquery id: Query UVD firmware version */ + #define AMDGPU_INFO_FW_UVD 0x2 + /* Subquery id: Query GMC firmware version */ + #define AMDGPU_INFO_FW_GMC 0x03 + /* Subquery id: Query GFX ME firmware version */ + #define AMDGPU_INFO_FW_GFX_ME 0x04 + /* Subquery id: Query GFX PFP firmware version */ + #define AMDGPU_INFO_FW_GFX_PFP 0x05 + /* Subquery id: Query GFX CE firmware version */ + #define AMDGPU_INFO_FW_GFX_CE 0x06 + /* Subquery id: Query GFX RLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC 0x07 + /* Subquery id: Query GFX MEC firmware version */ + #define AMDGPU_INFO_FW_GFX_MEC 0x08 + /* Subquery id: Query SMC firmware version */ + #define AMDGPU_INFO_FW_SMC 0x0a + /* Subquery id: Query SDMA firmware version */ + #define AMDGPU_INFO_FW_SDMA 0x0b + /* Subquery id: Query PSP SOS firmware version */ + #define AMDGPU_INFO_FW_SOS 0x0c + /* Subquery id: Query PSP ASD firmware version */ + #define AMDGPU_INFO_FW_ASD 0x0d + /* Subquery id: Query VCN firmware version */ + #define AMDGPU_INFO_FW_VCN 0x0e + /* Subquery id: Query GFX RLC SRLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_CNTL 0x0f + /* Subquery id: Query GFX RLC SRLG firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_GPM_MEM 0x10 + /* Subquery id: Query GFX RLC SRLS firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_SRM_MEM 0x11 + /* Subquery id: Query DMCU firmware version */ + #define AMDGPU_INFO_FW_DMCU 0x12 + #define AMDGPU_INFO_FW_TA 0x13 +/* number of bytes moved for TTM migration */ +#define AMDGPU_INFO_NUM_BYTES_MOVED 0x0f +/* the used VRAM size */ +#define AMDGPU_INFO_VRAM_USAGE 0x10 +/* the used GTT size */ +#define AMDGPU_INFO_GTT_USAGE 0x11 +/* Information about GDS, etc. resource configuration */ +#define AMDGPU_INFO_GDS_CONFIG 0x13 +/* Query information about VRAM and GTT domains */ +#define AMDGPU_INFO_VRAM_GTT 0x14 +/* Query information about register in MMR address space*/ +#define AMDGPU_INFO_READ_MMR_REG 0x15 +/* Query information about device: rev id, family, etc. */ +#define AMDGPU_INFO_DEV_INFO 0x16 +/* visible vram usage */ +#define AMDGPU_INFO_VIS_VRAM_USAGE 0x17 +/* number of TTM buffer evictions */ +#define AMDGPU_INFO_NUM_EVICTIONS 0x18 +/* Query memory about VRAM and GTT domains */ +#define AMDGPU_INFO_MEMORY 0x19 +/* Query vce clock table */ +#define AMDGPU_INFO_VCE_CLOCK_TABLE 0x1A +/* Query vbios related information */ +#define AMDGPU_INFO_VBIOS 0x1B + /* Subquery id: Query vbios size */ + #define AMDGPU_INFO_VBIOS_SIZE 0x1 + /* Subquery id: Query vbios image */ + #define AMDGPU_INFO_VBIOS_IMAGE 0x2 +/* Query UVD handles */ +#define AMDGPU_INFO_NUM_HANDLES 0x1C +/* Query sensor related information */ +#define AMDGPU_INFO_SENSOR 0x1D + /* Subquery id: Query GPU shader clock */ + #define AMDGPU_INFO_SENSOR_GFX_SCLK 0x1 + /* Subquery id: Query GPU memory clock */ + #define AMDGPU_INFO_SENSOR_GFX_MCLK 0x2 + /* Subquery id: Query GPU temperature */ + #define AMDGPU_INFO_SENSOR_GPU_TEMP 0x3 + /* Subquery id: Query GPU load */ + #define AMDGPU_INFO_SENSOR_GPU_LOAD 0x4 + /* Subquery id: Query average GPU power */ + #define AMDGPU_INFO_SENSOR_GPU_AVG_POWER 0x5 + /* Subquery id: Query northbridge voltage */ + #define AMDGPU_INFO_SENSOR_VDDNB 0x6 + /* Subquery id: Query graphics voltage */ + #define AMDGPU_INFO_SENSOR_VDDGFX 0x7 + /* Subquery id: Query GPU stable pstate shader clock */ + #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_SCLK 0x8 + /* Subquery id: Query GPU stable pstate memory clock */ + #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_MCLK 0x9 +/* Number of VRAM page faults on CPU access. */ +#define AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS 0x1E +#define AMDGPU_INFO_VRAM_LOST_COUNTER 0x1F +/* query ras mask of enabled features*/ +#define AMDGPU_INFO_RAS_ENABLED_FEATURES 0x20 + +/* RAS MASK: UMC (VRAM) */ +#define AMDGPU_INFO_RAS_ENABLED_UMC (1 << 0) +/* RAS MASK: SDMA */ +#define AMDGPU_INFO_RAS_ENABLED_SDMA (1 << 1) +/* RAS MASK: GFX */ +#define AMDGPU_INFO_RAS_ENABLED_GFX (1 << 2) +/* RAS MASK: MMHUB */ +#define AMDGPU_INFO_RAS_ENABLED_MMHUB (1 << 3) +/* RAS MASK: ATHUB */ +#define AMDGPU_INFO_RAS_ENABLED_ATHUB (1 << 4) +/* RAS MASK: PCIE */ +#define AMDGPU_INFO_RAS_ENABLED_PCIE (1 << 5) +/* RAS MASK: HDP */ +#define AMDGPU_INFO_RAS_ENABLED_HDP (1 << 6) +/* RAS MASK: XGMI */ +#define AMDGPU_INFO_RAS_ENABLED_XGMI (1 << 7) +/* RAS MASK: DF */ +#define AMDGPU_INFO_RAS_ENABLED_DF (1 << 8) +/* RAS MASK: SMN */ +#define AMDGPU_INFO_RAS_ENABLED_SMN (1 << 9) +/* RAS MASK: SEM */ +#define AMDGPU_INFO_RAS_ENABLED_SEM (1 << 10) +/* RAS MASK: MP0 */ +#define AMDGPU_INFO_RAS_ENABLED_MP0 (1 << 11) +/* RAS MASK: MP1 */ +#define AMDGPU_INFO_RAS_ENABLED_MP1 (1 << 12) +/* RAS MASK: FUSE */ +#define AMDGPU_INFO_RAS_ENABLED_FUSE (1 << 13) + +#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0 +#define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff +#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT 8 +#define AMDGPU_INFO_MMR_SH_INDEX_MASK 0xff + +struct drm_amdgpu_query_fw { + /** AMDGPU_INFO_FW_* */ + __u32 fw_type; + /** + * Index of the IP if there are more IPs of + * the same type. + */ + __u32 ip_instance; + /** + * Index of the engine. Whether this is used depends + * on the firmware type. (e.g. MEC, SDMA) + */ + __u32 index; + __u32 _pad; +}; + +/* Input structure for the INFO ioctl */ +struct drm_amdgpu_info { + /* Where the return value will be stored */ + __u64 return_pointer; + /* The size of the return value. Just like "size" in "snprintf", + * it limits how many bytes the kernel can write. */ + __u32 return_size; + /* The query request id. */ + __u32 query; + + union { + struct { + __u32 id; + __u32 _pad; + } mode_crtc; + + struct { + /** AMDGPU_HW_IP_* */ + __u32 type; + /** + * Index of the IP if there are more IPs of the same + * type. Ignored by AMDGPU_INFO_HW_IP_COUNT. + */ + __u32 ip_instance; + } query_hw_ip; + + struct { + __u32 dword_offset; + /** number of registers to read */ + __u32 count; + __u32 instance; + /** For future use, no flags defined so far */ + __u32 flags; + } read_mmr_reg; + + struct drm_amdgpu_query_fw query_fw; + + struct { + __u32 type; + __u32 offset; + } vbios_info; + + struct { + __u32 type; + } sensor_info; + }; +}; + +struct drm_amdgpu_info_gds { + /** GDS GFX partition size */ + __u32 gds_gfx_partition_size; + /** GDS compute partition size */ + __u32 compute_partition_size; + /** total GDS memory size */ + __u32 gds_total_size; + /** GWS size per GFX partition */ + __u32 gws_per_gfx_partition; + /** GSW size per compute partition */ + __u32 gws_per_compute_partition; + /** OA size per GFX partition */ + __u32 oa_per_gfx_partition; + /** OA size per compute partition */ + __u32 oa_per_compute_partition; + __u32 _pad; +}; + +struct drm_amdgpu_info_vram_gtt { + __u64 vram_size; + __u64 vram_cpu_accessible_size; + __u64 gtt_size; +}; + +struct drm_amdgpu_heap_info { + /** max. physical memory */ + __u64 total_heap_size; + + /** Theoretical max. available memory in the given heap */ + __u64 usable_heap_size; + + /** + * Number of bytes allocated in the heap. This includes all processes + * and private allocations in the kernel. It changes when new buffers + * are allocated, freed, and moved. It cannot be larger than + * heap_size. + */ + __u64 heap_usage; + + /** + * Theoretical possible max. size of buffer which + * could be allocated in the given heap + */ + __u64 max_allocation; +}; + +struct drm_amdgpu_memory_info { + struct drm_amdgpu_heap_info vram; + struct drm_amdgpu_heap_info cpu_accessible_vram; + struct drm_amdgpu_heap_info gtt; +}; + +struct drm_amdgpu_info_firmware { + __u32 ver; + __u32 feature; +}; + +#define AMDGPU_VRAM_TYPE_UNKNOWN 0 +#define AMDGPU_VRAM_TYPE_GDDR1 1 +#define AMDGPU_VRAM_TYPE_DDR2 2 +#define AMDGPU_VRAM_TYPE_GDDR3 3 +#define AMDGPU_VRAM_TYPE_GDDR4 4 +#define AMDGPU_VRAM_TYPE_GDDR5 5 +#define AMDGPU_VRAM_TYPE_HBM 6 +#define AMDGPU_VRAM_TYPE_DDR3 7 +#define AMDGPU_VRAM_TYPE_DDR4 8 +#define AMDGPU_VRAM_TYPE_GDDR6 9 + +struct drm_amdgpu_info_device { + /** PCI Device ID */ + __u32 device_id; + /** Internal chip revision: A0, A1, etc.) */ + __u32 chip_rev; + __u32 external_rev; + /** Revision id in PCI Config space */ + __u32 pci_rev; + __u32 family; + __u32 num_shader_engines; + __u32 num_shader_arrays_per_engine; + /* in KHz */ + __u32 gpu_counter_freq; + __u64 max_engine_clock; + __u64 max_memory_clock; + /* cu information */ + __u32 cu_active_number; + /* NOTE: cu_ao_mask is INVALID, DON'T use it */ + __u32 cu_ao_mask; + __u32 cu_bitmap[4][4]; + /** Render backend pipe mask. One render backend is CB+DB. */ + __u32 enabled_rb_pipes_mask; + __u32 num_rb_pipes; + __u32 num_hw_gfx_contexts; + __u32 _pad; + __u64 ids_flags; + /** Starting virtual address for UMDs. */ + __u64 virtual_address_offset; + /** The maximum virtual address */ + __u64 virtual_address_max; + /** Required alignment of virtual addresses. */ + __u32 virtual_address_alignment; + /** Page table entry - fragment size */ + __u32 pte_fragment_size; + __u32 gart_page_size; + /** constant engine ram size*/ + __u32 ce_ram_size; + /** video memory type info*/ + __u32 vram_type; + /** video memory bit width*/ + __u32 vram_bit_width; + /* vce harvesting instance */ + __u32 vce_harvest_config; + /* gfx double offchip LDS buffers */ + __u32 gc_double_offchip_lds_buf; + /* NGG Primitive Buffer */ + __u64 prim_buf_gpu_addr; + /* NGG Position Buffer */ + __u64 pos_buf_gpu_addr; + /* NGG Control Sideband */ + __u64 cntl_sb_buf_gpu_addr; + /* NGG Parameter Cache */ + __u64 param_buf_gpu_addr; + __u32 prim_buf_size; + __u32 pos_buf_size; + __u32 cntl_sb_buf_size; + __u32 param_buf_size; + /* wavefront size*/ + __u32 wave_front_size; + /* shader visible vgprs*/ + __u32 num_shader_visible_vgprs; + /* CU per shader array*/ + __u32 num_cu_per_sh; + /* number of tcc blocks*/ + __u32 num_tcc_blocks; + /* gs vgt table depth*/ + __u32 gs_vgt_table_depth; + /* gs primitive buffer depth*/ + __u32 gs_prim_buffer_depth; + /* max gs wavefront per vgt*/ + __u32 max_gs_waves_per_vgt; + __u32 _pad1; + /* always on cu bitmap */ + __u32 cu_ao_bitmap[4][4]; + /** Starting high virtual address for UMDs. */ + __u64 high_va_offset; + /** The maximum high virtual address */ + __u64 high_va_max; + /* gfx10 pa_sc_tile_steering_override */ + __u32 pa_sc_tile_steering_override; + /* disabled TCCs */ + __u64 tcc_disabled_mask; +}; + +struct drm_amdgpu_info_hw_ip { + /** Version of h/w IP */ + __u32 hw_ip_version_major; + __u32 hw_ip_version_minor; + /** Capabilities */ + __u64 capabilities_flags; + /** command buffer address start alignment*/ + __u32 ib_start_alignment; + /** command buffer size alignment*/ + __u32 ib_size_alignment; + /** Bitmask of available rings. Bit 0 means ring 0, etc. */ + __u32 available_rings; + __u32 _pad; +}; + +struct drm_amdgpu_info_num_handles { + /** Max handles as supported by firmware for UVD */ + __u32 uvd_max_handles; + /** Handles currently in use for UVD */ + __u32 uvd_used_handles; +}; + +#define AMDGPU_VCE_CLOCK_TABLE_ENTRIES 6 + +struct drm_amdgpu_info_vce_clock_table_entry { + /** System clock */ + __u32 sclk; + /** Memory clock */ + __u32 mclk; + /** VCE clock */ + __u32 eclk; + __u32 pad; +}; + +struct drm_amdgpu_info_vce_clock_table { + struct drm_amdgpu_info_vce_clock_table_entry entries[AMDGPU_VCE_CLOCK_TABLE_ENTRIES]; + __u32 num_valid_entries; + __u32 pad; +}; + +/* + * Supported GPU families + */ +#define AMDGPU_FAMILY_UNKNOWN 0 +#define AMDGPU_FAMILY_SI 110 /* Hainan, Oland, Verde, Pitcairn, Tahiti */ +#define AMDGPU_FAMILY_CI 120 /* Bonaire, Hawaii */ +#define AMDGPU_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */ +#define AMDGPU_FAMILY_VI 130 /* Iceland, Tonga */ +#define AMDGPU_FAMILY_CZ 135 /* Carrizo, Stoney */ +#define AMDGPU_FAMILY_AI 141 /* Vega10 */ +#define AMDGPU_FAMILY_RV 142 /* Raven */ +#define AMDGPU_FAMILY_NV 143 /* Navi10 */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm.h new file mode 100644 index 0000000..438abde --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm.h @@ -0,0 +1,1042 @@ +/** + * \file drm.h + * Header for the Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith + * + * \par Acknowledgments: + * Dec 1999, Richard Henderson , move to generic \c cmpxchg. + */ + +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#if defined(__linux__) + +#include +#include +typedef unsigned int drm_handle_t; + +#else /* One of the BSDs */ + +#include +#include +#include +typedef int8_t __s8; +typedef uint8_t __u8; +typedef int16_t __s16; +typedef uint16_t __u16; +typedef int32_t __s32; +typedef uint32_t __u32; +typedef int64_t __s64; +typedef uint64_t __u64; +typedef size_t __kernel_size_t; +typedef unsigned long drm_handle_t; + +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +typedef unsigned int drm_context_t; +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; + +/** + * Cliprect. + * + * \warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well + * + * \note KW: Actually it's illegal to change either for + * backwards-compatibility reasons. + */ +struct drm_clip_rect { + unsigned short x1; + unsigned short y1; + unsigned short x2; + unsigned short y2; +}; + +/** + * Drawable information. + */ +struct drm_drawable_info { + unsigned int num_rects; + struct drm_clip_rect *rects; +}; + +/** + * Texture region, + */ +struct drm_tex_region { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; + unsigned int age; +}; + +/** + * Hardware lock. + * + * The lock structure is a simple cache-line aligned integer. To avoid + * processor bus contention on a multiprocessor system, there should not be any + * other data stored in the same cache line. + */ +struct drm_hw_lock { + __volatile__ unsigned int lock; /**< lock variable */ + char padding[60]; /**< Pad to cache line */ +}; + +/** + * DRM_IOCTL_VERSION ioctl argument type. + * + * \sa drmGetVersion(). + */ +struct drm_version { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + __kernel_size_t name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + __kernel_size_t date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + __kernel_size_t desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ +}; + +/** + * DRM_IOCTL_GET_UNIQUE ioctl argument type. + * + * \sa drmGetBusid() and drmSetBusId(). + */ +struct drm_unique { + __kernel_size_t unique_len; /**< Length of unique */ + char *unique; /**< Unique name for driver instantiation */ +}; + +struct drm_list { + int count; /**< Length of user-space structures */ + struct drm_version *version; +}; + +struct drm_block { + int unused; +}; + +/** + * DRM_IOCTL_CONTROL ioctl argument type. + * + * \sa drmCtlInstHandler() and drmCtlUninstHandler(). + */ +struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +}; + +/** + * Type of memory to map. + */ +enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /**< no caching, no core dump */ + _DRM_SHM = 2, /**< shared, cached */ + _DRM_AGP = 3, /**< AGP/GART */ + _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ + _DRM_CONSISTENT = 5 /**< Consistent memory for PCI DMA */ +}; + +/** + * Memory mapping flags. + */ +enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /**< shared, cached, locked */ + _DRM_KERNEL = 0x08, /**< kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ + _DRM_REMOVABLE = 0x40, /**< Removable mapping */ + _DRM_DRIVER = 0x80 /**< Managed by driver */ +}; + +struct drm_ctx_priv_map { + unsigned int ctx_id; /**< Context requesting private mapping */ + void *handle; /**< Handle of map */ +}; + +/** + * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls + * argument type. + * + * \sa drmAddMap(). + */ +struct drm_map { + unsigned long offset; /**< Requested physical address (0 for SAREA)*/ + unsigned long size; /**< Requested physical size (bytes) */ + enum drm_map_type type; /**< Type of memory to map */ + enum drm_map_flags flags; /**< Flags */ + void *handle; /**< User-space: "Handle" to pass to mmap() */ + /**< Kernel-space: kernel-virtual address */ + int mtrr; /**< MTRR slot used */ + /* Private data */ +}; + +/** + * DRM_IOCTL_GET_CLIENT ioctl argument type. + */ +struct drm_client { + int idx; /**< Which client desired? */ + int auth; /**< Is client authenticated? */ + unsigned long pid; /**< Process ID */ + unsigned long uid; /**< User ID */ + unsigned long magic; /**< Magic */ + unsigned long iocs; /**< Ioctl count */ +}; + +enum drm_stat_type { + _DRM_STAT_LOCK, + _DRM_STAT_OPENS, + _DRM_STAT_CLOSES, + _DRM_STAT_IOCTLS, + _DRM_STAT_LOCKS, + _DRM_STAT_UNLOCKS, + _DRM_STAT_VALUE, /**< Generic value */ + _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ + _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ + + _DRM_STAT_IRQ, /**< IRQ */ + _DRM_STAT_PRIMARY, /**< Primary DMA bytes */ + _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ + _DRM_STAT_DMA, /**< DMA */ + _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ + _DRM_STAT_MISSED /**< Missed DMA opportunity */ + /* Add to the *END* of the list */ +}; + +/** + * DRM_IOCTL_GET_STATS ioctl argument type. + */ +struct drm_stats { + unsigned long count; + struct { + unsigned long value; + enum drm_stat_type type; + } data[15]; +}; + +/** + * Hardware locking flags. + */ +enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +}; + +/** + * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. + * + * \sa drmGetLock() and drmUnlock(). + */ +struct drm_lock { + int context; + enum drm_lock_flags flags; +}; + +/** + * DMA flags + * + * \warning + * These values \e must match xf86drm.h. + * + * \sa drm_dma. + */ +enum drm_dma_flags { + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note The buffer may not yet have + * been processed by the hardware -- + * getting a hardware lock with the + * hardware quiescent will ensure + * that the buffer has been + * processed. + */ + _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ +}; + +/** + * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. + * + * \sa drmAddBufs(). + */ +struct drm_buf_desc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ + enum { + _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ + _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ + _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ + _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ + } flags; + unsigned long agp_start; /**< + * Start address of where the AGP buffers are + * in the AGP aperture + */ +}; + +/** + * DRM_IOCTL_INFO_BUFS ioctl argument type. + */ +struct drm_buf_info { + int count; /**< Entries in list */ + struct drm_buf_desc *list; +}; + +/** + * DRM_IOCTL_FREE_BUFS ioctl argument type. + */ +struct drm_buf_free { + int count; + int *list; +}; + +/** + * Buffer information + * + * \sa drm_buf_map. + */ +struct drm_buf_pub { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + void *address; /**< Address of buffer */ +}; + +/** + * DRM_IOCTL_MAP_BUFS ioctl argument type. + */ +struct drm_buf_map { + int count; /**< Length of the buffer list */ +#ifdef __cplusplus + void *virt; +#else + void *virtual; /**< Mmap'd area in user-virtual */ +#endif + struct drm_buf_pub *list; /**< Buffer information */ +}; + +/** + * DRM_IOCTL_DMA ioctl argument type. + * + * Indices here refer to the offset into the buffer list in drm_buf_get. + * + * \sa drmDMA(). + */ +struct drm_dma { + int context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_indices; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send */ + enum drm_dma_flags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size for buffers */ + int *request_indices; /**< Buffer information */ + int *request_sizes; + int granted_count; /**< Number of buffers granted */ +}; + +enum drm_ctx_flags { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +}; + +/** + * DRM_IOCTL_ADD_CTX ioctl argument type. + * + * \sa drmCreateContext() and drmDestroyContext(). + */ +struct drm_ctx { + drm_context_t handle; + enum drm_ctx_flags flags; +}; + +/** + * DRM_IOCTL_RES_CTX ioctl argument type. + */ +struct drm_ctx_res { + int count; + struct drm_ctx *contexts; +}; + +/** + * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. + */ +struct drm_draw { + drm_drawable_t handle; +}; + +/** + * DRM_IOCTL_UPDATE_DRAW ioctl argument type. + */ +typedef enum { + DRM_DRAWABLE_CLIPRECTS +} drm_drawable_info_type_t; + +struct drm_update_draw { + drm_drawable_t handle; + unsigned int type; + unsigned int num; + unsigned long long data; +}; + +/** + * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. + */ +struct drm_auth { + drm_magic_t magic; +}; + +/** + * DRM_IOCTL_IRQ_BUSID ioctl argument type. + * + * \sa drmGetInterruptFromBusID(). + */ +struct drm_irq_busid { + int irq; /**< IRQ number */ + int busnum; /**< bus number */ + int devnum; /**< device number */ + int funcnum; /**< function number */ +}; + +enum drm_vblank_seq_type { + _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + /* bits 1-6 are reserved for high crtcs */ + _DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, + _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking, unsupported */ +}; +#define _DRM_VBLANK_HIGH_CRTC_SHIFT 1 + +#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \ + _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS) + +struct drm_wait_vblank_request { + enum drm_vblank_seq_type type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { + enum drm_vblank_seq_type type; + unsigned int sequence; + long tval_sec; + long tval_usec; +}; + +/** + * DRM_IOCTL_WAIT_VBLANK ioctl argument type. + * + * \sa drmWaitVBlank(). + */ +union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +}; + +#define _DRM_PRE_MODESET 1 +#define _DRM_POST_MODESET 2 + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +struct drm_modeset_ctl { + __u32 crtc; + __u32 cmd; +}; + +/** + * DRM_IOCTL_AGP_ENABLE ioctl argument type. + * + * \sa drmAgpEnable(). + */ +struct drm_agp_mode { + unsigned long mode; /**< AGP mode */ +}; + +/** + * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. + * + * \sa drmAgpAlloc() and drmAgpFree(). + */ +struct drm_agp_buffer { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for binding / unbinding */ + unsigned long type; /**< Type of memory to allocate */ + unsigned long physical; /**< Physical used by i810 */ +}; + +/** + * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. + * + * \sa drmAgpBind() and drmAgpUnbind(). + */ +struct drm_agp_binding { + unsigned long handle; /**< From drm_agp_buffer */ + unsigned long offset; /**< In bytes -- will round to page boundary */ +}; + +/** + * DRM_IOCTL_AGP_INFO ioctl argument type. + * + * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), + * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), + * drmAgpVendorId() and drmAgpDeviceId(). + */ +struct drm_agp_info { + int agp_version_major; + int agp_version_minor; + unsigned long mode; + unsigned long aperture_base; /* physical address */ + unsigned long aperture_size; /* bytes */ + unsigned long memory_allowed; /* bytes */ + unsigned long memory_used; + + /* PCI information */ + unsigned short id_vendor; + unsigned short id_device; +}; + +/** + * DRM_IOCTL_SG_ALLOC ioctl argument type. + */ +struct drm_scatter_gather { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for mapping / unmapping */ +}; + +/** + * DRM_IOCTL_SET_VERSION ioctl argument type. + */ +struct drm_set_version { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +}; + +/** DRM_IOCTL_GEM_CLOSE ioctl argument type */ +struct drm_gem_close { + /** Handle of the object to be closed. */ + __u32 handle; + __u32 pad; +}; + +/** DRM_IOCTL_GEM_FLINK ioctl argument type */ +struct drm_gem_flink { + /** Handle for the object being named */ + __u32 handle; + + /** Returned global name */ + __u32 name; +}; + +/** DRM_IOCTL_GEM_OPEN ioctl argument type */ +struct drm_gem_open { + /** Name of object being opened */ + __u32 name; + + /** Returned handle for the object */ + __u32 handle; + + /** Returned size of the object */ + __u64 size; +}; + +#define DRM_CAP_DUMB_BUFFER 0x1 +#define DRM_CAP_VBLANK_HIGH_CRTC 0x2 +#define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 +#define DRM_CAP_DUMB_PREFER_SHADOW 0x4 +#define DRM_CAP_PRIME 0x5 +#define DRM_PRIME_CAP_IMPORT 0x1 +#define DRM_PRIME_CAP_EXPORT 0x2 +#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 +#define DRM_CAP_ASYNC_PAGE_FLIP 0x7 +/* + * The CURSOR_WIDTH and CURSOR_HEIGHT capabilities return a valid widthxheight + * combination for the hardware cursor. The intention is that a hardware + * agnostic userspace can query a cursor plane size to use. + * + * Note that the cross-driver contract is to merely return a valid size; + * drivers are free to attach another meaning on top, eg. i915 returns the + * maximum plane size. + */ +#define DRM_CAP_CURSOR_WIDTH 0x8 +#define DRM_CAP_CURSOR_HEIGHT 0x9 +#define DRM_CAP_ADDFB2_MODIFIERS 0x10 +#define DRM_CAP_PAGE_FLIP_TARGET 0x11 +#define DRM_CAP_CRTC_IN_VBLANK_EVENT 0x12 +#define DRM_CAP_SYNCOBJ 0x13 +#define DRM_CAP_SYNCOBJ_TIMELINE 0x14 + +/** DRM_IOCTL_GET_CAP ioctl argument type */ +struct drm_get_cap { + __u64 capability; + __u64 value; +}; + +/** + * DRM_CLIENT_CAP_STEREO_3D + * + * if set to 1, the DRM core will expose the stereo 3D capabilities of the + * monitor by advertising the supported 3D layouts in the flags of struct + * drm_mode_modeinfo. + */ +#define DRM_CLIENT_CAP_STEREO_3D 1 + +/** + * DRM_CLIENT_CAP_UNIVERSAL_PLANES + * + * If set to 1, the DRM core will expose all planes (overlay, primary, and + * cursor) to userspace. + */ +#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 + +/** + * DRM_CLIENT_CAP_ATOMIC + * + * If set to 1, the DRM core will expose atomic properties to userspace + */ +#define DRM_CLIENT_CAP_ATOMIC 3 + +/** + * DRM_CLIENT_CAP_ASPECT_RATIO + * + * If set to 1, the DRM core will provide aspect ratio information in modes. + */ +#define DRM_CLIENT_CAP_ASPECT_RATIO 4 + +/** + * DRM_CLIENT_CAP_WRITEBACK_CONNECTORS + * + * If set to 1, the DRM core will expose special connectors to be used for + * writing back to memory the scene setup in the commit. Depends on client + * also supporting DRM_CLIENT_CAP_ATOMIC + */ +#define DRM_CLIENT_CAP_WRITEBACK_CONNECTORS 5 + +/** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ +struct drm_set_client_cap { + __u64 capability; + __u64 value; +}; + +#define DRM_RDWR O_RDWR +#define DRM_CLOEXEC O_CLOEXEC +struct drm_prime_handle { + __u32 handle; + + /** Flags.. only applicable for handle->fd */ + __u32 flags; + + /** Returned dmabuf file descriptor */ + __s32 fd; +}; + +struct drm_syncobj_create { + __u32 handle; +#define DRM_SYNCOBJ_CREATE_SIGNALED (1 << 0) + __u32 flags; +}; + +struct drm_syncobj_destroy { + __u32 handle; + __u32 pad; +}; + +#define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE (1 << 0) +struct drm_syncobj_handle { + __u32 handle; + __u32 flags; + + __s32 fd; + __u32 pad; +}; + +struct drm_syncobj_transfer { + __u32 src_handle; + __u32 dst_handle; + __u64 src_point; + __u64 dst_point; + __u32 flags; + __u32 pad; +}; + +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0) +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1) +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2) /* wait for time point to become available */ +struct drm_syncobj_wait { + __u64 handles; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + +struct drm_syncobj_timeline_wait { + __u64 handles; + /* wait on specific timeline point for every handles*/ + __u64 points; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + + +struct drm_syncobj_array { + __u64 handles; + __u32 count_handles; + __u32 pad; +}; + +struct drm_syncobj_timeline_array { + __u64 handles; + __u64 points; + __u32 count_handles; + __u32 pad; +}; + + +/* Query current scanout sequence number */ +struct drm_crtc_get_sequence { + __u32 crtc_id; /* requested crtc_id */ + __u32 active; /* return: crtc output is active */ + __u64 sequence; /* return: most recent vblank sequence */ + __s64 sequence_ns; /* return: most recent time of first pixel out */ +}; + +/* Queue event to be delivered at specified sequence. Time stamp marks + * when the first pixel of the refresh cycle leaves the display engine + * for the display + */ +#define DRM_CRTC_SEQUENCE_RELATIVE 0x00000001 /* sequence is relative to current */ +#define DRM_CRTC_SEQUENCE_NEXT_ON_MISS 0x00000002 /* Use next sequence if we've missed */ + +struct drm_crtc_queue_sequence { + __u32 crtc_id; + __u32 flags; + __u64 sequence; /* on input, target sequence. on output, actual sequence */ + __u64 user_data; /* user data passed to event */ +}; + +#if defined(__cplusplus) +} +#endif + +#include "drm_mode.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_IOCTL_BASE 'd' +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) +#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) +#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, struct drm_unique) +#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, struct drm_auth) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, struct drm_irq_busid) +#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, struct drm_map) +#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) +#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) +#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) +#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) +#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) +#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) +#define DRM_IOCTL_GET_CAP DRM_IOWR(0x0c, struct drm_get_cap) +#define DRM_IOCTL_SET_CLIENT_CAP DRM_IOW( 0x0d, struct drm_set_client_cap) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, struct drm_block) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, struct drm_block) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, struct drm_control) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, struct drm_map) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, struct drm_buf_desc) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, struct drm_buf_desc) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, struct drm_buf_info) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, struct drm_buf_map) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, struct drm_buf_free) + +#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, struct drm_map) + +#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) +#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) + +#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) +#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, struct drm_ctx) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, struct drm_ctx) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, struct drm_ctx) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, struct drm_ctx_res) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, struct drm_draw) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, struct drm_draw) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, struct drm_dma) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, struct drm_lock) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) + +#define DRM_IOCTL_PRIME_HANDLE_TO_FD DRM_IOWR(0x2d, struct drm_prime_handle) +#define DRM_IOCTL_PRIME_FD_TO_HANDLE DRM_IOWR(0x2e, struct drm_prime_handle) + +#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) +#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) +#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, struct drm_agp_mode) +#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, struct drm_agp_info) +#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) +#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) + +#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) + +#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) + +#define DRM_IOCTL_CRTC_GET_SEQUENCE DRM_IOWR(0x3b, struct drm_crtc_get_sequence) +#define DRM_IOCTL_CRTC_QUEUE_SEQUENCE DRM_IOWR(0x3c, struct drm_crtc_queue_sequence) + +#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) + +#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) +#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) +#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) +#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) +#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) /* deprecated (never worked) */ +#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) /* deprecated (never worked) */ + +#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) +#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) +#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) +#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) +#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip) +#define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd) + +#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb) +#define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb) +#define DRM_IOCTL_MODE_DESTROY_DUMB DRM_IOWR(0xB4, struct drm_mode_destroy_dumb) +#define DRM_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xB5, struct drm_mode_get_plane_res) +#define DRM_IOCTL_MODE_GETPLANE DRM_IOWR(0xB6, struct drm_mode_get_plane) +#define DRM_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct drm_mode_set_plane) +#define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2) +#define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) +#define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) +#define DRM_IOCTL_MODE_CURSOR2 DRM_IOWR(0xBB, struct drm_mode_cursor2) +#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic) +#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob) +#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob) + +#define DRM_IOCTL_SYNCOBJ_CREATE DRM_IOWR(0xBF, struct drm_syncobj_create) +#define DRM_IOCTL_SYNCOBJ_DESTROY DRM_IOWR(0xC0, struct drm_syncobj_destroy) +#define DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD DRM_IOWR(0xC1, struct drm_syncobj_handle) +#define DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE DRM_IOWR(0xC2, struct drm_syncobj_handle) +#define DRM_IOCTL_SYNCOBJ_WAIT DRM_IOWR(0xC3, struct drm_syncobj_wait) +#define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array) +#define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array) + +#define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease) +#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees) +#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease) +#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease) + +#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait) +#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array) +#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) +#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) + +/** + * Device specific ioctls should only be in their respective headers + * The device specific ioctl range is from 0x40 to 0x9f. + * Generic IOCTLS restart at 0xA0. + * + * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and + * drmCommandReadWrite(). + */ +#define DRM_COMMAND_BASE 0x40 +#define DRM_COMMAND_END 0xA0 + +/** + * Header for events written back to userspace on the drm fd. The + * type defines the type of event, the length specifies the total + * length of the event (including the header), and user_data is + * typically a 64 bit value passed with the ioctl that triggered the + * event. A read on the drm fd will always only return complete + * events, that is, if for example the read buffer is 100 bytes, and + * there are two 64 byte events pending, only one will be returned. + * + * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and + * up are chipset specific. + */ +struct drm_event { + __u32 type; + __u32 length; +}; + +#define DRM_EVENT_VBLANK 0x01 +#define DRM_EVENT_FLIP_COMPLETE 0x02 +#define DRM_EVENT_CRTC_SEQUENCE 0x03 + +struct drm_event_vblank { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 crtc_id; /* 0 on older kernels that do not support this */ +}; + +/* Event delivered at sequence. Time stamp marks when the first pixel + * of the refresh cycle leaves the display engine for the display + */ +struct drm_event_crtc_sequence { + struct drm_event base; + __u64 user_data; + __s64 time_ns; + __u64 sequence; +}; + +/* typedef area */ +typedef struct drm_clip_rect drm_clip_rect_t; +typedef struct drm_drawable_info drm_drawable_info_t; +typedef struct drm_tex_region drm_tex_region_t; +typedef struct drm_hw_lock drm_hw_lock_t; +typedef struct drm_version drm_version_t; +typedef struct drm_unique drm_unique_t; +typedef struct drm_list drm_list_t; +typedef struct drm_block drm_block_t; +typedef struct drm_control drm_control_t; +typedef enum drm_map_type drm_map_type_t; +typedef enum drm_map_flags drm_map_flags_t; +typedef struct drm_ctx_priv_map drm_ctx_priv_map_t; +typedef struct drm_map drm_map_t; +typedef struct drm_client drm_client_t; +typedef enum drm_stat_type drm_stat_type_t; +typedef struct drm_stats drm_stats_t; +typedef enum drm_lock_flags drm_lock_flags_t; +typedef struct drm_lock drm_lock_t; +typedef enum drm_dma_flags drm_dma_flags_t; +typedef struct drm_buf_desc drm_buf_desc_t; +typedef struct drm_buf_info drm_buf_info_t; +typedef struct drm_buf_free drm_buf_free_t; +typedef struct drm_buf_pub drm_buf_pub_t; +typedef struct drm_buf_map drm_buf_map_t; +typedef struct drm_dma drm_dma_t; +typedef union drm_wait_vblank drm_wait_vblank_t; +typedef struct drm_agp_mode drm_agp_mode_t; +typedef enum drm_ctx_flags drm_ctx_flags_t; +typedef struct drm_ctx drm_ctx_t; +typedef struct drm_ctx_res drm_ctx_res_t; +typedef struct drm_draw drm_draw_t; +typedef struct drm_update_draw drm_update_draw_t; +typedef struct drm_auth drm_auth_t; +typedef struct drm_irq_busid drm_irq_busid_t; +typedef enum drm_vblank_seq_type drm_vblank_seq_type_t; + +typedef struct drm_agp_buffer drm_agp_buffer_t; +typedef struct drm_agp_binding drm_agp_binding_t; +typedef struct drm_agp_info drm_agp_info_t; +typedef struct drm_scatter_gather drm_scatter_gather_t; +typedef struct drm_set_version drm_set_version_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_fourcc.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_fourcc.h new file mode 100644 index 0000000..5c69090 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_fourcc.h @@ -0,0 +1,763 @@ +/* + * Copyright 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_FOURCC_H +#define DRM_FOURCC_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * DOC: overview + * + * In the DRM subsystem, framebuffer pixel formats are described using the + * fourcc codes defined in `include/uapi/drm/drm_fourcc.h`. In addition to the + * fourcc code, a Format Modifier may optionally be provided, in order to + * further describe the buffer's format - for example tiling or compression. + * + * Format Modifiers + * ---------------- + * + * Format modifiers are used in conjunction with a fourcc code, forming a + * unique fourcc:modifier pair. This format:modifier pair must fully define the + * format and data layout of the buffer, and should be the only way to describe + * that particular buffer. + * + * Having multiple fourcc:modifier pairs which describe the same layout should + * be avoided, as such aliases run the risk of different drivers exposing + * different names for the same data format, forcing userspace to understand + * that they are aliases. + * + * Format modifiers may change any property of the buffer, including the number + * of planes and/or the required allocation size. Format modifiers are + * vendor-namespaced, and as such the relationship between a fourcc code and a + * modifier is specific to the modifer being used. For example, some modifiers + * may preserve meaning - such as number of planes - from the fourcc code, + * whereas others may not. + * + * Vendors should document their modifier usage in as much detail as + * possible, to ensure maximum compatibility across devices, drivers and + * applications. + * + * The authoritative list of format modifier codes is found in + * `include/uapi/drm/drm_fourcc.h` + */ + +#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ + ((__u32)(c) << 16) | ((__u32)(d) << 24)) + +#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ + +/* Reserve 0 for the invalid format specifier */ +#define DRM_FORMAT_INVALID 0 + +/* color index */ +#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ + +/* 8 bpp Red */ +#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ + +/* 16 bpp Red */ +#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ + +/* 16 bpp RG */ +#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ +#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ + +/* 32 bpp RG */ +#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ +#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ + +/* 8 bpp RGB */ +#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ +#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ + +/* 16 bpp RGB */ +#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ + +#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ + +#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ + +#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ + +#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ +#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ + +/* 24 bpp RGB */ +#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ +#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ + +/* 32 bpp RGB */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ + +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ + +#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ + +#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ + +/* + * Floating point 64bpp RGB + * IEEE 754-2008 binary16 half-precision float + * [15:0] sign:exponent:mantissa 1:5:10 + */ +#define DRM_FORMAT_XRGB16161616F fourcc_code('X', 'R', '4', 'H') /* [63:0] x:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_XBGR16161616F fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */ + +#define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ + +/* packed YCbCr */ +#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ +#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ + +#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_VUY888 fourcc_code('V', 'U', '2', '4') /* [23:0] Cr:Cb:Y 8:8:8 little endian */ +#define DRM_FORMAT_VUY101010 fourcc_code('V', 'U', '3', '0') /* Y followed by U then V, 10:10:10. Non-linear modifier only */ + +/* + * packed Y2xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb + */ +#define DRM_FORMAT_Y210 fourcc_code('Y', '2', '1', '0') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels */ +#define DRM_FORMAT_Y212 fourcc_code('Y', '2', '1', '2') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels */ +#define DRM_FORMAT_Y216 fourcc_code('Y', '2', '1', '6') /* [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels */ + +/* + * packed Y4xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb except Y410 + */ +#define DRM_FORMAT_Y410 fourcc_code('Y', '4', '1', '0') /* [31:0] A:Cr:Y:Cb 2:10:10:10 little endian */ +#define DRM_FORMAT_Y412 fourcc_code('Y', '4', '1', '2') /* [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ +#define DRM_FORMAT_Y416 fourcc_code('Y', '4', '1', '6') /* [63:0] A:Cr:Y:Cb 16:16:16:16 little endian */ + +#define DRM_FORMAT_XVYU2101010 fourcc_code('X', 'V', '3', '0') /* [31:0] X:Cr:Y:Cb 2:10:10:10 little endian */ +#define DRM_FORMAT_XVYU12_16161616 fourcc_code('X', 'V', '3', '6') /* [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ +#define DRM_FORMAT_XVYU16161616 fourcc_code('X', 'V', '4', '8') /* [63:0] X:Cr:Y:Cb 16:16:16:16 little endian */ + +/* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ +/* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_Y0L0 fourcc_code('Y', '0', 'L', '0') +/* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_X0L0 fourcc_code('X', '0', 'L', '0') + +/* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_Y0L2 fourcc_code('Y', '0', 'L', '2') +/* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_X0L2 fourcc_code('X', '0', 'L', '2') + +/* + * 1-plane YUV 4:2:0 + * In these formats, the component ordering is specified (Y, followed by U + * then V), but the exact Linear layout is undefined. + * These formats can only be used with a non-Linear modifier. + */ +#define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8') +#define DRM_FORMAT_YUV420_10BIT fourcc_code('Y', 'U', '1', '0') + +/* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ +#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') +#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') +#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') +#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') +#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') +#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') +#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') +#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') + +/* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ +#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ +#define DRM_FORMAT_P210 fourcc_code('P', '2', '1', '0') /* 2x1 subsampled Cr:Cb plane, 10 bit per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [12:4] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [12:4:12:4] little endian + */ +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane 12 bits per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr:Cb plane, [31:0] Cr:Cb [16:16] little endian + */ +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */ + +/* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ +#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ + + +/* + * Format Modifiers: + * + * Format modifiers describe, typically, a re-ordering or modification + * of the data in a plane of an FB. This can be used to express tiled/ + * swizzled formats, or compression, or a combination of the two. + * + * The upper 8 bits of the format modifier are a vendor-id as assigned + * below. The lower 56 bits are assigned as vendor sees fit. + */ + +/* Vendor Ids: */ +#define DRM_FORMAT_MOD_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 +#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 +#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03 +#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 +#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 +#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 +#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 +#define DRM_FORMAT_MOD_VENDOR_ARM 0x08 +#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 + +/* add more to the end as needed */ + +#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) + +#define fourcc_mod_code(vendor, val) \ + ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) + +/* + * Format Modifier tokens: + * + * When adding a new token please document the layout with a code comment, + * similar to the fourcc codes above. drm_fourcc.h is considered the + * authoritative source for all of these. + */ + +/* + * Invalid Modifier + * + * This modifier can be used as a sentinel to terminate the format modifiers + * list, or to initialize a variable with an invalid modifier. It might also be + * used to report an error back to userspace for certain APIs. + */ +#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) + +/* + * Linear Layout + * + * Just plain linear layout. Note that this is different from no specifying any + * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), + * which tells the driver to also take driver-internal information into account + * and so might actually result in a tiled framebuffer. + */ +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) + +/* Intel framebuffer modifiers */ + +/* + * Intel X-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out row-major, with + * a platform-dependent stride. On top of that the memory can apply + * platform-depending swizzling of some higher address bits into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) + +/* + * Intel Y-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) + * chunks column-major, with a platform-dependent height. On top of that the + * memory can apply platform-depending swizzling of some higher address bits + * into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) + +/* + * Intel Yf-tiling layout + * + * This is a tiled layout using 4Kb tiles in row-major layout. + * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which + * are arranged in four groups (two wide, two high) with column-major layout. + * Each group therefore consists out of four 256 byte units, which are also laid + * out as 2x2 column-major. + * 256 byte units are made out of four 64 byte blocks of pixels, producing + * either a square block or a 2:1 unit. + * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width + * in pixel depends on the pixel depth. + */ +#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) + +/* + * Intel color control surface (CCS) for render compression + * + * The framebuffer format must be one of the 8:8:8:8 RGB formats. + * The main surface will be plane index 0 and must be Y/Yf-tiled, + * the CCS will be plane index 1. + * + * Each CCS tile matches a 1024x512 pixel area of the main surface. + * To match certain aspects of the 3D hardware the CCS is + * considered to be made up of normal 128Bx32 Y tiles, Thus + * the CCS pitch must be specified in multiples of 128 bytes. + * + * In reality the CCS tile appears to be a 64Bx64 Y tile, composed + * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. + * But that fact is not relevant unless the memory is accessed + * directly. + */ +#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) +#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) + +/* + * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks + * + * Macroblocks are laid in a Z-shape, and each pixel data is following the + * standard NV12 style. + * As for NV12, an image is the result of two frame buffers: one for Y, + * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). + * Alignment requirements are (for each buffer): + * - multiple of 128 pixels for the width + * - multiple of 32 pixels for the height + * + * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html + */ +#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) + +/* + * Tiled, 16 (pixels) x 16 (lines) - sized macroblocks + * + * This is a simple tiled layout using tiles of 16x16 pixels in a row-major + * layout. For YCbCr formats Cb/Cr components are taken in such a way that + * they correspond to their 16x16 luma block. + */ +#define DRM_FORMAT_MOD_SAMSUNG_16_16_TILE fourcc_mod_code(SAMSUNG, 2) + +/* + * Qualcomm Compressed Format + * + * Refers to a compressed variant of the base format that is compressed. + * Implementation may be platform and base-format specific. + * + * Each macrotile consists of m x n (mostly 4 x 4) tiles. + * Pixel data pitch/stride is aligned with macrotile width. + * Pixel data height is aligned with macrotile height. + * Entire pixel data buffer is aligned with 4k(bytes). + */ +#define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1) + +/* Vivante framebuffer modifiers */ + +/* + * Vivante 4x4 tiling layout + * + * This is a simple tiled layout using tiles of 4x4 pixels in a row-major + * layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) + +/* + * Vivante 64x64 super-tiling layout + * + * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile + * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- + * major layout. + * + * For more information: see + * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling + */ +#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) + +/* + * Vivante 4x4 tiling layout for dual-pipe + * + * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a + * different base address. Offsets from the base addresses are therefore halved + * compared to the non-split tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) + +/* + * Vivante 64x64 super-tiling layout for dual-pipe + * + * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile + * starts at a different base address. Offsets from the base addresses are + * therefore halved compared to the non-split super-tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) + +/* NVIDIA frame buffer modifiers */ + +/* + * Tegra Tiled Layout, used by Tegra 2, 3 and 4. + * + * Pixels are arranged in simple tiles of 16 x 16 bytes. + */ +#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) + +/* + * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later + * + * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked + * vertically by a power of 2 (1 to 32 GOBs) to form a block. + * + * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. + * + * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. + * Valid values are: + * + * 0 == ONE_GOB + * 1 == TWO_GOBS + * 2 == FOUR_GOBS + * 3 == EIGHT_GOBS + * 4 == SIXTEEN_GOBS + * 5 == THIRTYTWO_GOBS + * + * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format + * in full detail. + */ +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ + fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf)) + +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ + fourcc_mod_code(NVIDIA, 0x10) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ + fourcc_mod_code(NVIDIA, 0x11) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ + fourcc_mod_code(NVIDIA, 0x12) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ + fourcc_mod_code(NVIDIA, 0x13) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ + fourcc_mod_code(NVIDIA, 0x14) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ + fourcc_mod_code(NVIDIA, 0x15) + +/* + * Some Broadcom modifiers take parameters, for example the number of + * vertical lines in the image. Reserve the lower 32 bits for modifier + * type, and the next 24 bits for parameters. Top 8 bits are the + * vendor code. + */ +#define __fourcc_mod_broadcom_param_shift 8 +#define __fourcc_mod_broadcom_param_bits 48 +#define fourcc_mod_broadcom_code(val, params) \ + fourcc_mod_code(BROADCOM, ((((__u64)params) << __fourcc_mod_broadcom_param_shift) | val)) +#define fourcc_mod_broadcom_param(m) \ + ((int)(((m) >> __fourcc_mod_broadcom_param_shift) & \ + ((1ULL << __fourcc_mod_broadcom_param_bits) - 1))) +#define fourcc_mod_broadcom_mod(m) \ + ((m) & ~(((1ULL << __fourcc_mod_broadcom_param_bits) - 1) << \ + __fourcc_mod_broadcom_param_shift)) + +/* + * Broadcom VC4 "T" format + * + * This is the primary layout that the V3D GPU can texture from (it + * can't do linear). The T format has: + * + * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 + * pixels at 32 bit depth. + * + * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually + * 16x16 pixels). + * + * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On + * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows + * they're (TR, BR, BL, TL), where bottom left is start of memory. + * + * - an image made of 4k tiles in rows either left-to-right (even rows of 4k + * tiles) or right-to-left (odd rows of 4k tiles). + */ +#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) + +/* + * Broadcom SAND format + * + * This is the native format that the H.264 codec block uses. For VC4 + * HVS, it is only valid for H.264 (NV12/21) and RGBA modes. + * + * The image can be considered to be split into columns, and the + * columns are placed consecutively into memory. The width of those + * columns can be either 32, 64, 128, or 256 pixels, but in practice + * only 128 pixel columns are used. + * + * The pitch between the start of each column is set to optimally + * switch between SDRAM banks. This is passed as the number of lines + * of column width in the modifier (we can't use the stride value due + * to various core checks that look at it , so you should set the + * stride to width*cpp). + * + * Note that the column height for this format modifier is the same + * for all of the planes, assuming that each column contains both Y + * and UV. Some SAND-using hardware stores UV in a separate tiled + * image from Y to reduce the column height, which is not supported + * with these modifiers. + */ + +#define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(2, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(3, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(4, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(5, v) + +#define DRM_FORMAT_MOD_BROADCOM_SAND32 \ + DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND64 \ + DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND128 \ + DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND256 \ + DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(0) + +/* Broadcom UIF format + * + * This is the common format for the current Broadcom multimedia + * blocks, including V3D 3.x and newer, newer video codecs, and + * displays. + * + * The image consists of utiles (64b blocks), UIF blocks (2x2 utiles), + * and macroblocks (4x4 UIF blocks). Those 4x4 UIF block groups are + * stored in columns, with padding between the columns to ensure that + * moving from one column to the next doesn't hit the same SDRAM page + * bank. + * + * To calculate the padding, it is assumed that each hardware block + * and the software driving it knows the platform's SDRAM page size, + * number of banks, and XOR address, and that it's identical between + * all blocks using the format. This tiling modifier will use XOR as + * necessary to reduce the padding. If a hardware block can't do XOR, + * the assumption is that a no-XOR tiling modifier will be created. + */ +#define DRM_FORMAT_MOD_BROADCOM_UIF fourcc_mod_code(BROADCOM, 6) + +/* + * Arm Framebuffer Compression (AFBC) modifiers + * + * AFBC is a proprietary lossless image compression protocol and format. + * It provides fine-grained random access and minimizes the amount of data + * transferred between IP blocks. + * + * AFBC has several features which may be supported and/or used, which are + * represented using bits in the modifier. Not all combinations are valid, + * and different devices or use-cases may support different combinations. + * + * Further information on the use of AFBC modifiers can be found in + * Documentation/gpu/afbc.rst + */ +#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) + +/* + * AFBC superblock size + * + * Indicates the superblock size(s) used for the AFBC buffer. The buffer + * size (in pixels) must be aligned to a multiple of the superblock size. + * Four lowest significant bits(LSBs) are reserved for block size. + * + * Where one superblock size is specified, it applies to all planes of the + * buffer (e.g. 16x16, 32x8). When multiple superblock sizes are specified, + * the first applies to the Luma plane and the second applies to the Chroma + * plane(s). e.g. (32x8_64x4 means 32x8 Luma, with 64x4 Chroma). + * Multiple superblock sizes are only valid for multi-plane YCbCr formats. + */ +#define AFBC_FORMAT_MOD_BLOCK_SIZE_MASK 0xf +#define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 (2ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 (3ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4 (4ULL) + +/* + * AFBC lossless colorspace transform + * + * Indicates that the buffer makes use of the AFBC lossless colorspace + * transform. + */ +#define AFBC_FORMAT_MOD_YTR (1ULL << 4) + +/* + * AFBC block-split + * + * Indicates that the payload of each superblock is split. The second + * half of the payload is positioned at a predefined offset from the start + * of the superblock payload. + */ +#define AFBC_FORMAT_MOD_SPLIT (1ULL << 5) + +/* + * AFBC sparse layout + * + * This flag indicates that the payload of each superblock must be stored at a + * predefined position relative to the other superblocks in the same AFBC + * buffer. This order is the same order used by the header buffer. In this mode + * each superblock is given the same amount of space as an uncompressed + * superblock of the particular format would require, rounding up to the next + * multiple of 128 bytes in size. + */ +#define AFBC_FORMAT_MOD_SPARSE (1ULL << 6) + +/* + * AFBC copy-block restrict + * + * Buffers with this flag must obey the copy-block restriction. The restriction + * is such that there are no copy-blocks referring across the border of 8x8 + * blocks. For the subsampled data the 8x8 limitation is also subsampled. + */ +#define AFBC_FORMAT_MOD_CBR (1ULL << 7) + +/* + * AFBC tiled layout + * + * The tiled layout groups superblocks in 8x8 or 4x4 tiles, where all + * superblocks inside a tile are stored together in memory. 8x8 tiles are used + * for pixel formats up to and including 32 bpp while 4x4 tiles are used for + * larger bpp formats. The order between the tiles is scan line. + * When the tiled layout is used, the buffer size (in pixels) must be aligned + * to the tile size. + */ +#define AFBC_FORMAT_MOD_TILED (1ULL << 8) + +/* + * AFBC solid color blocks + * + * Indicates that the buffer makes use of solid-color blocks, whereby bandwidth + * can be reduced if a whole superblock is a single color. + */ +#define AFBC_FORMAT_MOD_SC (1ULL << 9) + +/* + * AFBC double-buffer + * + * Indicates that the buffer is allocated in a layout safe for front-buffer + * rendering. + */ +#define AFBC_FORMAT_MOD_DB (1ULL << 10) + +/* + * AFBC buffer content hints + * + * Indicates that the buffer includes per-superblock content hints. + */ +#define AFBC_FORMAT_MOD_BCH (1ULL << 11) + +/* + * Allwinner tiled modifier + * + * This tiling mode is implemented by the VPU found on all Allwinner platforms, + * codenamed sunxi. It is associated with a YUV format that uses either 2 or 3 + * planes. + * + * With this tiling, the luminance samples are disposed in tiles representing + * 32x32 pixels and the chrominance samples in tiles representing 32x64 pixels. + * The pixel order in each tile is linear and the tiles are disposed linearly, + * both in row-major order. + */ +#define DRM_FORMAT_MOD_ALLWINNER_TILED fourcc_mod_code(ALLWINNER, 1) + +#if defined(__cplusplus) +} +#endif + +#endif /* DRM_FOURCC_H */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_mode.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_mode.h new file mode 100644 index 0000000..5fe6c64 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_mode.h @@ -0,0 +1,914 @@ +/* + * Copyright (c) 2007 Dave Airlie + * Copyright (c) 2007 Jakob Bornecrantz + * Copyright (c) 2008 Red Hat Inc. + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * Copyright (c) 2007-2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _DRM_MODE_H +#define _DRM_MODE_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) /* deprecated */ +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) /* deprecated */ +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +#define DRM_MODE_TYPE_ALL (DRM_MODE_TYPE_PREFERRED | \ + DRM_MODE_TYPE_USERDEF | \ + DRM_MODE_TYPE_DRIVER) + +/* Video mode flags */ +/* bit compatible with the xrandr RR_ definitions (bits 0-13) + * + * ABI warning: Existing userspace really expects + * the mode flags to match the xrandr definitions. Any + * changes that don't match the xrandr definitions will + * likely need a new client cap or some other mechanism + * to avoid breaking existing userspace. This includes + * allocating new flags in the previously unused bits! + */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) /* deprecated */ +#define DRM_MODE_FLAG_PIXMUX (1<<11) /* deprecated */ +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) + /* + * When adding a new stereo mode don't forget to adjust DRM_MODE_FLAGS_3D_MAX + * (define not exposed to user space). + */ +#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) +#define DRM_MODE_FLAG_3D_NONE (0<<14) +#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) +#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) +#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) +#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + +/* Picture aspect ratio options */ +#define DRM_MODE_PICTURE_ASPECT_NONE 0 +#define DRM_MODE_PICTURE_ASPECT_4_3 1 +#define DRM_MODE_PICTURE_ASPECT_16_9 2 +#define DRM_MODE_PICTURE_ASPECT_64_27 3 +#define DRM_MODE_PICTURE_ASPECT_256_135 4 + +/* Content type options */ +#define DRM_MODE_CONTENT_TYPE_NO_DATA 0 +#define DRM_MODE_CONTENT_TYPE_GRAPHICS 1 +#define DRM_MODE_CONTENT_TYPE_PHOTO 2 +#define DRM_MODE_CONTENT_TYPE_CINEMA 3 +#define DRM_MODE_CONTENT_TYPE_GAME 4 + +/* Aspect ratio flag bitmask (4 bits 22:19) */ +#define DRM_MODE_FLAG_PIC_AR_MASK (0x0F<<19) +#define DRM_MODE_FLAG_PIC_AR_NONE \ + (DRM_MODE_PICTURE_ASPECT_NONE<<19) +#define DRM_MODE_FLAG_PIC_AR_4_3 \ + (DRM_MODE_PICTURE_ASPECT_4_3<<19) +#define DRM_MODE_FLAG_PIC_AR_16_9 \ + (DRM_MODE_PICTURE_ASPECT_16_9<<19) +#define DRM_MODE_FLAG_PIC_AR_64_27 \ + (DRM_MODE_PICTURE_ASPECT_64_27<<19) +#define DRM_MODE_FLAG_PIC_AR_256_135 \ + (DRM_MODE_PICTURE_ASPECT_256_135<<19) + +#define DRM_MODE_FLAG_ALL (DRM_MODE_FLAG_PHSYNC | \ + DRM_MODE_FLAG_NHSYNC | \ + DRM_MODE_FLAG_PVSYNC | \ + DRM_MODE_FLAG_NVSYNC | \ + DRM_MODE_FLAG_INTERLACE | \ + DRM_MODE_FLAG_DBLSCAN | \ + DRM_MODE_FLAG_CSYNC | \ + DRM_MODE_FLAG_PCSYNC | \ + DRM_MODE_FLAG_NCSYNC | \ + DRM_MODE_FLAG_HSKEW | \ + DRM_MODE_FLAG_DBLCLK | \ + DRM_MODE_FLAG_CLKDIV2 | \ + DRM_MODE_FLAG_3D_MASK) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NONE 0 /* Unmodified timing (display or + software can still scale) */ +#define DRM_MODE_SCALE_FULLSCREEN 1 /* Full screen, ignore aspect */ +#define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ +#define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 +#define DRM_MODE_DITHERING_AUTO 2 + +/* Dirty info options */ +#define DRM_MODE_DIRTY_OFF 0 +#define DRM_MODE_DIRTY_ON 1 +#define DRM_MODE_DIRTY_ANNOTATE 2 + +/* Link Status options */ +#define DRM_MODE_LINK_STATUS_GOOD 0 +#define DRM_MODE_LINK_STATUS_BAD 1 + +/* + * DRM_MODE_ROTATE_ + * + * Signals that a drm plane is been rotated degrees in counter + * clockwise direction. + * + * This define is provided as a convenience, looking up the property id + * using the name->prop id lookup is the preferred method. + */ +#define DRM_MODE_ROTATE_0 (1<<0) +#define DRM_MODE_ROTATE_90 (1<<1) +#define DRM_MODE_ROTATE_180 (1<<2) +#define DRM_MODE_ROTATE_270 (1<<3) + +/* + * DRM_MODE_ROTATE_MASK + * + * Bitmask used to look for drm plane rotations. + */ +#define DRM_MODE_ROTATE_MASK (\ + DRM_MODE_ROTATE_0 | \ + DRM_MODE_ROTATE_90 | \ + DRM_MODE_ROTATE_180 | \ + DRM_MODE_ROTATE_270) + +/* + * DRM_MODE_REFLECT_ + * + * Signals that the contents of a drm plane is reflected along the axis, + * in the same way as mirroring. + * See kerneldoc chapter "Plane Composition Properties" for more details. + * + * This define is provided as a convenience, looking up the property id + * using the name->prop id lookup is the preferred method. + */ +#define DRM_MODE_REFLECT_X (1<<4) +#define DRM_MODE_REFLECT_Y (1<<5) + +/* + * DRM_MODE_REFLECT_MASK + * + * Bitmask used to look for drm plane reflections. + */ +#define DRM_MODE_REFLECT_MASK (\ + DRM_MODE_REFLECT_X | \ + DRM_MODE_REFLECT_Y) + +/* Content Protection Flags */ +#define DRM_MODE_CONTENT_PROTECTION_UNDESIRED 0 +#define DRM_MODE_CONTENT_PROTECTION_DESIRED 1 +#define DRM_MODE_CONTENT_PROTECTION_ENABLED 2 + +struct drm_mode_modeinfo { + __u32 clock; + __u16 hdisplay; + __u16 hsync_start; + __u16 hsync_end; + __u16 htotal; + __u16 hskew; + __u16 vdisplay; + __u16 vsync_start; + __u16 vsync_end; + __u16 vtotal; + __u16 vscan; + + __u32 vrefresh; + + __u32 flags; + __u32 type; + char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + __u64 fb_id_ptr; + __u64 crtc_id_ptr; + __u64 connector_id_ptr; + __u64 encoder_id_ptr; + __u32 count_fbs; + __u32 count_crtcs; + __u32 count_connectors; + __u32 count_encoders; + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; +}; + +struct drm_mode_crtc { + __u64 set_connectors_ptr; + __u32 count_connectors; + + __u32 crtc_id; /**< Id */ + __u32 fb_id; /**< Id of framebuffer */ + + __u32 x; /**< x Position on the framebuffer */ + __u32 y; /**< y Position on the framebuffer */ + + __u32 gamma_size; + __u32 mode_valid; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_PRESENT_TOP_FIELD (1<<0) +#define DRM_MODE_PRESENT_BOTTOM_FIELD (1<<1) + +/* Planes blend with or override other bits on the CRTC */ +struct drm_mode_set_plane { + __u32 plane_id; + __u32 crtc_id; + __u32 fb_id; /* fb object contains surface format type */ + __u32 flags; /* see above flags */ + + /* Signed dest location allows it to be partially off screen */ + __s32 crtc_x; + __s32 crtc_y; + __u32 crtc_w; + __u32 crtc_h; + + /* Source values are 16.16 fixed point */ + __u32 src_x; + __u32 src_y; + __u32 src_h; + __u32 src_w; +}; + +struct drm_mode_get_plane { + __u32 plane_id; + + __u32 crtc_id; + __u32 fb_id; + + __u32 possible_crtcs; + __u32 gamma_size; + + __u32 count_format_types; + __u64 format_type_ptr; +}; + +struct drm_mode_get_plane_res { + __u64 plane_id_ptr; + __u32 count_planes; +}; + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 +#define DRM_MODE_ENCODER_VIRTUAL 5 +#define DRM_MODE_ENCODER_DSI 6 +#define DRM_MODE_ENCODER_DPMST 7 +#define DRM_MODE_ENCODER_DPI 8 + +struct drm_mode_get_encoder { + __u32 encoder_id; + __u32 encoder_type; + + __u32 crtc_id; /**< Id of crtc */ + + __u32 possible_crtcs; + __u32 possible_clones; +}; + +/* This is for connectors with multiple signal types. */ +/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ +enum drm_mode_subconnector { + DRM_MODE_SUBCONNECTOR_Automatic = 0, + DRM_MODE_SUBCONNECTOR_Unknown = 0, + DRM_MODE_SUBCONNECTOR_DVID = 3, + DRM_MODE_SUBCONNECTOR_DVIA = 4, + DRM_MODE_SUBCONNECTOR_Composite = 5, + DRM_MODE_SUBCONNECTOR_SVIDEO = 6, + DRM_MODE_SUBCONNECTOR_Component = 8, + DRM_MODE_SUBCONNECTOR_SCART = 9, +}; + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 +#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_eDP 14 +#define DRM_MODE_CONNECTOR_VIRTUAL 15 +#define DRM_MODE_CONNECTOR_DSI 16 +#define DRM_MODE_CONNECTOR_DPI 17 +#define DRM_MODE_CONNECTOR_WRITEBACK 18 + +struct drm_mode_get_connector { + + __u64 encoders_ptr; + __u64 modes_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + + __u32 count_modes; + __u32 count_props; + __u32 count_encoders; + + __u32 encoder_id; /**< Current Encoder */ + __u32 connector_id; /**< Id */ + __u32 connector_type; + __u32 connector_type_id; + + __u32 connection; + __u32 mm_width; /**< width in millimeters */ + __u32 mm_height; /**< height in millimeters */ + __u32 subpixel; + + __u32 pad; +}; + +#define DRM_MODE_PROP_PENDING (1<<0) /* deprecated, do not use */ +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) +#define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */ + +/* non-extended types: legacy bitmask, one bit per type: */ +#define DRM_MODE_PROP_LEGACY_TYPE ( \ + DRM_MODE_PROP_RANGE | \ + DRM_MODE_PROP_ENUM | \ + DRM_MODE_PROP_BLOB | \ + DRM_MODE_PROP_BITMASK) + +/* extended-types: rather than continue to consume a bit per type, + * grab a chunk of the bits to use as integer type id. + */ +#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) +#define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) + +/* the PROP_ATOMIC flag is used to hide properties from userspace that + * is not aware of atomic properties. This is mostly to work around + * older userspace (DDX drivers) that read/write each prop they find, + * without being aware that this could be triggering a lengthy modeset. + */ +#define DRM_MODE_PROP_ATOMIC 0x80000000 + +struct drm_mode_property_enum { + __u64 value; + char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_mode_get_property { + __u64 values_ptr; /* values and blob lengths */ + __u64 enum_blob_ptr; /* enum and blob id ptrs */ + + __u32 prop_id; + __u32 flags; + char name[DRM_PROP_NAME_LEN]; + + __u32 count_values; + /* This is only used to count enum values, not blobs. The _blobs is + * simply because of a historical reason, i.e. backwards compat. */ + __u32 count_enum_blobs; +}; + +struct drm_mode_connector_set_property { + __u64 value; + __u32 prop_id; + __u32 connector_id; +}; + +#define DRM_MODE_OBJECT_CRTC 0xcccccccc +#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 +#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 +#define DRM_MODE_OBJECT_MODE 0xdededede +#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 +#define DRM_MODE_OBJECT_FB 0xfbfbfbfb +#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb +#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee +#define DRM_MODE_OBJECT_ANY 0 + +struct drm_mode_obj_get_properties { + __u64 props_ptr; + __u64 prop_values_ptr; + __u32 count_props; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_obj_set_property { + __u64 value; + __u32 prop_id; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_get_blob { + __u32 blob_id; + __u32 length; + __u64 data; +}; + +struct drm_mode_fb_cmd { + __u32 fb_id; + __u32 width; + __u32 height; + __u32 pitch; + __u32 bpp; + __u32 depth; + /* driver specific handle */ + __u32 handle; +}; + +#define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ +#define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[] */ + +struct drm_mode_fb_cmd2 { + __u32 fb_id; + __u32 width; + __u32 height; + __u32 pixel_format; /* fourcc code from drm_fourcc.h */ + __u32 flags; /* see above flags */ + + /* + * In case of planar formats, this ioctl allows up to 4 + * buffer objects with offsets and pitches per plane. + * The pitch and offset order is dictated by the fourcc, + * e.g. NV12 (http://fourcc.org/yuv.php#NV12) is described as: + * + * YUV 4:2:0 image with a plane of 8 bit Y samples + * followed by an interleaved U/V plane containing + * 8 bit 2x2 subsampled colour difference samples. + * + * So it would consist of Y as offsets[0] and UV as + * offsets[1]. Note that offsets[0] will generally + * be 0 (but this is not required). + * + * To accommodate tiled, compressed, etc formats, a + * modifier can be specified. The default value of zero + * indicates "native" format as specified by the fourcc. + * Vendor specific modifier token. Note that even though + * it looks like we have a modifier per-plane, we in fact + * do not. The modifier for each plane must be identical. + * Thus all combinations of different data layouts for + * multi plane formats must be enumerated as separate + * modifiers. + */ + __u32 handles[4]; + __u32 pitches[4]; /* pitch for each plane */ + __u32 offsets[4]; /* offset of each plane */ + __u64 modifier[4]; /* ie, tiling, compress */ +}; + +#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 +#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02 +#define DRM_MODE_FB_DIRTY_FLAGS 0x03 + +#define DRM_MODE_FB_DIRTY_MAX_CLIPS 256 + +/* + * Mark a region of a framebuffer as dirty. + * + * Some hardware does not automatically update display contents + * as a hardware or software draw to a framebuffer. This ioctl + * allows userspace to tell the kernel and the hardware what + * regions of the framebuffer have changed. + * + * The kernel or hardware is free to update more then just the + * region specified by the clip rects. The kernel or hardware + * may also delay and/or coalesce several calls to dirty into a + * single update. + * + * Userspace may annotate the updates, the annotates are a + * promise made by the caller that the change is either a copy + * of pixels or a fill of a single color in the region specified. + * + * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then + * the number of updated regions are half of num_clips given, + * where the clip rects are paired in src and dst. The width and + * height of each one of the pairs must match. + * + * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller + * promises that the region specified of the clip rects is filled + * completely with a single color as given in the color argument. + */ + +struct drm_mode_fb_dirty_cmd { + __u32 fb_id; + __u32 flags; + __u32 color; + __u32 num_clips; + __u64 clips_ptr; +}; + +struct drm_mode_mode_cmd { + __u32 connector_id; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_CURSOR_BO 0x01 +#define DRM_MODE_CURSOR_MOVE 0x02 +#define DRM_MODE_CURSOR_FLAGS 0x03 + +/* + * depending on the value in flags different members are used. + * + * CURSOR_BO uses + * crtc_id + * width + * height + * handle - if 0 turns the cursor off + * + * CURSOR_MOVE uses + * crtc_id + * x + * y + */ +struct drm_mode_cursor { + __u32 flags; + __u32 crtc_id; + __s32 x; + __s32 y; + __u32 width; + __u32 height; + /* driver specific handle */ + __u32 handle; +}; + +struct drm_mode_cursor2 { + __u32 flags; + __u32 crtc_id; + __s32 x; + __s32 y; + __u32 width; + __u32 height; + /* driver specific handle */ + __u32 handle; + __s32 hot_x; + __s32 hot_y; +}; + +struct drm_mode_crtc_lut { + __u32 crtc_id; + __u32 gamma_size; + + /* pointers to arrays */ + __u64 red; + __u64 green; + __u64 blue; +}; + +struct drm_color_ctm { + /* + * Conversion matrix in S31.32 sign-magnitude + * (not two's complement!) format. + */ + __u64 matrix[9]; +}; + +struct drm_color_lut { + /* + * Values are mapped linearly to 0.0 - 1.0 range, with 0x0 == 0.0 and + * 0xffff == 1.0. + */ + __u16 red; + __u16 green; + __u16 blue; + __u16 reserved; +}; + +#define DRM_MODE_PAGE_FLIP_EVENT 0x01 +#define DRM_MODE_PAGE_FLIP_ASYNC 0x02 +#define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4 +#define DRM_MODE_PAGE_FLIP_TARGET_RELATIVE 0x8 +#define DRM_MODE_PAGE_FLIP_TARGET (DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE | \ + DRM_MODE_PAGE_FLIP_TARGET_RELATIVE) +#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT | \ + DRM_MODE_PAGE_FLIP_ASYNC | \ + DRM_MODE_PAGE_FLIP_TARGET) + +/* + * Request a page flip on the specified crtc. + * + * This ioctl will ask KMS to schedule a page flip for the specified + * crtc. Once any pending rendering targeting the specified fb (as of + * ioctl time) has completed, the crtc will be reprogrammed to display + * that fb after the next vertical refresh. The ioctl returns + * immediately, but subsequent rendering to the current fb will block + * in the execbuffer ioctl until the page flip happens. If a page + * flip is already pending as the ioctl is called, EBUSY will be + * returned. + * + * Flag DRM_MODE_PAGE_FLIP_EVENT requests that drm sends back a vblank + * event (see drm.h: struct drm_event_vblank) when the page flip is + * done. The user_data field passed in with this ioctl will be + * returned as the user_data field in the vblank event struct. + * + * Flag DRM_MODE_PAGE_FLIP_ASYNC requests that the flip happen + * 'as soon as possible', meaning that it not delay waiting for vblank. + * This may cause tearing on the screen. + * + * The reserved field must be zero. + */ + +struct drm_mode_crtc_page_flip { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 reserved; + __u64 user_data; +}; + +/* + * Request a page flip on the specified crtc. + * + * Same as struct drm_mode_crtc_page_flip, but supports new flags and + * re-purposes the reserved field: + * + * The sequence field must be zero unless either of the + * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is specified. When + * the ABSOLUTE flag is specified, the sequence field denotes the absolute + * vblank sequence when the flip should take effect. When the RELATIVE + * flag is specified, the sequence field denotes the relative (to the + * current one when the ioctl is called) vblank sequence when the flip + * should take effect. NOTE: DRM_IOCTL_WAIT_VBLANK must still be used to + * make sure the vblank sequence before the target one has passed before + * calling this ioctl. The purpose of the + * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is merely to clarify + * the target for when code dealing with a page flip runs during a + * vertical blank period. + */ + +struct drm_mode_crtc_page_flip_target { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 sequence; + __u64 user_data; +}; + +/* create a dumb scanout buffer */ +struct drm_mode_create_dumb { + __u32 height; + __u32 width; + __u32 bpp; + __u32 flags; + /* handle, pitch, size will be returned */ + __u32 handle; + __u32 pitch; + __u64 size; +}; + +/* set up for mmap of a dumb scanout buffer */ +struct drm_mode_map_dumb { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_mode_destroy_dumb { + __u32 handle; +}; + +/* page-flip flags are valid, plus: */ +#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 +#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 + +#define DRM_MODE_ATOMIC_FLAGS (\ + DRM_MODE_PAGE_FLIP_EVENT |\ + DRM_MODE_PAGE_FLIP_ASYNC |\ + DRM_MODE_ATOMIC_TEST_ONLY |\ + DRM_MODE_ATOMIC_NONBLOCK |\ + DRM_MODE_ATOMIC_ALLOW_MODESET) + +struct drm_mode_atomic { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 reserved; + __u64 user_data; +}; + +struct drm_format_modifier_blob { +#define FORMAT_BLOB_CURRENT 1 + /* Version of this blob format */ + __u32 version; + + /* Flags */ + __u32 flags; + + /* Number of fourcc formats supported */ + __u32 count_formats; + + /* Where in this blob the formats exist (in bytes) */ + __u32 formats_offset; + + /* Number of drm_format_modifiers */ + __u32 count_modifiers; + + /* Where in this blob the modifiers exist (in bytes) */ + __u32 modifiers_offset; + + /* __u32 formats[] */ + /* struct drm_format_modifier modifiers[] */ +}; + +struct drm_format_modifier { + /* Bitmask of formats in get_plane format list this info applies to. The + * offset allows a sliding window of which 64 formats (bits). + * + * Some examples: + * In today's world with < 65 formats, and formats 0, and 2 are + * supported + * 0x0000000000000005 + * ^-offset = 0, formats = 5 + * + * If the number formats grew to 128, and formats 98-102 are + * supported with the modifier: + * + * 0x0000007c00000000 0000000000000000 + * ^ + * |__offset = 64, formats = 0x7c00000000 + * + */ + __u64 formats; + __u32 offset; + __u32 pad; + + /* The modifier that applies to the >get_plane format list bitmask. */ + __u64 modifier; +}; + +/** + * Create a new 'blob' data property, copying length bytes from data pointer, + * and returning new blob ID. + */ +struct drm_mode_create_blob { + /** Pointer to data to copy. */ + __u64 data; + /** Length of data to copy. */ + __u32 length; + /** Return: new property ID. */ + __u32 blob_id; +}; + +/** + * Destroy a user-created blob property. + */ +struct drm_mode_destroy_blob { + __u32 blob_id; +}; + +/** + * Lease mode resources, creating another drm_master. + */ +struct drm_mode_create_lease { + /** Pointer to array of object ids (__u32) */ + __u64 object_ids; + /** Number of object ids */ + __u32 object_count; + /** flags for new FD (O_CLOEXEC, etc) */ + __u32 flags; + + /** Return: unique identifier for lessee. */ + __u32 lessee_id; + /** Return: file descriptor to new drm_master file */ + __u32 fd; +}; + +/** + * List lesses from a drm_master + */ +struct drm_mode_list_lessees { + /** Number of lessees. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_lessees; + __u32 pad; + + /** Pointer to lessees. + * pointer to __u64 array of lessee ids + */ + __u64 lessees_ptr; +}; + +/** + * Get leased objects + */ +struct drm_mode_get_lease { + /** Number of leased objects. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_objects; + __u32 pad; + + /** Pointer to objects. + * pointer to __u32 array of object ids + */ + __u64 objects_ptr; +}; + +/** + * Revoke lease + */ +struct drm_mode_revoke_lease { + /** Unique ID of lessee + */ + __u32 lessee_id; +}; + +/** + * struct drm_mode_rect - Two dimensional rectangle. + * @x1: Horizontal starting coordinate (inclusive). + * @y1: Vertical starting coordinate (inclusive). + * @x2: Horizontal ending coordinate (exclusive). + * @y2: Vertical ending coordinate (exclusive). + * + * With drm subsystem using struct drm_rect to manage rectangular area this + * export it to user-space. + * + * Currently used by drm_mode_atomic blob property FB_DAMAGE_CLIPS. + */ +struct drm_mode_rect { + __s32 x1; + __s32 y1; + __s32 x2; + __s32 y2; +}; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_sarea.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_sarea.h new file mode 100644 index 0000000..93025be --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_sarea.h @@ -0,0 +1,92 @@ +/** + * \file drm_sarea.h + * \brief SAREA definitions + * + * \author Michel Dänzer + */ + +/* + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_SAREA_H_ +#define _DRM_SAREA_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* SAREA area needs to be at least a page */ +#if defined(__alpha__) +#define SAREA_MAX 0x2000U +#elif defined(__mips__) +#define SAREA_MAX 0x4000U +#elif defined(__ia64__) +#define SAREA_MAX 0x10000U /* 64kB */ +#else +/* Intel 830M driver needs at least 8k SAREA */ +#define SAREA_MAX 0x2000U +#endif + +/** Maximum number of drawables in the SAREA */ +#define SAREA_MAX_DRAWABLES 256 + +#define SAREA_DRAWABLE_CLAIMED_ENTRY 0x80000000 + +/** SAREA drawable */ +struct drm_sarea_drawable { + unsigned int stamp; + unsigned int flags; +}; + +/** SAREA frame */ +struct drm_sarea_frame { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int fullscreen; +}; + +/** SAREA */ +struct drm_sarea { + /** first thing is always the DRM locking structure */ + struct drm_hw_lock lock; + /** \todo Use readers/writer lock for drm_sarea::drawable_lock */ + struct drm_hw_lock drawable_lock; + struct drm_sarea_drawable drawableTable[SAREA_MAX_DRAWABLES]; /**< drawables */ + struct drm_sarea_frame frame; /**< frame */ + drm_context_t dummy_context; +}; + +typedef struct drm_sarea_drawable drm_sarea_drawable_t; +typedef struct drm_sarea_frame drm_sarea_frame_t; +typedef struct drm_sarea drm_sarea_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* _DRM_SAREA_H_ */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/i915_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/i915_drm.h new file mode 100644 index 0000000..72afd94 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/i915_drm.h @@ -0,0 +1,1915 @@ +/* + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _I915_DRM_H_ +#define _I915_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + */ + +/** + * DOC: uevents generated by i915 on it's device node + * + * I915_L3_PARITY_UEVENT - Generated when the driver receives a parity mismatch + * event from the gpu l3 cache. Additional information supplied is ROW, + * BANK, SUBBANK, SLICE of the affected cacheline. Userspace should keep + * track of these events and if a specific cache-line seems to have a + * persistent error remap it with the l3 remapping tool supplied in + * intel-gpu-tools. The value supplied with the event is always 1. + * + * I915_ERROR_UEVENT - Generated upon error detection, currently only via + * hangcheck. The error detection event is a good indicator of when things + * began to go badly. The value supplied with the event is a 1 upon error + * detection, and a 0 upon reset completion, signifying no more error + * exists. NOTE: Disabling hangcheck or reset via module parameter will + * cause the related events to not be seen. + * + * I915_RESET_UEVENT - Event is generated just before an attempt to reset the + * the GPU. The value supplied with the event is always 1. NOTE: Disable + * reset via module parameter will cause this event to not be seen. + */ +#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR" +#define I915_ERROR_UEVENT "ERROR" +#define I915_RESET_UEVENT "RESET" + +/* + * i915_user_extension: Base class for defining a chain of extensions + * + * Many interfaces need to grow over time. In most cases we can simply + * extend the struct and have userspace pass in more data. Another option, + * as demonstrated by Vulkan's approach to providing extensions for forward + * and backward compatibility, is to use a list of optional structs to + * provide those extra details. + * + * The key advantage to using an extension chain is that it allows us to + * redefine the interface more easily than an ever growing struct of + * increasing complexity, and for large parts of that interface to be + * entirely optional. The downside is more pointer chasing; chasing across + * the boundary with pointers encapsulated inside u64. + */ +struct i915_user_extension { + __u64 next_extension; + __u32 name; + __u32 flags; /* All undefined bits must be zero. */ + __u32 rsvd[4]; /* Reserved for future use; must be zero. */ +}; + +/* + * MOCS indexes used for GPU surfaces, defining the cacheability of the + * surface data and the coherency for this data wrt. CPU vs. GPU accesses. + */ +enum i915_mocs_table_index { + /* + * Not cached anywhere, coherency between CPU and GPU accesses is + * guaranteed. + */ + I915_MOCS_UNCACHED, + /* + * Cacheability and coherency controlled by the kernel automatically + * based on the DRM_I915_GEM_SET_CACHING IOCTL setting and the current + * usage of the surface (used for display scanout or not). + */ + I915_MOCS_PTE, + /* + * Cached in all GPU caches available on the platform. + * Coherency between CPU and GPU accesses to the surface is not + * guaranteed without extra synchronization. + */ + I915_MOCS_CACHED, +}; + +/* + * Different engines serve different roles, and there may be more than one + * engine serving each role. enum drm_i915_gem_engine_class provides a + * classification of the role of the engine, which may be used when requesting + * operations to be performed on a certain subset of engines, or for providing + * information about that group. + */ +enum drm_i915_gem_engine_class { + I915_ENGINE_CLASS_RENDER = 0, + I915_ENGINE_CLASS_COPY = 1, + I915_ENGINE_CLASS_VIDEO = 2, + I915_ENGINE_CLASS_VIDEO_ENHANCE = 3, + + /* should be kept compact */ + + I915_ENGINE_CLASS_INVALID = -1 +}; + +/** + * DOC: perf_events exposed by i915 through /sys/bus/event_sources/drivers/i915 + * + */ + +enum drm_i915_pmu_engine_sample { + I915_SAMPLE_BUSY = 0, + I915_SAMPLE_WAIT = 1, + I915_SAMPLE_SEMA = 2 +}; + +#define I915_PMU_SAMPLE_BITS (4) +#define I915_PMU_SAMPLE_MASK (0xf) +#define I915_PMU_SAMPLE_INSTANCE_BITS (8) +#define I915_PMU_CLASS_SHIFT \ + (I915_PMU_SAMPLE_BITS + I915_PMU_SAMPLE_INSTANCE_BITS) + +#define __I915_PMU_ENGINE(class, instance, sample) \ + ((class) << I915_PMU_CLASS_SHIFT | \ + (instance) << I915_PMU_SAMPLE_BITS | \ + (sample)) + +#define I915_PMU_ENGINE_BUSY(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_BUSY) + +#define I915_PMU_ENGINE_WAIT(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_WAIT) + +#define I915_PMU_ENGINE_SEMA(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_SEMA) + +#define __I915_PMU_OTHER(x) (__I915_PMU_ENGINE(0xff, 0xff, 0xf) + 1 + (x)) + +#define I915_PMU_ACTUAL_FREQUENCY __I915_PMU_OTHER(0) +#define I915_PMU_REQUESTED_FREQUENCY __I915_PMU_OTHER(1) +#define I915_PMU_INTERRUPTS __I915_PMU_OTHER(2) +#define I915_PMU_RC6_RESIDENCY __I915_PMU_OTHER(3) + +#define I915_PMU_LAST I915_PMU_RC6_RESIDENCY + +/* Each region is a minimum of 16k, and there are at most 255 of them. + */ +#define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use + * of chars for next/prev indices */ +#define I915_LOG_MIN_TEX_REGION_SIZE 14 + +typedef struct _drm_i915_init { + enum { + I915_INIT_DMA = 0x01, + I915_CLEANUP_DMA = 0x02, + I915_RESUME_DMA = 0x03 + } func; + unsigned int mmio_offset; + int sarea_priv_offset; + unsigned int ring_start; + unsigned int ring_end; + unsigned int ring_size; + unsigned int front_offset; + unsigned int back_offset; + unsigned int depth_offset; + unsigned int w; + unsigned int h; + unsigned int pitch; + unsigned int pitch_bits; + unsigned int back_pitch; + unsigned int depth_pitch; + unsigned int cpp; + unsigned int chipset; +} drm_i915_init_t; + +typedef struct _drm_i915_sarea { + struct drm_tex_region texList[I915_NR_TEX_REGIONS + 1]; + int last_upload; /* last time texture was uploaded */ + int last_enqueue; /* last time a buffer was enqueued */ + int last_dispatch; /* age of the most recently dispatched buffer */ + int ctxOwner; /* last context to upload state */ + int texAge; + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ + int perf_boxes; /* performance boxes to be displayed */ + int width, height; /* screen size in pixels */ + + drm_handle_t front_handle; + int front_offset; + int front_size; + + drm_handle_t back_handle; + int back_offset; + int back_size; + + drm_handle_t depth_handle; + int depth_offset; + int depth_size; + + drm_handle_t tex_handle; + int tex_offset; + int tex_size; + int log_tex_granularity; + int pitch; + int rotation; /* 0, 90, 180 or 270 */ + int rotated_offset; + int rotated_size; + int rotated_pitch; + int virtualX, virtualY; + + unsigned int front_tiled; + unsigned int back_tiled; + unsigned int depth_tiled; + unsigned int rotated_tiled; + unsigned int rotated2_tiled; + + int pipeA_x; + int pipeA_y; + int pipeA_w; + int pipeA_h; + int pipeB_x; + int pipeB_y; + int pipeB_w; + int pipeB_h; + + /* fill out some space for old userspace triple buffer */ + drm_handle_t unused_handle; + __u32 unused1, unused2, unused3; + + /* buffer object handles for static buffers. May change + * over the lifetime of the client. + */ + __u32 front_bo_handle; + __u32 back_bo_handle; + __u32 unused_bo_handle; + __u32 depth_bo_handle; + +} drm_i915_sarea_t; + +/* due to userspace building against these headers we need some compat here */ +#define planeA_x pipeA_x +#define planeA_y pipeA_y +#define planeA_w pipeA_w +#define planeA_h pipeA_h +#define planeB_x pipeB_x +#define planeB_y pipeB_y +#define planeB_w pipeB_w +#define planeB_h pipeB_h + +/* Flags for perf_boxes + */ +#define I915_BOX_RING_EMPTY 0x1 +#define I915_BOX_FLIP 0x2 +#define I915_BOX_WAIT 0x4 +#define I915_BOX_TEXTURE_LOAD 0x8 +#define I915_BOX_LOST_CONTEXT 0x10 + +/* + * i915 specific ioctls. + * + * The device specific ioctl range is [DRM_COMMAND_BASE, DRM_COMMAND_END) ie + * [0x40, 0xa0) (a0 is excluded). The numbers below are defined as offset + * against DRM_COMMAND_BASE and should be between [0x0, 0x60). + */ +#define DRM_I915_INIT 0x00 +#define DRM_I915_FLUSH 0x01 +#define DRM_I915_FLIP 0x02 +#define DRM_I915_BATCHBUFFER 0x03 +#define DRM_I915_IRQ_EMIT 0x04 +#define DRM_I915_IRQ_WAIT 0x05 +#define DRM_I915_GETPARAM 0x06 +#define DRM_I915_SETPARAM 0x07 +#define DRM_I915_ALLOC 0x08 +#define DRM_I915_FREE 0x09 +#define DRM_I915_INIT_HEAP 0x0a +#define DRM_I915_CMDBUFFER 0x0b +#define DRM_I915_DESTROY_HEAP 0x0c +#define DRM_I915_SET_VBLANK_PIPE 0x0d +#define DRM_I915_GET_VBLANK_PIPE 0x0e +#define DRM_I915_VBLANK_SWAP 0x0f +#define DRM_I915_HWS_ADDR 0x11 +#define DRM_I915_GEM_INIT 0x13 +#define DRM_I915_GEM_EXECBUFFER 0x14 +#define DRM_I915_GEM_PIN 0x15 +#define DRM_I915_GEM_UNPIN 0x16 +#define DRM_I915_GEM_BUSY 0x17 +#define DRM_I915_GEM_THROTTLE 0x18 +#define DRM_I915_GEM_ENTERVT 0x19 +#define DRM_I915_GEM_LEAVEVT 0x1a +#define DRM_I915_GEM_CREATE 0x1b +#define DRM_I915_GEM_PREAD 0x1c +#define DRM_I915_GEM_PWRITE 0x1d +#define DRM_I915_GEM_MMAP 0x1e +#define DRM_I915_GEM_SET_DOMAIN 0x1f +#define DRM_I915_GEM_SW_FINISH 0x20 +#define DRM_I915_GEM_SET_TILING 0x21 +#define DRM_I915_GEM_GET_TILING 0x22 +#define DRM_I915_GEM_GET_APERTURE 0x23 +#define DRM_I915_GEM_MMAP_GTT 0x24 +#define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25 +#define DRM_I915_GEM_MADVISE 0x26 +#define DRM_I915_OVERLAY_PUT_IMAGE 0x27 +#define DRM_I915_OVERLAY_ATTRS 0x28 +#define DRM_I915_GEM_EXECBUFFER2 0x29 +#define DRM_I915_GEM_EXECBUFFER2_WR DRM_I915_GEM_EXECBUFFER2 +#define DRM_I915_GET_SPRITE_COLORKEY 0x2a +#define DRM_I915_SET_SPRITE_COLORKEY 0x2b +#define DRM_I915_GEM_WAIT 0x2c +#define DRM_I915_GEM_CONTEXT_CREATE 0x2d +#define DRM_I915_GEM_CONTEXT_DESTROY 0x2e +#define DRM_I915_GEM_SET_CACHING 0x2f +#define DRM_I915_GEM_GET_CACHING 0x30 +#define DRM_I915_REG_READ 0x31 +#define DRM_I915_GET_RESET_STATS 0x32 +#define DRM_I915_GEM_USERPTR 0x33 +#define DRM_I915_GEM_CONTEXT_GETPARAM 0x34 +#define DRM_I915_GEM_CONTEXT_SETPARAM 0x35 +#define DRM_I915_PERF_OPEN 0x36 +#define DRM_I915_PERF_ADD_CONFIG 0x37 +#define DRM_I915_PERF_REMOVE_CONFIG 0x38 +#define DRM_I915_QUERY 0x39 +/* Must be kept compact -- no holes */ + +#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) +#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) +#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP) +#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t) +#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t) +#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t) +#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, drm_i915_getparam_t) +#define DRM_IOCTL_I915_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SETPARAM, drm_i915_setparam_t) +#define DRM_IOCTL_I915_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_ALLOC, drm_i915_mem_alloc_t) +#define DRM_IOCTL_I915_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FREE, drm_i915_mem_free_t) +#define DRM_IOCTL_I915_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT_HEAP, drm_i915_mem_init_heap_t) +#define DRM_IOCTL_I915_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_CMDBUFFER, drm_i915_cmdbuffer_t) +#define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) +#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) +#define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2_WR DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2_WR, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) +#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) +#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) +#define DRM_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_CACHING, struct drm_i915_gem_caching) +#define DRM_IOCTL_I915_GEM_GET_CACHING DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GET_CACHING, struct drm_i915_gem_caching) +#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) +#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT) +#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT) +#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) +#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) +#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) +#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) +#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt) +#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) +#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish) +#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) +#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) +#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) +#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id) +#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise) +#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image) +#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) +#define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create_ext) +#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) +#define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read) +#define DRM_IOCTL_I915_GET_RESET_STATS DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats) +#define DRM_IOCTL_I915_GEM_USERPTR DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr) +#define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_PERF_OPEN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_OPEN, struct drm_i915_perf_open_param) +#define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) +#define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) +#define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) + +/* Allow drivers to submit batchbuffers directly to hardware, relying + * on the security mechanisms provided by hardware. + */ +typedef struct drm_i915_batchbuffer { + int start; /* agp offset */ + int used; /* nr bytes in use */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect *cliprects; /* pointer to userspace cliprects */ +} drm_i915_batchbuffer_t; + +/* As above, but pass a pointer to userspace buffer which can be + * validated by the kernel prior to sending to hardware. + */ +typedef struct _drm_i915_cmdbuffer { + char *buf; /* pointer to userspace command buffer */ + int sz; /* nr bytes in buf */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect *cliprects; /* pointer to userspace cliprects */ +} drm_i915_cmdbuffer_t; + +/* Userspace can request & wait on irq's: + */ +typedef struct drm_i915_irq_emit { + int *irq_seq; +} drm_i915_irq_emit_t; + +typedef struct drm_i915_irq_wait { + int irq_seq; +} drm_i915_irq_wait_t; + +/* + * Different modes of per-process Graphics Translation Table, + * see I915_PARAM_HAS_ALIASING_PPGTT + */ +#define I915_GEM_PPGTT_NONE 0 +#define I915_GEM_PPGTT_ALIASING 1 +#define I915_GEM_PPGTT_FULL 2 + +/* Ioctl to query kernel params: + */ +#define I915_PARAM_IRQ_ACTIVE 1 +#define I915_PARAM_ALLOW_BATCHBUFFER 2 +#define I915_PARAM_LAST_DISPATCH 3 +#define I915_PARAM_CHIPSET_ID 4 +#define I915_PARAM_HAS_GEM 5 +#define I915_PARAM_NUM_FENCES_AVAIL 6 +#define I915_PARAM_HAS_OVERLAY 7 +#define I915_PARAM_HAS_PAGEFLIPPING 8 +#define I915_PARAM_HAS_EXECBUF2 9 +#define I915_PARAM_HAS_BSD 10 +#define I915_PARAM_HAS_BLT 11 +#define I915_PARAM_HAS_RELAXED_FENCING 12 +#define I915_PARAM_HAS_COHERENT_RINGS 13 +#define I915_PARAM_HAS_EXEC_CONSTANTS 14 +#define I915_PARAM_HAS_RELAXED_DELTA 15 +#define I915_PARAM_HAS_GEN7_SOL_RESET 16 +#define I915_PARAM_HAS_LLC 17 +#define I915_PARAM_HAS_ALIASING_PPGTT 18 +#define I915_PARAM_HAS_WAIT_TIMEOUT 19 +#define I915_PARAM_HAS_SEMAPHORES 20 +#define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21 +#define I915_PARAM_HAS_VEBOX 22 +#define I915_PARAM_HAS_SECURE_BATCHES 23 +#define I915_PARAM_HAS_PINNED_BATCHES 24 +#define I915_PARAM_HAS_EXEC_NO_RELOC 25 +#define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 +#define I915_PARAM_HAS_WT 27 +#define I915_PARAM_CMD_PARSER_VERSION 28 +#define I915_PARAM_HAS_COHERENT_PHYS_GTT 29 +#define I915_PARAM_MMAP_VERSION 30 +#define I915_PARAM_HAS_BSD2 31 +#define I915_PARAM_REVISION 32 +#define I915_PARAM_SUBSLICE_TOTAL 33 +#define I915_PARAM_EU_TOTAL 34 +#define I915_PARAM_HAS_GPU_RESET 35 +#define I915_PARAM_HAS_RESOURCE_STREAMER 36 +#define I915_PARAM_HAS_EXEC_SOFTPIN 37 +#define I915_PARAM_HAS_POOLED_EU 38 +#define I915_PARAM_MIN_EU_IN_POOL 39 +#define I915_PARAM_MMAP_GTT_VERSION 40 + +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports user defined execution + * priorities and the driver will attempt to execute batches in priority order. + * The param returns a capability bitmask, nonzero implies that the scheduler + * is enabled, with different features present according to the mask. + * + * The initial priority for each batch is supplied by the context and is + * controlled via I915_CONTEXT_PARAM_PRIORITY. + */ +#define I915_PARAM_HAS_SCHEDULER 41 +#define I915_SCHEDULER_CAP_ENABLED (1ul << 0) +#define I915_SCHEDULER_CAP_PRIORITY (1ul << 1) +#define I915_SCHEDULER_CAP_PREEMPTION (1ul << 2) +#define I915_SCHEDULER_CAP_SEMAPHORES (1ul << 3) + +#define I915_PARAM_HUC_STATUS 42 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to opt-out of + * synchronisation with implicit fencing on individual objects. + * See EXEC_OBJECT_ASYNC. + */ +#define I915_PARAM_HAS_EXEC_ASYNC 43 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports explicit fence support - + * both being able to pass in a sync_file fd to wait upon before executing, + * and being able to return a new sync_file fd that is signaled when the + * current request is complete. See I915_EXEC_FENCE_IN and I915_EXEC_FENCE_OUT. + */ +#define I915_PARAM_HAS_EXEC_FENCE 44 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to capture + * user specified buffers for post-mortem debugging of GPU hangs. See + * EXEC_OBJECT_CAPTURE. + */ +#define I915_PARAM_HAS_EXEC_CAPTURE 45 + +#define I915_PARAM_SLICE_MASK 46 + +/* Assuming it's uniform for each slice, this queries the mask of subslices + * per-slice for this system. + */ +#define I915_PARAM_SUBSLICE_MASK 47 + +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying the batch buffer + * as the first execobject as opposed to the last. See I915_EXEC_BATCH_FIRST. + */ +#define I915_PARAM_HAS_EXEC_BATCH_FIRST 48 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying an array of + * drm_i915_gem_exec_fence structures. See I915_EXEC_FENCE_ARRAY. + */ +#define I915_PARAM_HAS_EXEC_FENCE_ARRAY 49 + +/* + * Query whether every context (both per-file default and user created) is + * isolated (insofar as HW supports). If this parameter is not true, then + * freshly created contexts may inherit values from an existing context, + * rather than default HW values. If true, it also ensures (insofar as HW + * supports) that all state set by this context will not leak to any other + * context. + * + * As not every engine across every gen support contexts, the returned + * value reports the support of context isolation for individual engines by + * returning a bitmask of each engine class set to true if that class supports + * isolation. + */ +#define I915_PARAM_HAS_CONTEXT_ISOLATION 50 + +/* Frequency of the command streamer timestamps given by the *_TIMESTAMP + * registers. This used to be fixed per platform but from CNL onwards, this + * might vary depending on the parts. + */ +#define I915_PARAM_CS_TIMESTAMP_FREQUENCY 51 + +/* + * Once upon a time we supposed that writes through the GGTT would be + * immediately in physical memory (once flushed out of the CPU path). However, + * on a few different processors and chipsets, this is not necessarily the case + * as the writes appear to be buffered internally. Thus a read of the backing + * storage (physical memory) via a different path (with different physical tags + * to the indirect write via the GGTT) will see stale values from before + * the GGTT write. Inside the kernel, we can for the most part keep track of + * the different read/write domains in use (e.g. set-domain), but the assumption + * of coherency is baked into the ABI, hence reporting its true state in this + * parameter. + * + * Reports true when writes via mmap_gtt are immediately visible following an + * lfence to flush the WCB. + * + * Reports false when writes via mmap_gtt are indeterminately delayed in an in + * internal buffer and are _not_ immediately visible to third parties accessing + * directly via mmap_cpu/mmap_wc. Use of mmap_gtt as part of an IPC + * communications channel when reporting false is strongly disadvised. + */ +#define I915_PARAM_MMAP_GTT_COHERENT 52 + +/* Must be kept compact -- no holes and well documented */ + +typedef struct drm_i915_getparam { + __s32 param; + /* + * WARNING: Using pointers instead of fixed-size u64 means we need to write + * compat32 code. Don't repeat this mistake. + */ + int *value; +} drm_i915_getparam_t; + +/* Ioctl to set kernel params: + */ +#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1 +#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2 +#define I915_SETPARAM_ALLOW_BATCHBUFFER 3 +#define I915_SETPARAM_NUM_USED_FENCES 4 +/* Must be kept compact -- no holes */ + +typedef struct drm_i915_setparam { + int param; + int value; +} drm_i915_setparam_t; + +/* A memory manager for regions of shared memory: + */ +#define I915_MEM_REGION_AGP 1 + +typedef struct drm_i915_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or agp */ +} drm_i915_mem_alloc_t; + +typedef struct drm_i915_mem_free { + int region; + int region_offset; +} drm_i915_mem_free_t; + +typedef struct drm_i915_mem_init_heap { + int region; + int size; + int start; +} drm_i915_mem_init_heap_t; + +/* Allow memory manager to be torn down and re-initialized (eg on + * rotate): + */ +typedef struct drm_i915_mem_destroy_heap { + int region; +} drm_i915_mem_destroy_heap_t; + +/* Allow X server to configure which pipes to monitor for vblank signals + */ +#define DRM_I915_VBLANK_PIPE_A 1 +#define DRM_I915_VBLANK_PIPE_B 2 + +typedef struct drm_i915_vblank_pipe { + int pipe; +} drm_i915_vblank_pipe_t; + +/* Schedule buffer swap at given vertical blank: + */ +typedef struct drm_i915_vblank_swap { + drm_drawable_t drawable; + enum drm_vblank_seq_type seqtype; + unsigned int sequence; +} drm_i915_vblank_swap_t; + +typedef struct drm_i915_hws_addr { + __u64 addr; +} drm_i915_hws_addr_t; + +struct drm_i915_gem_init { + /** + * Beginning offset in the GTT to be managed by the DRM memory + * manager. + */ + __u64 gtt_start; + /** + * Ending offset in the GTT to be managed by the DRM memory + * manager. + */ + __u64 gtt_end; +}; + +struct drm_i915_gem_create { + /** + * Requested size for the object. + * + * The (page-aligned) allocated size for the object will be returned. + */ + __u64 size; + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; + __u32 pad; +}; + +struct drm_i915_gem_pread { + /** Handle for the object being read. */ + __u32 handle; + __u32 pad; + /** Offset into the object to read from */ + __u64 offset; + /** Length of data to read */ + __u64 size; + /** + * Pointer to write the data into. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 data_ptr; +}; + +struct drm_i915_gem_pwrite { + /** Handle for the object being written to. */ + __u32 handle; + __u32 pad; + /** Offset into the object to write to */ + __u64 offset; + /** Length of data to write */ + __u64 size; + /** + * Pointer to read the data from. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 data_ptr; +}; + +struct drm_i915_gem_mmap { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** Offset in the object to map. */ + __u64 offset; + /** + * Length of data to map. + * + * The value will be page-aligned. + */ + __u64 size; + /** + * Returned pointer the data was mapped at. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 addr_ptr; + + /** + * Flags for extended behaviour. + * + * Added in version 2. + */ + __u64 flags; +#define I915_MMAP_WC 0x1 +}; + +struct drm_i915_gem_mmap_gtt { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_i915_gem_set_domain { + /** Handle for the object */ + __u32 handle; + + /** New read domains */ + __u32 read_domains; + + /** New write domain */ + __u32 write_domain; +}; + +struct drm_i915_gem_sw_finish { + /** Handle for the object */ + __u32 handle; +}; + +struct drm_i915_gem_relocation_entry { + /** + * Handle of the buffer being pointed to by this relocation entry. + * + * It's appealing to make this be an index into the mm_validate_entry + * list to refer to the buffer, but this allows the driver to create + * a relocation list for state buffers and not re-write it per + * exec using the buffer. + */ + __u32 target_handle; + + /** + * Value to be added to the offset of the target buffer to make up + * the relocation entry. + */ + __u32 delta; + + /** Offset in the buffer the relocation entry will be written into */ + __u64 offset; + + /** + * Offset value of the target buffer that the relocation entry was last + * written as. + * + * If the buffer has the same offset as last time, we can skip syncing + * and writing the relocation. This value is written back out by + * the execbuffer ioctl when the relocation is written. + */ + __u64 presumed_offset; + + /** + * Target memory domains read by this operation. + */ + __u32 read_domains; + + /** + * Target memory domains written by this operation. + * + * Note that only one domain may be written by the whole + * execbuffer operation, so that where there are conflicts, + * the application will get -EINVAL back. + */ + __u32 write_domain; +}; + +/** @{ + * Intel memory domains + * + * Most of these just align with the various caches in + * the system and are used to flush and invalidate as + * objects end up cached in different domains. + */ +/** CPU cache */ +#define I915_GEM_DOMAIN_CPU 0x00000001 +/** Render cache, used by 2D and 3D drawing */ +#define I915_GEM_DOMAIN_RENDER 0x00000002 +/** Sampler cache, used by texture engine */ +#define I915_GEM_DOMAIN_SAMPLER 0x00000004 +/** Command queue, used to load batch buffers */ +#define I915_GEM_DOMAIN_COMMAND 0x00000008 +/** Instruction cache, used by shader programs */ +#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010 +/** Vertex address cache */ +#define I915_GEM_DOMAIN_VERTEX 0x00000020 +/** GTT domain - aperture and scanout */ +#define I915_GEM_DOMAIN_GTT 0x00000040 +/** WC domain - uncached access */ +#define I915_GEM_DOMAIN_WC 0x00000080 +/** @} */ + +struct drm_i915_gem_exec_object { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + __u32 handle; + + /** Number of relocations to be performed on this buffer */ + __u32 relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + __u64 relocs_ptr; + + /** Required alignment in graphics aperture */ + __u64 alignment; + + /** + * Returned value of the updated offset of the object, for future + * presumed_offset writes. + */ + __u64 offset; +}; + +struct drm_i915_gem_execbuffer { + /** + * List of buffers to be validated with their relocations to be + * performend on them. + * + * This is a pointer to an array of struct drm_i915_gem_validate_entry. + * + * These buffers must be listed in an order such that all relocations + * a buffer is performing refer to buffers that have already appeared + * in the validate list. + */ + __u64 buffers_ptr; + __u32 buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + __u32 batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + __u32 batch_len; + __u32 DR1; + __u32 DR4; + __u32 num_cliprects; + /** This is a struct drm_clip_rect *cliprects */ + __u64 cliprects_ptr; +}; + +struct drm_i915_gem_exec_object2 { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + __u32 handle; + + /** Number of relocations to be performed on this buffer */ + __u32 relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + __u64 relocs_ptr; + + /** Required alignment in graphics aperture */ + __u64 alignment; + + /** + * When the EXEC_OBJECT_PINNED flag is specified this is populated by + * the user with the GTT offset at which this object will be pinned. + * When the I915_EXEC_NO_RELOC flag is specified this must contain the + * presumed_offset of the object. + * During execbuffer2 the kernel populates it with the value of the + * current GTT offset of the object, for future presumed_offset writes. + */ + __u64 offset; + +#define EXEC_OBJECT_NEEDS_FENCE (1<<0) +#define EXEC_OBJECT_NEEDS_GTT (1<<1) +#define EXEC_OBJECT_WRITE (1<<2) +#define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3) +#define EXEC_OBJECT_PINNED (1<<4) +#define EXEC_OBJECT_PAD_TO_SIZE (1<<5) +/* The kernel implicitly tracks GPU activity on all GEM objects, and + * synchronises operations with outstanding rendering. This includes + * rendering on other devices if exported via dma-buf. However, sometimes + * this tracking is too coarse and the user knows better. For example, + * if the object is split into non-overlapping ranges shared between different + * clients or engines (i.e. suballocating objects), the implicit tracking + * by kernel assumes that each operation affects the whole object rather + * than an individual range, causing needless synchronisation between clients. + * The kernel will also forgo any CPU cache flushes prior to rendering from + * the object as the client is expected to be also handling such domain + * tracking. + * + * The kernel maintains the implicit tracking in order to manage resources + * used by the GPU - this flag only disables the synchronisation prior to + * rendering with this object in this execbuf. + * + * Opting out of implicit synhronisation requires the user to do its own + * explicit tracking to avoid rendering corruption. See, for example, + * I915_PARAM_HAS_EXEC_FENCE to order execbufs and execute them asynchronously. + */ +#define EXEC_OBJECT_ASYNC (1<<6) +/* Request that the contents of this execobject be copied into the error + * state upon a GPU hang involving this batch for post-mortem debugging. + * These buffers are recorded in no particular order as "user" in + * /sys/class/drm/cardN/error. Query I915_PARAM_HAS_EXEC_CAPTURE to see + * if the kernel supports this flag. + */ +#define EXEC_OBJECT_CAPTURE (1<<7) +/* All remaining bits are MBZ and RESERVED FOR FUTURE USE */ +#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_CAPTURE<<1) + __u64 flags; + + union { + __u64 rsvd1; + __u64 pad_to_size; + }; + __u64 rsvd2; +}; + +struct drm_i915_gem_exec_fence { + /** + * User's handle for a drm_syncobj to wait on or signal. + */ + __u32 handle; + +#define I915_EXEC_FENCE_WAIT (1<<0) +#define I915_EXEC_FENCE_SIGNAL (1<<1) +#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SIGNAL << 1)) + __u32 flags; +}; + +struct drm_i915_gem_execbuffer2 { + /** + * List of gem_exec_object2 structs + */ + __u64 buffers_ptr; + __u32 buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + __u32 batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + __u32 batch_len; + __u32 DR1; + __u32 DR4; + __u32 num_cliprects; + /** + * This is a struct drm_clip_rect *cliprects if I915_EXEC_FENCE_ARRAY + * is not set. If I915_EXEC_FENCE_ARRAY is set, then this is a + * struct drm_i915_gem_exec_fence *fences. + */ + __u64 cliprects_ptr; +#define I915_EXEC_RING_MASK (0x3f) +#define I915_EXEC_DEFAULT (0<<0) +#define I915_EXEC_RENDER (1<<0) +#define I915_EXEC_BSD (2<<0) +#define I915_EXEC_BLT (3<<0) +#define I915_EXEC_VEBOX (4<<0) + +/* Used for switching the constants addressing mode on gen4+ RENDER ring. + * Gen6+ only supports relative addressing to dynamic state (default) and + * absolute addressing. + * + * These flags are ignored for the BSD and BLT rings. + */ +#define I915_EXEC_CONSTANTS_MASK (3<<6) +#define I915_EXEC_CONSTANTS_REL_GENERAL (0<<6) /* default */ +#define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6) +#define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */ + __u64 flags; + __u64 rsvd1; /* now used for context info */ + __u64 rsvd2; +}; + +/** Resets the SO write offset registers for transform feedback on gen7. */ +#define I915_EXEC_GEN7_SOL_RESET (1<<8) + +/** Request a privileged ("secure") batch buffer. Note only available for + * DRM_ROOT_ONLY | DRM_MASTER processes. + */ +#define I915_EXEC_SECURE (1<<9) + +/** Inform the kernel that the batch is and will always be pinned. This + * negates the requirement for a workaround to be performed to avoid + * an incoherent CS (such as can be found on 830/845). If this flag is + * not passed, the kernel will endeavour to make sure the batch is + * coherent with the CS before execution. If this flag is passed, + * userspace assumes the responsibility for ensuring the same. + */ +#define I915_EXEC_IS_PINNED (1<<10) + +/** Provide a hint to the kernel that the command stream and auxiliary + * state buffers already holds the correct presumed addresses and so the + * relocation process may be skipped if no buffers need to be moved in + * preparation for the execbuffer. + */ +#define I915_EXEC_NO_RELOC (1<<11) + +/** Use the reloc.handle as an index into the exec object array rather + * than as the per-file handle. + */ +#define I915_EXEC_HANDLE_LUT (1<<12) + +/** Used for switching BSD rings on the platforms with two BSD rings */ +#define I915_EXEC_BSD_SHIFT (13) +#define I915_EXEC_BSD_MASK (3 << I915_EXEC_BSD_SHIFT) +/* default ping-pong mode */ +#define I915_EXEC_BSD_DEFAULT (0 << I915_EXEC_BSD_SHIFT) +#define I915_EXEC_BSD_RING1 (1 << I915_EXEC_BSD_SHIFT) +#define I915_EXEC_BSD_RING2 (2 << I915_EXEC_BSD_SHIFT) + +/** Tell the kernel that the batchbuffer is processed by + * the resource streamer. + */ +#define I915_EXEC_RESOURCE_STREAMER (1<<15) + +/* Setting I915_EXEC_FENCE_IN implies that lower_32_bits(rsvd2) represent + * a sync_file fd to wait upon (in a nonblocking manner) prior to executing + * the batch. + * + * Returns -EINVAL if the sync_file fd cannot be found. + */ +#define I915_EXEC_FENCE_IN (1<<16) + +/* Setting I915_EXEC_FENCE_OUT causes the ioctl to return a sync_file fd + * in the upper_32_bits(rsvd2) upon success. Ownership of the fd is given + * to the caller, and it should be close() after use. (The fd is a regular + * file descriptor and will be cleaned up on process termination. It holds + * a reference to the request, but nothing else.) + * + * The sync_file fd can be combined with other sync_file and passed either + * to execbuf using I915_EXEC_FENCE_IN, to atomic KMS ioctls (so that a flip + * will only occur after this request completes), or to other devices. + * + * Using I915_EXEC_FENCE_OUT requires use of + * DRM_IOCTL_I915_GEM_EXECBUFFER2_WR ioctl so that the result is written + * back to userspace. Failure to do so will cause the out-fence to always + * be reported as zero, and the real fence fd to be leaked. + */ +#define I915_EXEC_FENCE_OUT (1<<17) + +/* + * Traditionally the execbuf ioctl has only considered the final element in + * the execobject[] to be the executable batch. Often though, the client + * will known the batch object prior to construction and being able to place + * it into the execobject[] array first can simplify the relocation tracking. + * Setting I915_EXEC_BATCH_FIRST tells execbuf to use element 0 of the + * execobject[] as the * batch instead (the default is to use the last + * element). + */ +#define I915_EXEC_BATCH_FIRST (1<<18) + +/* Setting I915_FENCE_ARRAY implies that num_cliprects and cliprects_ptr + * define an array of i915_gem_exec_fence structures which specify a set of + * dma fences to wait upon or signal. + */ +#define I915_EXEC_FENCE_ARRAY (1<<19) + +#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1)) + +#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) +#define i915_execbuffer2_set_context_id(eb2, context) \ + (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK +#define i915_execbuffer2_get_context_id(eb2) \ + ((eb2).rsvd1 & I915_EXEC_CONTEXT_ID_MASK) + +struct drm_i915_gem_pin { + /** Handle of the buffer to be pinned. */ + __u32 handle; + __u32 pad; + + /** alignment required within the aperture */ + __u64 alignment; + + /** Returned GTT offset of the buffer. */ + __u64 offset; +}; + +struct drm_i915_gem_unpin { + /** Handle of the buffer to be unpinned. */ + __u32 handle; + __u32 pad; +}; + +struct drm_i915_gem_busy { + /** Handle of the buffer to check for busy */ + __u32 handle; + + /** Return busy status + * + * A return of 0 implies that the object is idle (after + * having flushed any pending activity), and a non-zero return that + * the object is still in-flight on the GPU. (The GPU has not yet + * signaled completion for all pending requests that reference the + * object.) An object is guaranteed to become idle eventually (so + * long as no new GPU commands are executed upon it). Due to the + * asynchronous nature of the hardware, an object reported + * as busy may become idle before the ioctl is completed. + * + * Furthermore, if the object is busy, which engine is busy is only + * provided as a guide and only indirectly by reporting its class + * (there may be more than one engine in each class). There are race + * conditions which prevent the report of which engines are busy from + * being always accurate. However, the converse is not true. If the + * object is idle, the result of the ioctl, that all engines are idle, + * is accurate. + * + * The returned dword is split into two fields to indicate both + * the engine classess on which the object is being read, and the + * engine class on which it is currently being written (if any). + * + * The low word (bits 0:15) indicate if the object is being written + * to by any engine (there can only be one, as the GEM implicit + * synchronisation rules force writes to be serialised). Only the + * engine class (offset by 1, I915_ENGINE_CLASS_RENDER is reported as + * 1 not 0 etc) for the last write is reported. + * + * The high word (bits 16:31) are a bitmask of which engines classes + * are currently reading from the object. Multiple engines may be + * reading from the object simultaneously. + * + * The value of each engine class is the same as specified in the + * I915_CONTEXT_SET_ENGINES parameter and via perf, i.e. + * I915_ENGINE_CLASS_RENDER, I915_ENGINE_CLASS_COPY, etc. + * reported as active itself. Some hardware may have parallel + * execution engines, e.g. multiple media engines, which are + * mapped to the same class identifier and so are not separately + * reported for busyness. + * + * Caveat emptor: + * Only the boolean result of this query is reliable; that is whether + * the object is idle or busy. The report of which engines are busy + * should be only used as a heuristic. + */ + __u32 busy; +}; + +/** + * I915_CACHING_NONE + * + * GPU access is not coherent with cpu caches. Default for machines without an + * LLC. + */ +#define I915_CACHING_NONE 0 +/** + * I915_CACHING_CACHED + * + * GPU access is coherent with cpu caches and furthermore the data is cached in + * last-level caches shared between cpu cores and the gpu GT. Default on + * machines with HAS_LLC. + */ +#define I915_CACHING_CACHED 1 +/** + * I915_CACHING_DISPLAY + * + * Special GPU caching mode which is coherent with the scanout engines. + * Transparently falls back to I915_CACHING_NONE on platforms where no special + * cache mode (like write-through or gfdt flushing) is available. The kernel + * automatically sets this mode when using a buffer as a scanout target. + * Userspace can manually set this mode to avoid a costly stall and clflush in + * the hotpath of drawing the first frame. + */ +#define I915_CACHING_DISPLAY 2 + +struct drm_i915_gem_caching { + /** + * Handle of the buffer to set/get the caching level of. */ + __u32 handle; + + /** + * Caching level to apply or return value + * + * bits0-15 are for generic caching control (i.e. the above defined + * values). bits16-31 are reserved for platform-specific variations + * (e.g. l3$ caching on gen7). */ + __u32 caching; +}; + +#define I915_TILING_NONE 0 +#define I915_TILING_X 1 +#define I915_TILING_Y 2 +#define I915_TILING_LAST I915_TILING_Y + +#define I915_BIT_6_SWIZZLE_NONE 0 +#define I915_BIT_6_SWIZZLE_9 1 +#define I915_BIT_6_SWIZZLE_9_10 2 +#define I915_BIT_6_SWIZZLE_9_11 3 +#define I915_BIT_6_SWIZZLE_9_10_11 4 +/* Not seen by userland */ +#define I915_BIT_6_SWIZZLE_UNKNOWN 5 +/* Seen by userland. */ +#define I915_BIT_6_SWIZZLE_9_17 6 +#define I915_BIT_6_SWIZZLE_9_10_17 7 + +struct drm_i915_gem_set_tiling { + /** Handle of the buffer to have its tiling state updated */ + __u32 handle; + + /** + * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + * + * This value is to be set on request, and will be updated by the + * kernel on successful return with the actual chosen tiling layout. + * + * The tiling mode may be demoted to I915_TILING_NONE when the system + * has bit 6 swizzling that can't be managed correctly by GEM. + * + * Buffer contents become undefined when changing tiling_mode. + */ + __u32 tiling_mode; + + /** + * Stride in bytes for the object when in I915_TILING_X or + * I915_TILING_Y. + */ + __u32 stride; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + __u32 swizzle_mode; +}; + +struct drm_i915_gem_get_tiling { + /** Handle of the buffer to get tiling state for. */ + __u32 handle; + + /** + * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + */ + __u32 tiling_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + __u32 swizzle_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping whilst bound. + */ + __u32 phys_swizzle_mode; +}; + +struct drm_i915_gem_get_aperture { + /** Total size of the aperture used by i915_gem_execbuffer, in bytes */ + __u64 aper_size; + + /** + * Available space in the aperture used by i915_gem_execbuffer, in + * bytes + */ + __u64 aper_available_size; +}; + +struct drm_i915_get_pipe_from_crtc_id { + /** ID of CRTC being requested **/ + __u32 crtc_id; + + /** pipe of requested CRTC **/ + __u32 pipe; +}; + +#define I915_MADV_WILLNEED 0 +#define I915_MADV_DONTNEED 1 +#define __I915_MADV_PURGED 2 /* internal state */ + +struct drm_i915_gem_madvise { + /** Handle of the buffer to change the backing store advice */ + __u32 handle; + + /* Advice: either the buffer will be needed again in the near future, + * or wont be and could be discarded under memory pressure. + */ + __u32 madv; + + /** Whether the backing store still exists. */ + __u32 retained; +}; + +/* flags */ +#define I915_OVERLAY_TYPE_MASK 0xff +#define I915_OVERLAY_YUV_PLANAR 0x01 +#define I915_OVERLAY_YUV_PACKED 0x02 +#define I915_OVERLAY_RGB 0x03 + +#define I915_OVERLAY_DEPTH_MASK 0xff00 +#define I915_OVERLAY_RGB24 0x1000 +#define I915_OVERLAY_RGB16 0x2000 +#define I915_OVERLAY_RGB15 0x3000 +#define I915_OVERLAY_YUV422 0x0100 +#define I915_OVERLAY_YUV411 0x0200 +#define I915_OVERLAY_YUV420 0x0300 +#define I915_OVERLAY_YUV410 0x0400 + +#define I915_OVERLAY_SWAP_MASK 0xff0000 +#define I915_OVERLAY_NO_SWAP 0x000000 +#define I915_OVERLAY_UV_SWAP 0x010000 +#define I915_OVERLAY_Y_SWAP 0x020000 +#define I915_OVERLAY_Y_AND_UV_SWAP 0x030000 + +#define I915_OVERLAY_FLAGS_MASK 0xff000000 +#define I915_OVERLAY_ENABLE 0x01000000 + +struct drm_intel_overlay_put_image { + /* various flags and src format description */ + __u32 flags; + /* source picture description */ + __u32 bo_handle; + /* stride values and offsets are in bytes, buffer relative */ + __u16 stride_Y; /* stride for packed formats */ + __u16 stride_UV; + __u32 offset_Y; /* offset for packet formats */ + __u32 offset_U; + __u32 offset_V; + /* in pixels */ + __u16 src_width; + __u16 src_height; + /* to compensate the scaling factors for partially covered surfaces */ + __u16 src_scan_width; + __u16 src_scan_height; + /* output crtc description */ + __u32 crtc_id; + __u16 dst_x; + __u16 dst_y; + __u16 dst_width; + __u16 dst_height; +}; + +/* flags */ +#define I915_OVERLAY_UPDATE_ATTRS (1<<0) +#define I915_OVERLAY_UPDATE_GAMMA (1<<1) +#define I915_OVERLAY_DISABLE_DEST_COLORKEY (1<<2) +struct drm_intel_overlay_attrs { + __u32 flags; + __u32 color_key; + __s32 brightness; + __u32 contrast; + __u32 saturation; + __u32 gamma0; + __u32 gamma1; + __u32 gamma2; + __u32 gamma3; + __u32 gamma4; + __u32 gamma5; +}; + +/* + * Intel sprite handling + * + * Color keying works with a min/mask/max tuple. Both source and destination + * color keying is allowed. + * + * Source keying: + * Sprite pixels within the min & max values, masked against the color channels + * specified in the mask field, will be transparent. All other pixels will + * be displayed on top of the primary plane. For RGB surfaces, only the min + * and mask fields will be used; ranged compares are not allowed. + * + * Destination keying: + * Primary plane pixels that match the min value, masked against the color + * channels specified in the mask field, will be replaced by corresponding + * pixels from the sprite plane. + * + * Note that source & destination keying are exclusive; only one can be + * active on a given plane. + */ + +#define I915_SET_COLORKEY_NONE (1<<0) /* Deprecated. Instead set + * flags==0 to disable colorkeying. + */ +#define I915_SET_COLORKEY_DESTINATION (1<<1) +#define I915_SET_COLORKEY_SOURCE (1<<2) +struct drm_intel_sprite_colorkey { + __u32 plane_id; + __u32 min_value; + __u32 channel_mask; + __u32 max_value; + __u32 flags; +}; + +struct drm_i915_gem_wait { + /** Handle of BO we shall wait on */ + __u32 bo_handle; + __u32 flags; + /** Number of nanoseconds to wait, Returns time remaining. */ + __s64 timeout_ns; +}; + +struct drm_i915_gem_context_create { + __u32 ctx_id; /* output: id of new context*/ + __u32 pad; +}; + +struct drm_i915_gem_context_create_ext { + __u32 ctx_id; /* output: id of new context*/ + __u32 flags; +#define I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS (1u << 0) +#define I915_CONTEXT_CREATE_FLAGS_UNKNOWN \ + (-(I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS << 1)) + __u64 extensions; +}; + +struct drm_i915_gem_context_param { + __u32 ctx_id; + __u32 size; + __u64 param; +#define I915_CONTEXT_PARAM_BAN_PERIOD 0x1 +#define I915_CONTEXT_PARAM_NO_ZEROMAP 0x2 +#define I915_CONTEXT_PARAM_GTT_SIZE 0x3 +#define I915_CONTEXT_PARAM_NO_ERROR_CAPTURE 0x4 +#define I915_CONTEXT_PARAM_BANNABLE 0x5 +#define I915_CONTEXT_PARAM_PRIORITY 0x6 +#define I915_CONTEXT_MAX_USER_PRIORITY 1023 /* inclusive */ +#define I915_CONTEXT_DEFAULT_PRIORITY 0 +#define I915_CONTEXT_MIN_USER_PRIORITY -1023 /* inclusive */ + /* + * When using the following param, value should be a pointer to + * drm_i915_gem_context_param_sseu. + */ +#define I915_CONTEXT_PARAM_SSEU 0x7 + +/* + * Not all clients may want to attempt automatic recover of a context after + * a hang (for example, some clients may only submit very small incremental + * batches relying on known logical state of previous batches which will never + * recover correctly and each attempt will hang), and so would prefer that + * the context is forever banned instead. + * + * If set to false (0), after a reset, subsequent (and in flight) rendering + * from this context is discarded, and the client will need to create a new + * context to use instead. + * + * If set to true (1), the kernel will automatically attempt to recover the + * context by skipping the hanging batch and executing the next batch starting + * from the default context state (discarding the incomplete logical context + * state lost due to the reset). + * + * On creation, all new contexts are marked as recoverable. + */ +#define I915_CONTEXT_PARAM_RECOVERABLE 0x8 +/* Must be kept compact -- no holes and well documented */ + + __u64 value; +}; + +/** + * Context SSEU programming + * + * It may be necessary for either functional or performance reason to configure + * a context to run with a reduced number of SSEU (where SSEU stands for Slice/ + * Sub-slice/EU). + * + * This is done by configuring SSEU configuration using the below + * @struct drm_i915_gem_context_param_sseu for every supported engine which + * userspace intends to use. + * + * Not all GPUs or engines support this functionality in which case an error + * code -ENODEV will be returned. + * + * Also, flexibility of possible SSEU configuration permutations varies between + * GPU generations and software imposed limitations. Requesting such a + * combination will return an error code of -EINVAL. + * + * NOTE: When perf/OA is active the context's SSEU configuration is ignored in + * favour of a single global setting. + */ +struct drm_i915_gem_context_param_sseu { + /* + * Engine class & instance to be configured or queried. + */ + __u16 engine_class; + __u16 engine_instance; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * Mask of slices to enable for the context. Valid values are a subset + * of the bitmask value returned for I915_PARAM_SLICE_MASK. + */ + __u64 slice_mask; + + /* + * Mask of subslices to enable for the context. Valid values are a + * subset of the bitmask value return by I915_PARAM_SUBSLICE_MASK. + */ + __u64 subslice_mask; + + /* + * Minimum/Maximum number of EUs to enable per subslice for the + * context. min_eus_per_subslice must be inferior or equal to + * max_eus_per_subslice. + */ + __u16 min_eus_per_subslice; + __u16 max_eus_per_subslice; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 rsvd; +}; + +struct drm_i915_gem_context_create_ext_setparam { +#define I915_CONTEXT_CREATE_EXT_SETPARAM 0 + struct i915_user_extension base; + struct drm_i915_gem_context_param param; +}; + +struct drm_i915_gem_context_destroy { + __u32 ctx_id; + __u32 pad; +}; + +/* + * DRM_I915_GEM_VM_CREATE - + * + * Create a new virtual memory address space (ppGTT) for use within a context + * on the same file. Extensions can be provided to configure exactly how the + * address space is setup upon creation. + * + * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is + * returned in the outparam @id. + * + * No flags are defined, with all bits reserved and must be zero. + * + * An extension chain maybe provided, starting with @extensions, and terminated + * by the @next_extension being 0. Currently, no extensions are defined. + * + * DRM_I915_GEM_VM_DESTROY - + * + * Destroys a previously created VM id, specified in @id. + * + * No extensions or flags are allowed currently, and so must be zero. + */ +struct drm_i915_gem_vm_control { + __u64 extensions; + __u32 flags; + __u32 vm_id; +}; + +struct drm_i915_reg_read { + /* + * Register offset. + * For 64bit wide registers where the upper 32bits don't immediately + * follow the lower 32bits, the offset of the lower 32bits must + * be specified + */ + __u64 offset; +#define I915_REG_READ_8B_WA (1ul << 0) + + __u64 val; /* Return value */ +}; + +/* Known registers: + * + * Render engine timestamp - 0x2358 + 64bit - gen7+ + * - Note this register returns an invalid value if using the default + * single instruction 8byte read, in order to workaround that pass + * flag I915_REG_READ_8B_WA in offset field. + * + */ + +struct drm_i915_reset_stats { + __u32 ctx_id; + __u32 flags; + + /* All resets since boot/module reload, for all contexts */ + __u32 reset_count; + + /* Number of batches lost when active in GPU, for this context */ + __u32 batch_active; + + /* Number of batches lost pending for execution, for this context */ + __u32 batch_pending; + + __u32 pad; +}; + +struct drm_i915_gem_userptr { + __u64 user_ptr; + __u64 user_size; + __u32 flags; +#define I915_USERPTR_READ_ONLY 0x1 +#define I915_USERPTR_UNSYNCHRONIZED 0x80000000 + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; +}; + +enum drm_i915_oa_format { + I915_OA_FORMAT_A13 = 1, /* HSW only */ + I915_OA_FORMAT_A29, /* HSW only */ + I915_OA_FORMAT_A13_B8_C8, /* HSW only */ + I915_OA_FORMAT_B4_C8, /* HSW only */ + I915_OA_FORMAT_A45_B8_C8, /* HSW only */ + I915_OA_FORMAT_B4_C8_A16, /* HSW only */ + I915_OA_FORMAT_C4_B8, /* HSW+ */ + + /* Gen8+ */ + I915_OA_FORMAT_A12, + I915_OA_FORMAT_A12_B8_C8, + I915_OA_FORMAT_A32u40_A4u32_B8_C8, + + I915_OA_FORMAT_MAX /* non-ABI */ +}; + +enum drm_i915_perf_property_id { + /** + * Open the stream for a specific context handle (as used with + * execbuffer2). A stream opened for a specific context this way + * won't typically require root privileges. + */ + DRM_I915_PERF_PROP_CTX_HANDLE = 1, + + /** + * A value of 1 requests the inclusion of raw OA unit reports as + * part of stream samples. + */ + DRM_I915_PERF_PROP_SAMPLE_OA, + + /** + * The value specifies which set of OA unit metrics should be + * be configured, defining the contents of any OA unit reports. + */ + DRM_I915_PERF_PROP_OA_METRICS_SET, + + /** + * The value specifies the size and layout of OA unit reports. + */ + DRM_I915_PERF_PROP_OA_FORMAT, + + /** + * Specifying this property implicitly requests periodic OA unit + * sampling and (at least on Haswell) the sampling frequency is derived + * from this exponent as follows: + * + * 80ns * 2^(period_exponent + 1) + */ + DRM_I915_PERF_PROP_OA_EXPONENT, + + DRM_I915_PERF_PROP_MAX /* non-ABI */ +}; + +struct drm_i915_perf_open_param { + __u32 flags; +#define I915_PERF_FLAG_FD_CLOEXEC (1<<0) +#define I915_PERF_FLAG_FD_NONBLOCK (1<<1) +#define I915_PERF_FLAG_DISABLED (1<<2) + + /** The number of u64 (id, value) pairs */ + __u32 num_properties; + + /** + * Pointer to array of u64 (id, value) pairs configuring the stream + * to open. + */ + __u64 properties_ptr; +}; + +/** + * Enable data capture for a stream that was either opened in a disabled state + * via I915_PERF_FLAG_DISABLED or was later disabled via + * I915_PERF_IOCTL_DISABLE. + * + * It is intended to be cheaper to disable and enable a stream than it may be + * to close and re-open a stream with the same configuration. + * + * It's undefined whether any pending data for the stream will be lost. + */ +#define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) + +/** + * Disable data capture for a stream. + * + * It is an error to try and read a stream that is disabled. + */ +#define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) + +/** + * Common to all i915 perf records + */ +struct drm_i915_perf_record_header { + __u32 type; + __u16 pad; + __u16 size; +}; + +enum drm_i915_perf_record_type { + + /** + * Samples are the work horse record type whose contents are extensible + * and defined when opening an i915 perf stream based on the given + * properties. + * + * Boolean properties following the naming convention + * DRM_I915_PERF_SAMPLE_xyz_PROP request the inclusion of 'xyz' data in + * every sample. + * + * The order of these sample properties given by userspace has no + * affect on the ordering of data within a sample. The order is + * documented here. + * + * struct { + * struct drm_i915_perf_record_header header; + * + * { u32 oa_report[]; } && DRM_I915_PERF_PROP_SAMPLE_OA + * }; + */ + DRM_I915_PERF_RECORD_SAMPLE = 1, + + /* + * Indicates that one or more OA reports were not written by the + * hardware. This can happen for example if an MI_REPORT_PERF_COUNT + * command collides with periodic sampling - which would be more likely + * at higher sampling frequencies. + */ + DRM_I915_PERF_RECORD_OA_REPORT_LOST = 2, + + /** + * An error occurred that resulted in all pending OA reports being lost. + */ + DRM_I915_PERF_RECORD_OA_BUFFER_LOST = 3, + + DRM_I915_PERF_RECORD_MAX /* non-ABI */ +}; + +/** + * Structure to upload perf dynamic configuration into the kernel. + */ +struct drm_i915_perf_oa_config { + /** String formatted like "%08x-%04x-%04x-%04x-%012x" */ + char uuid[36]; + + __u32 n_mux_regs; + __u32 n_boolean_regs; + __u32 n_flex_regs; + + /* + * These fields are pointers to tuples of u32 values (register address, + * value). For example the expected length of the buffer pointed by + * mux_regs_ptr is (2 * sizeof(u32) * n_mux_regs). + */ + __u64 mux_regs_ptr; + __u64 boolean_regs_ptr; + __u64 flex_regs_ptr; +}; + +struct drm_i915_query_item { + __u64 query_id; +#define DRM_I915_QUERY_TOPOLOGY_INFO 1 +/* Must be kept compact -- no holes and well documented */ + + /* + * When set to zero by userspace, this is filled with the size of the + * data to be written at the data_ptr pointer. The kernel sets this + * value to a negative value to signal an error on a particular query + * item. + */ + __s32 length; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * Data will be written at the location pointed by data_ptr when the + * value of length matches the length of the data to be written by the + * kernel. + */ + __u64 data_ptr; +}; + +struct drm_i915_query { + __u32 num_items; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * This points to an array of num_items drm_i915_query_item structures. + */ + __u64 items_ptr; +}; + +/* + * Data written by the kernel with query DRM_I915_QUERY_TOPOLOGY_INFO : + * + * data: contains the 3 pieces of information : + * + * - the slice mask with one bit per slice telling whether a slice is + * available. The availability of slice X can be queried with the following + * formula : + * + * (data[X / 8] >> (X % 8)) & 1 + * + * - the subslice mask for each slice with one bit per subslice telling + * whether a subslice is available. The availability of subslice Y in slice + * X can be queried with the following formula : + * + * (data[subslice_offset + + * X * subslice_stride + + * Y / 8] >> (Y % 8)) & 1 + * + * - the EU mask for each subslice in each slice with one bit per EU telling + * whether an EU is available. The availability of EU Z in subslice Y in + * slice X can be queried with the following formula : + * + * (data[eu_offset + + * (X * max_subslices + Y) * eu_stride + + * Z / 8] >> (Z % 8)) & 1 + */ +struct drm_i915_query_topology_info { + /* + * Unused for now. Must be cleared to zero. + */ + __u16 flags; + + __u16 max_slices; + __u16 max_subslices; + __u16 max_eus_per_subslice; + + /* + * Offset in data[] at which the subslice masks are stored. + */ + __u16 subslice_offset; + + /* + * Stride at which each of the subslice masks for each slice are + * stored. + */ + __u16 subslice_stride; + + /* + * Offset in data[] at which the EU masks are stored. + */ + __u16 eu_offset; + + /* + * Stride at which each of the EU masks for each subslice are stored. + */ + __u16 eu_stride; + + __u8 data[]; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _I915_DRM_H_ */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mach64_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mach64_drm.h new file mode 100644 index 0000000..1f5fd84 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mach64_drm.h @@ -0,0 +1,256 @@ +/* mach64_drm.h -- Public header for the mach64 driver -*- linux-c -*- + * Created: Thu Nov 30 20:04:32 2000 by gareth@valinux.com + */ +/* + * Copyright 2000 Gareth Hughes + * Copyright 2002 Frank C. Earl + * Copyright 2002-2003 Leif Delgass + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * Frank C. Earl + * Leif Delgass + */ + +#ifndef __MACH64_DRM_H__ +#define __MACH64_DRM_H__ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mach64_sarea.h) + */ +#ifndef __MACH64_SAREA_DEFINES__ +#define __MACH64_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + * GH: We're going to be pedantic about this. We want the card to do as + * little as possible, so let's avoid having it fetch a whole bunch of + * register values that don't change all that often, if at all. + */ +#define MACH64_UPLOAD_DST_OFF_PITCH 0x0001 +#define MACH64_UPLOAD_Z_OFF_PITCH 0x0002 +#define MACH64_UPLOAD_Z_ALPHA_CNTL 0x0004 +#define MACH64_UPLOAD_SCALE_3D_CNTL 0x0008 +#define MACH64_UPLOAD_DP_FOG_CLR 0x0010 +#define MACH64_UPLOAD_DP_WRITE_MASK 0x0020 +#define MACH64_UPLOAD_DP_PIX_WIDTH 0x0040 +#define MACH64_UPLOAD_SETUP_CNTL 0x0080 +#define MACH64_UPLOAD_MISC 0x0100 +#define MACH64_UPLOAD_TEXTURE 0x0200 +#define MACH64_UPLOAD_TEX0IMAGE 0x0400 +#define MACH64_UPLOAD_TEX1IMAGE 0x0800 +#define MACH64_UPLOAD_CLIPRECTS 0x1000 /* handled client-side */ +#define MACH64_UPLOAD_CONTEXT 0x00ff +#define MACH64_UPLOAD_ALL 0x1fff + +/* DMA buffer size + */ +#define MACH64_BUFFER_SIZE 16384 + +/* Max number of swaps allowed on the ring + * before the client must wait + */ +#define MACH64_MAX_QUEUED_FRAMES 3U + +/* Byte offsets for host blit buffer data + */ +#define MACH64_HOSTDATA_BLIT_OFFSET 104 + +/* Keep these small for testing. + */ +#define MACH64_NR_SAREA_CLIPRECTS 8 + +#define MACH64_CARD_HEAP 0 +#define MACH64_AGP_HEAP 1 +#define MACH64_NR_TEX_HEAPS 2 +#define MACH64_NR_TEX_REGIONS 64 +#define MACH64_LOG_TEX_GRANULARITY 16 + +#define MACH64_TEX_MAXLEVELS 1 + +#define MACH64_NR_CONTEXT_REGS 15 +#define MACH64_NR_TEXTURE_REGS 4 + +#endif /* __MACH64_SAREA_DEFINES__ */ + +typedef struct { + unsigned int dst_off_pitch; + + unsigned int z_off_pitch; + unsigned int z_cntl; + unsigned int alpha_tst_cntl; + + unsigned int scale_3d_cntl; + + unsigned int sc_left_right; + unsigned int sc_top_bottom; + + unsigned int dp_fog_clr; + unsigned int dp_write_mask; + unsigned int dp_pix_width; + unsigned int dp_mix; + unsigned int dp_src; + + unsigned int clr_cmp_cntl; + unsigned int gui_traj_cntl; + + unsigned int setup_cntl; + + unsigned int tex_size_pitch; + unsigned int tex_cntl; + unsigned int secondary_tex_off; + unsigned int tex_offset; +} drm_mach64_context_regs_t; + +typedef struct drm_mach64_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex dma buffer. + */ + drm_mach64_context_regs_t context_state; + unsigned int dirty; + unsigned int vertsize; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[MACH64_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int frames_queued; + + /* Texture memory LRU. + */ + struct drm_tex_region tex_list[MACH64_NR_TEX_HEAPS][MACH64_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[MACH64_NR_TEX_HEAPS]; + int ctx_owner; +} drm_mach64_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mach64_common.h) + */ + +/* Mach64 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ + +#define DRM_MACH64_INIT 0x00 +#define DRM_MACH64_IDLE 0x01 +#define DRM_MACH64_RESET 0x02 +#define DRM_MACH64_SWAP 0x03 +#define DRM_MACH64_CLEAR 0x04 +#define DRM_MACH64_VERTEX 0x05 +#define DRM_MACH64_BLIT 0x06 +#define DRM_MACH64_FLUSH 0x07 +#define DRM_MACH64_GETPARAM 0x08 + +#define DRM_IOCTL_MACH64_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_INIT, drm_mach64_init_t) +#define DRM_IOCTL_MACH64_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_IDLE ) +#define DRM_IOCTL_MACH64_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_RESET ) +#define DRM_IOCTL_MACH64_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_SWAP ) +#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_CLEAR, drm_mach64_clear_t) +#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_VERTEX, drm_mach64_vertex_t) +#define DRM_IOCTL_MACH64_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_BLIT, drm_mach64_blit_t) +#define DRM_IOCTL_MACH64_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_FLUSH ) +#define DRM_IOCTL_MACH64_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_MACH64_GETPARAM, drm_mach64_getparam_t) + +/* Buffer flags for clears + */ +#define MACH64_FRONT 0x1 +#define MACH64_BACK 0x2 +#define MACH64_DEPTH 0x4 + +/* Primitive types for vertex buffers + */ +#define MACH64_PRIM_POINTS 0x00000000 +#define MACH64_PRIM_LINES 0x00000001 +#define MACH64_PRIM_LINE_LOOP 0x00000002 +#define MACH64_PRIM_LINE_STRIP 0x00000003 +#define MACH64_PRIM_TRIANGLES 0x00000004 +#define MACH64_PRIM_TRIANGLE_STRIP 0x00000005 +#define MACH64_PRIM_TRIANGLE_FAN 0x00000006 +#define MACH64_PRIM_QUADS 0x00000007 +#define MACH64_PRIM_QUAD_STRIP 0x00000008 +#define MACH64_PRIM_POLYGON 0x00000009 + +typedef enum _drm_mach64_dma_mode_t { + MACH64_MODE_DMA_ASYNC, + MACH64_MODE_DMA_SYNC, + MACH64_MODE_MMIO +} drm_mach64_dma_mode_t; + +typedef struct drm_mach64_init { + enum { + DRM_MACH64_INIT_DMA = 0x01, + DRM_MACH64_CLEANUP_DMA = 0x02 + } func; + + unsigned long sarea_priv_offset; + int is_pci; + drm_mach64_dma_mode_t dma_mode; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; +} drm_mach64_init_t; + +typedef struct drm_mach64_clear { + unsigned int flags; + int x, y, w, h; + unsigned int clear_color; + unsigned int clear_depth; +} drm_mach64_clear_t; + +typedef struct drm_mach64_vertex { + int prim; + void *buf; /* Address of vertex buffer */ + unsigned long used; /* Number of bytes in buffer */ + int discard; /* Client finished with buffer? */ +} drm_mach64_vertex_t; + +typedef struct drm_mach64_blit { + void *buf; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_mach64_blit_t; + +typedef struct drm_mach64_getparam { + enum { + MACH64_PARAM_FRAMES_QUEUED = 0x01, + MACH64_PARAM_IRQ_NR = 0x02 + } param; + void *value; +} drm_mach64_getparam_t; + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mga_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mga_drm.h new file mode 100644 index 0000000..7930011 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mga_drm.h @@ -0,0 +1,427 @@ +/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jeff Hartmann + * Keith Whitwell + * + * Rewritten by: + * Gareth Hughes + */ + +#ifndef __MGA_DRM_H__ +#define __MGA_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mga_sarea.h) + */ + +#ifndef __MGA_SAREA_DEFINES__ +#define __MGA_SAREA_DEFINES__ + +/* WARP pipe flags + */ +#define MGA_F 0x1 /* fog */ +#define MGA_A 0x2 /* alpha */ +#define MGA_S 0x4 /* specular */ +#define MGA_T2 0x8 /* multitexture */ + +#define MGA_WARP_TGZ 0 +#define MGA_WARP_TGZF (MGA_F) +#define MGA_WARP_TGZA (MGA_A) +#define MGA_WARP_TGZAF (MGA_F|MGA_A) +#define MGA_WARP_TGZS (MGA_S) +#define MGA_WARP_TGZSF (MGA_S|MGA_F) +#define MGA_WARP_TGZSA (MGA_S|MGA_A) +#define MGA_WARP_TGZSAF (MGA_S|MGA_F|MGA_A) +#define MGA_WARP_T2GZ (MGA_T2) +#define MGA_WARP_T2GZF (MGA_T2|MGA_F) +#define MGA_WARP_T2GZA (MGA_T2|MGA_A) +#define MGA_WARP_T2GZAF (MGA_T2|MGA_A|MGA_F) +#define MGA_WARP_T2GZS (MGA_T2|MGA_S) +#define MGA_WARP_T2GZSF (MGA_T2|MGA_S|MGA_F) +#define MGA_WARP_T2GZSA (MGA_T2|MGA_S|MGA_A) +#define MGA_WARP_T2GZSAF (MGA_T2|MGA_S|MGA_F|MGA_A) + +#define MGA_MAX_G200_PIPES 8 /* no multitex */ +#define MGA_MAX_G400_PIPES 16 +#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES +#define MGA_WARP_UCODE_SIZE 32768 /* in bytes */ + +#define MGA_CARD_TYPE_G200 1 +#define MGA_CARD_TYPE_G400 2 +#define MGA_CARD_TYPE_G450 3 /* not currently used */ +#define MGA_CARD_TYPE_G550 4 + +#define MGA_FRONT 0x1 +#define MGA_BACK 0x2 +#define MGA_DEPTH 0x4 + +/* What needs to be changed for the current vertex dma buffer? + */ +#define MGA_UPLOAD_CONTEXT 0x1 +#define MGA_UPLOAD_TEX0 0x2 +#define MGA_UPLOAD_TEX1 0x4 +#define MGA_UPLOAD_PIPE 0x8 +#define MGA_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */ +#define MGA_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */ +#define MGA_UPLOAD_2D 0x40 +#define MGA_WAIT_AGE 0x80 /* handled client-side */ +#define MGA_UPLOAD_CLIPRECTS 0x100 /* handled client-side */ +#if 0 +#define MGA_DMA_FLUSH 0x200 /* set when someone gets the lock + quiescent */ +#endif + +/* 32 buffers of 64k each, total 2 meg. + */ +#define MGA_BUFFER_SIZE (1 << 16) +#define MGA_NUM_BUFFERS 128 + +/* Keep these small for testing. + */ +#define MGA_NR_SAREA_CLIPRECTS 8 + +/* 2 heaps (1 for card, 1 for agp), each divided into up to 128 + * regions, subject to a minimum region size of (1<<16) == 64k. + * + * Clients may subdivide regions internally, but when sharing between + * clients, the region size is the minimum granularity. + */ + +#define MGA_CARD_HEAP 0 +#define MGA_AGP_HEAP 1 +#define MGA_NR_TEX_HEAPS 2 +#define MGA_NR_TEX_REGIONS 16 +#define MGA_LOG_MIN_TEX_REGION_SIZE 16 + +#define DRM_MGA_IDLE_RETRY 2048 + +#endif /* __MGA_SAREA_DEFINES__ */ + +/* Setup registers for 3D context + */ +typedef struct { + unsigned int dstorg; + unsigned int maccess; + unsigned int plnwt; + unsigned int dwgctl; + unsigned int alphactrl; + unsigned int fogcolor; + unsigned int wflag; + unsigned int tdualstage0; + unsigned int tdualstage1; + unsigned int fcol; + unsigned int stencil; + unsigned int stencilctl; +} drm_mga_context_regs_t; + +/* Setup registers for 2D, X server + */ +typedef struct { + unsigned int pitch; +} drm_mga_server_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int texctl; + unsigned int texctl2; + unsigned int texfilter; + unsigned int texbordercol; + unsigned int texorg; + unsigned int texwidth; + unsigned int texheight; + unsigned int texorg1; + unsigned int texorg2; + unsigned int texorg3; + unsigned int texorg4; +} drm_mga_texture_regs_t; + +/* General aging mechanism + */ +typedef struct { + unsigned int head; /* Position of head pointer */ + unsigned int wrap; /* Primary DMA wrap count */ +} drm_mga_age_t; + +typedef struct _drm_mga_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex dma buffer. + */ + drm_mga_context_regs_t context_state; + drm_mga_server_regs_t server_state; + drm_mga_texture_regs_t tex_state[2]; + unsigned int warp_pipe; + unsigned int dirty; + unsigned int vertsize; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[MGA_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Information about the most recently used 3d drawable. The + * client fills in the req_* fields, the server fills in the + * exported_ fields and puts the cliprects into boxes, above. + * + * The client clears the exported_drawable field before + * clobbering the boxes data. + */ + unsigned int req_drawable; /* the X drawable id */ + unsigned int req_draw_buffer; /* MGA_FRONT or MGA_BACK */ + + unsigned int exported_drawable; + unsigned int exported_index; + unsigned int exported_stamp; + unsigned int exported_buffers; + unsigned int exported_nfront; + unsigned int exported_nback; + int exported_back_x, exported_front_x, exported_w; + int exported_back_y, exported_front_y, exported_h; + struct drm_clip_rect exported_boxes[MGA_NR_SAREA_CLIPRECTS]; + + /* Counters for aging textures and for client-side throttling. + */ + unsigned int status[4]; + unsigned int last_wrap; + + drm_mga_age_t last_frame; + unsigned int last_enqueue; /* last time a buffer was enqueued */ + unsigned int last_dispatch; /* age of the most recently dispatched buffer */ + unsigned int last_quiescent; /* */ + + /* LRU lists for texture memory in agp space and on the card. + */ + struct drm_tex_region texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS + 1]; + unsigned int texAge[MGA_NR_TEX_HEAPS]; + + /* Mechanism to validate card state. + */ + int ctxOwner; +} drm_mga_sarea_t; + +/* MGA specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_MGA_INIT 0x00 +#define DRM_MGA_FLUSH 0x01 +#define DRM_MGA_RESET 0x02 +#define DRM_MGA_SWAP 0x03 +#define DRM_MGA_CLEAR 0x04 +#define DRM_MGA_VERTEX 0x05 +#define DRM_MGA_INDICES 0x06 +#define DRM_MGA_ILOAD 0x07 +#define DRM_MGA_BLIT 0x08 +#define DRM_MGA_GETPARAM 0x09 + +/* 3.2: + * ioctls for operating on fences. + */ +#define DRM_MGA_SET_FENCE 0x0a +#define DRM_MGA_WAIT_FENCE 0x0b +#define DRM_MGA_DMA_BOOTSTRAP 0x0c + +#define DRM_IOCTL_MGA_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_INIT, drm_mga_init_t) +#define DRM_IOCTL_MGA_FLUSH DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_FLUSH, struct drm_lock) +#define DRM_IOCTL_MGA_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MGA_RESET) +#define DRM_IOCTL_MGA_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MGA_SWAP) +#define DRM_IOCTL_MGA_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_CLEAR, drm_mga_clear_t) +#define DRM_IOCTL_MGA_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_VERTEX, drm_mga_vertex_t) +#define DRM_IOCTL_MGA_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_INDICES, drm_mga_indices_t) +#define DRM_IOCTL_MGA_ILOAD DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_ILOAD, drm_mga_iload_t) +#define DRM_IOCTL_MGA_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_BLIT, drm_mga_blit_t) +#define DRM_IOCTL_MGA_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_GETPARAM, drm_mga_getparam_t) +#define DRM_IOCTL_MGA_SET_FENCE DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_SET_FENCE, __u32) +#define DRM_IOCTL_MGA_WAIT_FENCE DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_WAIT_FENCE, __u32) +#define DRM_IOCTL_MGA_DMA_BOOTSTRAP DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_DMA_BOOTSTRAP, drm_mga_dma_bootstrap_t) + +typedef struct _drm_mga_warp_index { + int installed; + unsigned long phys_addr; + int size; +} drm_mga_warp_index_t; + +typedef struct drm_mga_init { + enum { + MGA_INIT_DMA = 0x01, + MGA_CLEANUP_DMA = 0x02 + } func; + + unsigned long sarea_priv_offset; + + int chipset; + int sgram; + + unsigned int maccess; + + unsigned int fb_cpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + + unsigned int depth_cpp; + unsigned int depth_offset, depth_pitch; + + unsigned int texture_offset[MGA_NR_TEX_HEAPS]; + unsigned int texture_size[MGA_NR_TEX_HEAPS]; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long status_offset; + unsigned long warp_offset; + unsigned long primary_offset; + unsigned long buffers_offset; +} drm_mga_init_t; + +typedef struct drm_mga_dma_bootstrap { + /** + * \name AGP texture region + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, these fields will + * be filled in with the actual AGP texture settings. + * + * \warning + * If these fields are non-zero, but dma_mga_dma_bootstrap::agp_mode + * is zero, it means that PCI memory (most likely through the use of + * an IOMMU) is being used for "AGP" textures. + */ + /*@{ */ + unsigned long texture_handle; /**< Handle used to map AGP textures. */ + __u32 texture_size; /**< Size of the AGP texture region. */ + /*@} */ + + /** + * Requested size of the primary DMA region. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual AGP mode. If AGP was not available + */ + __u32 primary_size; + + /** + * Requested number of secondary DMA buffers. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual number of secondary DMA buffers + * allocated. Particularly when PCI DMA is used, this may be + * (subtantially) less than the number requested. + */ + __u32 secondary_bin_count; + + /** + * Requested size of each secondary DMA buffer. + * + * While the kernel \b is free to reduce + * dma_mga_dma_bootstrap::secondary_bin_count, it is \b not allowed + * to reduce dma_mga_dma_bootstrap::secondary_bin_size. + */ + __u32 secondary_bin_size; + + /** + * Bit-wise mask of AGPSTAT2_* values. Currently only \c AGPSTAT2_1X, + * \c AGPSTAT2_2X, and \c AGPSTAT2_4X are supported. If this value is + * zero, it means that PCI DMA should be used, even if AGP is + * possible. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual AGP mode. If AGP was not available + * (i.e., PCI DMA was used), this value will be zero. + */ + __u32 agp_mode; + + /** + * Desired AGP GART size, measured in megabytes. + */ + __u8 agp_size; +} drm_mga_dma_bootstrap_t; + +typedef struct drm_mga_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; +} drm_mga_clear_t; + +typedef struct drm_mga_vertex { + int idx; /* buffer to queue */ + int used; /* bytes in use */ + int discard; /* client finished with buffer? */ +} drm_mga_vertex_t; + +typedef struct drm_mga_indices { + int idx; /* buffer to queue */ + unsigned int start; + unsigned int end; + int discard; /* client finished with buffer? */ +} drm_mga_indices_t; + +typedef struct drm_mga_iload { + int idx; + unsigned int dstorg; + unsigned int length; +} drm_mga_iload_t; + +typedef struct _drm_mga_blit { + unsigned int planemask; + unsigned int srcorg; + unsigned int dstorg; + int src_pitch, dst_pitch; + int delta_sx, delta_sy; + int delta_dx, delta_dy; + int height, ydir; /* flip image vertically */ + int source_pitch, dest_pitch; +} drm_mga_blit_t; + +/* 3.1: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define MGA_PARAM_IRQ_NR 1 + +/* 3.2: Query the actual card type. The DDX only distinguishes between + * G200 chips and non-G200 chips, which it calls G400. It turns out that + * there are some very sublte differences between the G4x0 chips and the G550 + * chips. Using this parameter query, a client-side driver can detect the + * difference between a G4x0 and a G550. + */ +#define MGA_PARAM_CARD_TYPE 2 + +typedef struct drm_mga_getparam { + int param; + void *value; +} drm_mga_getparam_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/nouveau_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/nouveau_drm.h new file mode 100644 index 0000000..d42105c --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/nouveau_drm.h @@ -0,0 +1,221 @@ +/* + * Copyright 2005 Stephane Marchesin. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NOUVEAU_DRM_H__ +#define __NOUVEAU_DRM_H__ + +#define NOUVEAU_DRM_HEADER_PATCHLEVEL 16 + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +struct drm_nouveau_channel_alloc { + uint32_t fb_ctxdma_handle; + uint32_t tt_ctxdma_handle; + + int channel; + uint32_t pushbuf_domains; + + /* Notifier memory */ + uint32_t notifier_handle; + + /* DRM-enforced subchannel assignments */ + struct { + uint32_t handle; + uint32_t grclass; + } subchan[8]; + uint32_t nr_subchan; +}; + +struct drm_nouveau_channel_free { + int channel; +}; + +struct drm_nouveau_grobj_alloc { + int channel; + uint32_t handle; + int class; +}; + +struct drm_nouveau_notifierobj_alloc { + uint32_t channel; + uint32_t handle; + uint32_t size; + uint32_t offset; +}; + +struct drm_nouveau_gpuobj_free { + int channel; + uint32_t handle; +}; + +/* FIXME : maybe unify {GET,SET}PARAMs */ +#define NOUVEAU_GETPARAM_PCI_VENDOR 3 +#define NOUVEAU_GETPARAM_PCI_DEVICE 4 +#define NOUVEAU_GETPARAM_BUS_TYPE 5 +#define NOUVEAU_GETPARAM_FB_PHYSICAL 6 +#define NOUVEAU_GETPARAM_AGP_PHYSICAL 7 +#define NOUVEAU_GETPARAM_FB_SIZE 8 +#define NOUVEAU_GETPARAM_AGP_SIZE 9 +#define NOUVEAU_GETPARAM_PCI_PHYSICAL 10 +#define NOUVEAU_GETPARAM_CHIPSET_ID 11 +#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12 +#define NOUVEAU_GETPARAM_GRAPH_UNITS 13 +#define NOUVEAU_GETPARAM_PTIMER_TIME 14 +#define NOUVEAU_GETPARAM_HAS_BO_USAGE 15 +#define NOUVEAU_GETPARAM_HAS_PAGEFLIP 16 +struct drm_nouveau_getparam { + uint64_t param; + uint64_t value; +}; + +struct drm_nouveau_setparam { + uint64_t param; + uint64_t value; +}; + +#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0) +#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1) +#define NOUVEAU_GEM_DOMAIN_GART (1 << 2) +#define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3) +#define NOUVEAU_GEM_DOMAIN_COHERENT (1 << 4) + +#define NOUVEAU_GEM_TILE_COMP 0x00030000 /* nv50-only */ +#define NOUVEAU_GEM_TILE_LAYOUT_MASK 0x0000ff00 +#define NOUVEAU_GEM_TILE_16BPP 0x00000001 +#define NOUVEAU_GEM_TILE_32BPP 0x00000002 +#define NOUVEAU_GEM_TILE_ZETA 0x00000004 +#define NOUVEAU_GEM_TILE_NONCONTIG 0x00000008 + +struct drm_nouveau_gem_info { + __u32 handle; + __u32 domain; + __u64 size; + __u64 offset; + __u64 map_handle; + __u32 tile_mode; + __u32 tile_flags; +}; + +struct drm_nouveau_gem_new { + struct drm_nouveau_gem_info info; + __u32 channel_hint; + __u32 align; +}; + +#define NOUVEAU_GEM_MAX_BUFFERS 1024 +struct drm_nouveau_gem_pushbuf_bo_presumed { + __u32 valid; + __u32 domain; + __u64 offset; +}; + +struct drm_nouveau_gem_pushbuf_bo { + __u64 user_priv; + __u32 handle; + __u32 read_domains; + __u32 write_domains; + __u32 valid_domains; + struct drm_nouveau_gem_pushbuf_bo_presumed presumed; +}; + +#define NOUVEAU_GEM_RELOC_LOW (1 << 0) +#define NOUVEAU_GEM_RELOC_HIGH (1 << 1) +#define NOUVEAU_GEM_RELOC_OR (1 << 2) +#define NOUVEAU_GEM_MAX_RELOCS 1024 +struct drm_nouveau_gem_pushbuf_reloc { + __u32 reloc_bo_index; + __u32 reloc_bo_offset; + __u32 bo_index; + __u32 flags; + __u32 data; + __u32 vor; + __u32 tor; +}; + +#define NOUVEAU_GEM_MAX_PUSH 512 +struct drm_nouveau_gem_pushbuf_push { + __u32 bo_index; + __u32 pad; + __u64 offset; + __u64 length; +}; + +struct drm_nouveau_gem_pushbuf { + __u32 channel; + __u32 nr_buffers; + __u64 buffers; + __u32 nr_relocs; + __u32 nr_push; + __u64 relocs; + __u64 push; + __u32 suffix0; + __u32 suffix1; + __u64 vram_available; + __u64 gart_available; +}; + +#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001 +#define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002 +#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004 +struct drm_nouveau_gem_cpu_prep { + __u32 handle; + __u32 flags; +}; + +struct drm_nouveau_gem_cpu_fini { + __u32 handle; +}; + +enum nouveau_bus_type { + NV_AGP = 0, + NV_PCI = 1, + NV_PCIE = 2, +}; + +struct drm_nouveau_sarea { +}; + +#define DRM_NOUVEAU_GETPARAM 0x00 +#define DRM_NOUVEAU_SETPARAM 0x01 +#define DRM_NOUVEAU_CHANNEL_ALLOC 0x02 +#define DRM_NOUVEAU_CHANNEL_FREE 0x03 +#define DRM_NOUVEAU_GROBJ_ALLOC 0x04 +#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x05 +#define DRM_NOUVEAU_GPUOBJ_FREE 0x06 +#define DRM_NOUVEAU_NVIF 0x07 +#define DRM_NOUVEAU_GEM_NEW 0x40 +#define DRM_NOUVEAU_GEM_PUSHBUF 0x41 +#define DRM_NOUVEAU_GEM_CPU_PREP 0x42 +#define DRM_NOUVEAU_GEM_CPU_FINI 0x43 +#define DRM_NOUVEAU_GEM_INFO 0x44 + +#if defined(__cplusplus) +} +#endif + +#endif /* __NOUVEAU_DRM_H__ */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/qxl_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/qxl_drm.h new file mode 100644 index 0000000..880999d --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/qxl_drm.h @@ -0,0 +1,158 @@ +/* + * Copyright 2013 Red Hat + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef QXL_DRM_H +#define QXL_DRM_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + * + * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel + * compatibility Keep fields aligned to their size + */ + +#define QXL_GEM_DOMAIN_CPU 0 +#define QXL_GEM_DOMAIN_VRAM 1 +#define QXL_GEM_DOMAIN_SURFACE 2 + +#define DRM_QXL_ALLOC 0x00 +#define DRM_QXL_MAP 0x01 +#define DRM_QXL_EXECBUFFER 0x02 +#define DRM_QXL_UPDATE_AREA 0x03 +#define DRM_QXL_GETPARAM 0x04 +#define DRM_QXL_CLIENTCAP 0x05 + +#define DRM_QXL_ALLOC_SURF 0x06 + +struct drm_qxl_alloc { + __u32 size; + __u32 handle; /* 0 is an invalid handle */ +}; + +struct drm_qxl_map { + __u64 offset; /* use for mmap system call */ + __u32 handle; + __u32 pad; +}; + +/* + * dest is the bo we are writing the relocation into + * src is bo we are relocating. + * *(dest_handle.base_addr + dest_offset) = physical_address(src_handle.addr + + * src_offset) + */ +#define QXL_RELOC_TYPE_BO 1 +#define QXL_RELOC_TYPE_SURF 2 + +struct drm_qxl_reloc { + __u64 src_offset; /* offset into src_handle or src buffer */ + __u64 dst_offset; /* offset in dest handle */ + __u32 src_handle; /* dest handle to compute address from */ + __u32 dst_handle; /* 0 if to command buffer */ + __u32 reloc_type; + __u32 pad; +}; + +struct drm_qxl_command { + __u64 command; /* void* */ + __u64 relocs; /* struct drm_qxl_reloc* */ + __u32 type; + __u32 command_size; + __u32 relocs_num; + __u32 pad; +}; + +struct drm_qxl_execbuffer { + __u32 flags; /* for future use */ + __u32 commands_num; + __u64 commands; /* struct drm_qxl_command* */ +}; + +struct drm_qxl_update_area { + __u32 handle; + __u32 top; + __u32 left; + __u32 bottom; + __u32 right; + __u32 pad; +}; + +#define QXL_PARAM_NUM_SURFACES 1 /* rom->n_surfaces */ +#define QXL_PARAM_MAX_RELOCS 2 +struct drm_qxl_getparam { + __u64 param; + __u64 value; +}; + +/* these are one bit values */ +struct drm_qxl_clientcap { + __u32 index; + __u32 pad; +}; + +struct drm_qxl_alloc_surf { + __u32 format; + __u32 width; + __u32 height; + __s32 stride; + __u32 handle; + __u32 pad; +}; + +#define DRM_IOCTL_QXL_ALLOC \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC, struct drm_qxl_alloc) + +#define DRM_IOCTL_QXL_MAP \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_MAP, struct drm_qxl_map) + +#define DRM_IOCTL_QXL_EXECBUFFER \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_EXECBUFFER,\ + struct drm_qxl_execbuffer) + +#define DRM_IOCTL_QXL_UPDATE_AREA \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_UPDATE_AREA,\ + struct drm_qxl_update_area) + +#define DRM_IOCTL_QXL_GETPARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_GETPARAM,\ + struct drm_qxl_getparam) + +#define DRM_IOCTL_QXL_CLIENTCAP \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_CLIENTCAP,\ + struct drm_qxl_clientcap) + +#define DRM_IOCTL_QXL_ALLOC_SURF \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC_SURF,\ + struct drm_qxl_alloc_surf) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/r128_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/r128_drm.h new file mode 100644 index 0000000..bf431a0 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/r128_drm.h @@ -0,0 +1,336 @@ +/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + */ +/* + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * Kevin E. Martin + */ + +#ifndef __R128_DRM_H__ +#define __R128_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (r128_sarea.h) + */ +#ifndef __R128_SAREA_DEFINES__ +#define __R128_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + */ +#define R128_UPLOAD_CONTEXT 0x001 +#define R128_UPLOAD_SETUP 0x002 +#define R128_UPLOAD_TEX0 0x004 +#define R128_UPLOAD_TEX1 0x008 +#define R128_UPLOAD_TEX0IMAGES 0x010 +#define R128_UPLOAD_TEX1IMAGES 0x020 +#define R128_UPLOAD_CORE 0x040 +#define R128_UPLOAD_MASKS 0x080 +#define R128_UPLOAD_WINDOW 0x100 +#define R128_UPLOAD_CLIPRECTS 0x200 /* handled client-side */ +#define R128_REQUIRE_QUIESCENCE 0x400 +#define R128_UPLOAD_ALL 0x7ff + +#define R128_FRONT 0x1 +#define R128_BACK 0x2 +#define R128_DEPTH 0x4 + +/* Primitive types + */ +#define R128_POINTS 0x1 +#define R128_LINES 0x2 +#define R128_LINE_STRIP 0x3 +#define R128_TRIANGLES 0x4 +#define R128_TRIANGLE_FAN 0x5 +#define R128_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define R128_BUFFER_SIZE 16384 + +/* Byte offsets for indirect buffer data + */ +#define R128_INDEX_PRIM_OFFSET 20 +#define R128_HOSTDATA_BLIT_OFFSET 32 + +/* Keep these small for testing. + */ +#define R128_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/AGP). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define R128_LOCAL_TEX_HEAP 0 +#define R128_AGP_TEX_HEAP 1 +#define R128_NR_TEX_HEAPS 2 +#define R128_NR_TEX_REGIONS 64 +#define R128_LOG_TEX_GRANULARITY 16 + +#define R128_NR_CONTEXT_REGS 12 + +#define R128_MAX_TEXTURE_LEVELS 11 +#define R128_MAX_TEXTURE_UNITS 2 + +#endif /* __R128_SAREA_DEFINES__ */ + +typedef struct { + /* Context state - can be written in one large chunk */ + unsigned int dst_pitch_offset_c; + unsigned int dp_gui_master_cntl_c; + unsigned int sc_top_left_c; + unsigned int sc_bottom_right_c; + unsigned int z_offset_c; + unsigned int z_pitch_c; + unsigned int z_sten_cntl_c; + unsigned int tex_cntl_c; + unsigned int misc_3d_state_cntl_reg; + unsigned int texture_clr_cmp_clr_c; + unsigned int texture_clr_cmp_msk_c; + unsigned int fog_color_c; + + /* Texture state */ + unsigned int tex_size_pitch_c; + unsigned int constant_color_c; + + /* Setup state */ + unsigned int pm4_vc_fpu_setup; + unsigned int setup_cntl; + + /* Mask state */ + unsigned int dp_write_mask; + unsigned int sten_ref_mask_c; + unsigned int plane_3d_mask_c; + + /* Window state */ + unsigned int window_xy_offset; + + /* Core state */ + unsigned int scale_3d_cntl; +} drm_r128_context_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int tex_cntl; + unsigned int tex_combine_cntl; + unsigned int tex_size_pitch; + unsigned int tex_offset[R128_MAX_TEXTURE_LEVELS]; + unsigned int tex_border_color; +} drm_r128_texture_regs_t; + +typedef struct drm_r128_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex buffer. + */ + drm_r128_context_regs_t context_state; + drm_r128_texture_regs_t tex_state[R128_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[R128_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + + struct drm_tex_region tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS + 1]; + unsigned int tex_age[R128_NR_TEX_HEAPS]; + int ctx_owner; + int pfAllowPageFlip; /* number of 3d windows (0,1,2 or more) */ + int pfCurrentPage; /* which buffer is being displayed? */ +} drm_r128_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmR128.h) + */ + +/* Rage 128 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_R128_INIT 0x00 +#define DRM_R128_CCE_START 0x01 +#define DRM_R128_CCE_STOP 0x02 +#define DRM_R128_CCE_RESET 0x03 +#define DRM_R128_CCE_IDLE 0x04 +/* 0x05 not used */ +#define DRM_R128_RESET 0x06 +#define DRM_R128_SWAP 0x07 +#define DRM_R128_CLEAR 0x08 +#define DRM_R128_VERTEX 0x09 +#define DRM_R128_INDICES 0x0a +#define DRM_R128_BLIT 0x0b +#define DRM_R128_DEPTH 0x0c +#define DRM_R128_STIPPLE 0x0d +/* 0x0e not used */ +#define DRM_R128_INDIRECT 0x0f +#define DRM_R128_FULLSCREEN 0x10 +#define DRM_R128_CLEAR2 0x11 +#define DRM_R128_GETPARAM 0x12 +#define DRM_R128_FLIP 0x13 + +#define DRM_IOCTL_R128_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_R128_INIT, drm_r128_init_t) +#define DRM_IOCTL_R128_CCE_START DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_START) +#define DRM_IOCTL_R128_CCE_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CCE_STOP, drm_r128_cce_stop_t) +#define DRM_IOCTL_R128_CCE_RESET DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_RESET) +#define DRM_IOCTL_R128_CCE_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_IDLE) +/* 0x05 not used */ +#define DRM_IOCTL_R128_RESET DRM_IO( DRM_COMMAND_BASE + DRM_R128_RESET) +#define DRM_IOCTL_R128_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_R128_SWAP) +#define DRM_IOCTL_R128_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CLEAR, drm_r128_clear_t) +#define DRM_IOCTL_R128_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_R128_VERTEX, drm_r128_vertex_t) +#define DRM_IOCTL_R128_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_R128_INDICES, drm_r128_indices_t) +#define DRM_IOCTL_R128_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_R128_BLIT, drm_r128_blit_t) +#define DRM_IOCTL_R128_DEPTH DRM_IOW( DRM_COMMAND_BASE + DRM_R128_DEPTH, drm_r128_depth_t) +#define DRM_IOCTL_R128_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_R128_STIPPLE, drm_r128_stipple_t) +/* 0x0e not used */ +#define DRM_IOCTL_R128_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_R128_INDIRECT, drm_r128_indirect_t) +#define DRM_IOCTL_R128_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_R128_FULLSCREEN, drm_r128_fullscreen_t) +#define DRM_IOCTL_R128_CLEAR2 DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CLEAR2, drm_r128_clear2_t) +#define DRM_IOCTL_R128_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_R128_GETPARAM, drm_r128_getparam_t) +#define DRM_IOCTL_R128_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_R128_FLIP) + +typedef struct drm_r128_init { + enum { + R128_INIT_CCE = 0x01, + R128_CLEANUP_CCE = 0x02 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cce_mode; + int cce_secure; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + unsigned int span_offset; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; +} drm_r128_init_t; + +typedef struct drm_r128_cce_stop { + int flush; + int idle; +} drm_r128_cce_stop_t; + +typedef struct drm_r128_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; +} drm_r128_clear_t; + +typedef struct drm_r128_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_r128_vertex_t; + +typedef struct drm_r128_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_r128_indices_t; + +typedef struct drm_r128_blit { + int idx; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_r128_blit_t; + +typedef struct drm_r128_depth { + enum { + R128_WRITE_SPAN = 0x01, + R128_WRITE_PIXELS = 0x02, + R128_READ_SPAN = 0x03, + R128_READ_PIXELS = 0x04 + } func; + int n; + int *x; + int *y; + unsigned int *buffer; + unsigned char *mask; +} drm_r128_depth_t; + +typedef struct drm_r128_stipple { + unsigned int *mask; +} drm_r128_stipple_t; + +typedef struct drm_r128_indirect { + int idx; + int start; + int end; + int discard; +} drm_r128_indirect_t; + +typedef struct drm_r128_fullscreen { + enum { + R128_INIT_FULLSCREEN = 0x01, + R128_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_r128_fullscreen_t; + +/* 2.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define R128_PARAM_IRQ_NR 1 + +typedef struct drm_r128_getparam { + int param; + void *value; +} drm_r128_getparam_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/radeon_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/radeon_drm.h new file mode 100644 index 0000000..a1e385d --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/radeon_drm.h @@ -0,0 +1,1079 @@ +/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __RADEON_DRM_H__ +#define __RADEON_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (radeon_sarea.h) + */ +#ifndef __RADEON_SAREA_DEFINES__ +#define __RADEON_SAREA_DEFINES__ + +/* Old style state flags, required for sarea interface (1.1 and 1.2 + * clears) and 1.2 drm_vertex2 ioctl. + */ +#define RADEON_UPLOAD_CONTEXT 0x00000001 +#define RADEON_UPLOAD_VERTFMT 0x00000002 +#define RADEON_UPLOAD_LINE 0x00000004 +#define RADEON_UPLOAD_BUMPMAP 0x00000008 +#define RADEON_UPLOAD_MASKS 0x00000010 +#define RADEON_UPLOAD_VIEWPORT 0x00000020 +#define RADEON_UPLOAD_SETUP 0x00000040 +#define RADEON_UPLOAD_TCL 0x00000080 +#define RADEON_UPLOAD_MISC 0x00000100 +#define RADEON_UPLOAD_TEX0 0x00000200 +#define RADEON_UPLOAD_TEX1 0x00000400 +#define RADEON_UPLOAD_TEX2 0x00000800 +#define RADEON_UPLOAD_TEX0IMAGES 0x00001000 +#define RADEON_UPLOAD_TEX1IMAGES 0x00002000 +#define RADEON_UPLOAD_TEX2IMAGES 0x00004000 +#define RADEON_UPLOAD_CLIPRECTS 0x00008000 /* handled client-side */ +#define RADEON_REQUIRE_QUIESCENCE 0x00010000 +#define RADEON_UPLOAD_ZBIAS 0x00020000 /* version 1.2 and newer */ +#define RADEON_UPLOAD_ALL 0x003effff +#define RADEON_UPLOAD_CONTEXT_ALL 0x003e01ff + +/* New style per-packet identifiers for use in cmd_buffer ioctl with + * the RADEON_EMIT_PACKET command. Comments relate new packets to old + * state bits and the packet size: + */ +#define RADEON_EMIT_PP_MISC 0 /* context/7 */ +#define RADEON_EMIT_PP_CNTL 1 /* context/3 */ +#define RADEON_EMIT_RB3D_COLORPITCH 2 /* context/1 */ +#define RADEON_EMIT_RE_LINE_PATTERN 3 /* line/2 */ +#define RADEON_EMIT_SE_LINE_WIDTH 4 /* line/1 */ +#define RADEON_EMIT_PP_LUM_MATRIX 5 /* bumpmap/1 */ +#define RADEON_EMIT_PP_ROT_MATRIX_0 6 /* bumpmap/2 */ +#define RADEON_EMIT_RB3D_STENCILREFMASK 7 /* masks/3 */ +#define RADEON_EMIT_SE_VPORT_XSCALE 8 /* viewport/6 */ +#define RADEON_EMIT_SE_CNTL 9 /* setup/2 */ +#define RADEON_EMIT_SE_CNTL_STATUS 10 /* setup/1 */ +#define RADEON_EMIT_RE_MISC 11 /* misc/1 */ +#define RADEON_EMIT_PP_TXFILTER_0 12 /* tex0/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_0 13 /* tex0/1 */ +#define RADEON_EMIT_PP_TXFILTER_1 14 /* tex1/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_1 15 /* tex1/1 */ +#define RADEON_EMIT_PP_TXFILTER_2 16 /* tex2/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_2 17 /* tex2/1 */ +#define RADEON_EMIT_SE_ZBIAS_FACTOR 18 /* zbias/2 */ +#define RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT 19 /* tcl/11 */ +#define RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED 20 /* material/17 */ +#define R200_EMIT_PP_TXCBLEND_0 21 /* tex0/4 */ +#define R200_EMIT_PP_TXCBLEND_1 22 /* tex1/4 */ +#define R200_EMIT_PP_TXCBLEND_2 23 /* tex2/4 */ +#define R200_EMIT_PP_TXCBLEND_3 24 /* tex3/4 */ +#define R200_EMIT_PP_TXCBLEND_4 25 /* tex4/4 */ +#define R200_EMIT_PP_TXCBLEND_5 26 /* tex5/4 */ +#define R200_EMIT_PP_TXCBLEND_6 27 /* /4 */ +#define R200_EMIT_PP_TXCBLEND_7 28 /* /4 */ +#define R200_EMIT_TCL_LIGHT_MODEL_CTL_0 29 /* tcl/7 */ +#define R200_EMIT_TFACTOR_0 30 /* tf/7 */ +#define R200_EMIT_VTX_FMT_0 31 /* vtx/5 */ +#define R200_EMIT_VAP_CTL 32 /* vap/1 */ +#define R200_EMIT_MATRIX_SELECT_0 33 /* msl/5 */ +#define R200_EMIT_TEX_PROC_CTL_2 34 /* tcg/5 */ +#define R200_EMIT_TCL_UCP_VERT_BLEND_CTL 35 /* tcl/1 */ +#define R200_EMIT_PP_TXFILTER_0 36 /* tex0/6 */ +#define R200_EMIT_PP_TXFILTER_1 37 /* tex1/6 */ +#define R200_EMIT_PP_TXFILTER_2 38 /* tex2/6 */ +#define R200_EMIT_PP_TXFILTER_3 39 /* tex3/6 */ +#define R200_EMIT_PP_TXFILTER_4 40 /* tex4/6 */ +#define R200_EMIT_PP_TXFILTER_5 41 /* tex5/6 */ +#define R200_EMIT_PP_TXOFFSET_0 42 /* tex0/1 */ +#define R200_EMIT_PP_TXOFFSET_1 43 /* tex1/1 */ +#define R200_EMIT_PP_TXOFFSET_2 44 /* tex2/1 */ +#define R200_EMIT_PP_TXOFFSET_3 45 /* tex3/1 */ +#define R200_EMIT_PP_TXOFFSET_4 46 /* tex4/1 */ +#define R200_EMIT_PP_TXOFFSET_5 47 /* tex5/1 */ +#define R200_EMIT_VTE_CNTL 48 /* vte/1 */ +#define R200_EMIT_OUTPUT_VTX_COMP_SEL 49 /* vtx/1 */ +#define R200_EMIT_PP_TAM_DEBUG3 50 /* tam/1 */ +#define R200_EMIT_PP_CNTL_X 51 /* cst/1 */ +#define R200_EMIT_RB3D_DEPTHXY_OFFSET 52 /* cst/1 */ +#define R200_EMIT_RE_AUX_SCISSOR_CNTL 53 /* cst/1 */ +#define R200_EMIT_RE_SCISSOR_TL_0 54 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_1 55 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_2 56 /* cst/2 */ +#define R200_EMIT_SE_VAP_CNTL_STATUS 57 /* cst/1 */ +#define R200_EMIT_SE_VTX_STATE_CNTL 58 /* cst/1 */ +#define R200_EMIT_RE_POINTSIZE 59 /* cst/1 */ +#define R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0 60 /* cst/4 */ +#define R200_EMIT_PP_CUBIC_FACES_0 61 +#define R200_EMIT_PP_CUBIC_OFFSETS_0 62 +#define R200_EMIT_PP_CUBIC_FACES_1 63 +#define R200_EMIT_PP_CUBIC_OFFSETS_1 64 +#define R200_EMIT_PP_CUBIC_FACES_2 65 +#define R200_EMIT_PP_CUBIC_OFFSETS_2 66 +#define R200_EMIT_PP_CUBIC_FACES_3 67 +#define R200_EMIT_PP_CUBIC_OFFSETS_3 68 +#define R200_EMIT_PP_CUBIC_FACES_4 69 +#define R200_EMIT_PP_CUBIC_OFFSETS_4 70 +#define R200_EMIT_PP_CUBIC_FACES_5 71 +#define R200_EMIT_PP_CUBIC_OFFSETS_5 72 +#define RADEON_EMIT_PP_TEX_SIZE_0 73 +#define RADEON_EMIT_PP_TEX_SIZE_1 74 +#define RADEON_EMIT_PP_TEX_SIZE_2 75 +#define R200_EMIT_RB3D_BLENDCOLOR 76 +#define R200_EMIT_TCL_POINT_SPRITE_CNTL 77 +#define RADEON_EMIT_PP_CUBIC_FACES_0 78 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T0 79 +#define RADEON_EMIT_PP_CUBIC_FACES_1 80 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T1 81 +#define RADEON_EMIT_PP_CUBIC_FACES_2 82 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T2 83 +#define R200_EMIT_PP_TRI_PERF_CNTL 84 +#define R200_EMIT_PP_AFS_0 85 +#define R200_EMIT_PP_AFS_1 86 +#define R200_EMIT_ATF_TFACTOR 87 +#define R200_EMIT_PP_TXCTLALL_0 88 +#define R200_EMIT_PP_TXCTLALL_1 89 +#define R200_EMIT_PP_TXCTLALL_2 90 +#define R200_EMIT_PP_TXCTLALL_3 91 +#define R200_EMIT_PP_TXCTLALL_4 92 +#define R200_EMIT_PP_TXCTLALL_5 93 +#define R200_EMIT_VAP_PVS_CNTL 94 +#define RADEON_MAX_STATE_PACKETS 95 + +/* Commands understood by cmd_buffer ioctl. More can be added but + * obviously these can't be removed or changed: + */ +#define RADEON_CMD_PACKET 1 /* emit one of the register packets above */ +#define RADEON_CMD_SCALARS 2 /* emit scalar data */ +#define RADEON_CMD_VECTORS 3 /* emit vector data */ +#define RADEON_CMD_DMA_DISCARD 4 /* discard current dma buf */ +#define RADEON_CMD_PACKET3 5 /* emit hw packet */ +#define RADEON_CMD_PACKET3_CLIP 6 /* emit hw packet wrapped in cliprects */ +#define RADEON_CMD_SCALARS2 7 /* r200 stopgap */ +#define RADEON_CMD_WAIT 8 /* emit hw wait commands -- note: + * doesn't make the cpu wait, just + * the graphics hardware */ +#define RADEON_CMD_VECLINEAR 9 /* another r200 stopgap */ + +typedef union { + int i; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, packet_id, pad0, pad1; + } packet; + struct { + unsigned char cmd_type, offset, stride, count; + } scalars; + struct { + unsigned char cmd_type, offset, stride, count; + } vectors; + struct { + unsigned char cmd_type, addr_lo, addr_hi, count; + } veclinear; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; +} drm_radeon_cmd_header_t; + +#define RADEON_WAIT_2D 0x1 +#define RADEON_WAIT_3D 0x2 + +/* Allowed parameters for R300_CMD_PACKET3 + */ +#define R300_CMD_PACKET3_CLEAR 0 +#define R300_CMD_PACKET3_RAW 1 + +/* Commands understood by cmd_buffer ioctl for R300. + * The interface has not been stabilized, so some of these may be removed + * and eventually reordered before stabilization. + */ +#define R300_CMD_PACKET0 1 +#define R300_CMD_VPU 2 /* emit vertex program upload */ +#define R300_CMD_PACKET3 3 /* emit a packet3 */ +#define R300_CMD_END3D 4 /* emit sequence ending 3d rendering */ +#define R300_CMD_CP_DELAY 5 +#define R300_CMD_DMA_DISCARD 6 +#define R300_CMD_WAIT 7 +# define R300_WAIT_2D 0x1 +# define R300_WAIT_3D 0x2 +/* these two defines are DOING IT WRONG - however + * we have userspace which relies on using these. + * The wait interface is backwards compat new + * code should use the NEW_WAIT defines below + * THESE ARE NOT BIT FIELDS + */ +# define R300_WAIT_2D_CLEAN 0x3 +# define R300_WAIT_3D_CLEAN 0x4 + +# define R300_NEW_WAIT_2D_3D 0x3 +# define R300_NEW_WAIT_2D_2D_CLEAN 0x4 +# define R300_NEW_WAIT_3D_3D_CLEAN 0x6 +# define R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN 0x8 + +#define R300_CMD_SCRATCH 8 +#define R300_CMD_R500FP 9 + +typedef union { + unsigned int u; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, count, reglo, reghi; + } packet0; + struct { + unsigned char cmd_type, count, adrlo, adrhi; + } vpu; + struct { + unsigned char cmd_type, packet, pad0, pad1; + } packet3; + struct { + unsigned char cmd_type, packet; + unsigned short count; /* amount of packet2 to emit */ + } delay; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; + struct { + unsigned char cmd_type, reg, n_bufs, flags; + } scratch; + struct { + unsigned char cmd_type, count, adrlo, adrhi_flags; + } r500fp; +} drm_r300_cmd_header_t; + +#define RADEON_FRONT 0x1 +#define RADEON_BACK 0x2 +#define RADEON_DEPTH 0x4 +#define RADEON_STENCIL 0x8 +#define RADEON_CLEAR_FASTZ 0x80000000 +#define RADEON_USE_HIERZ 0x40000000 +#define RADEON_USE_COMP_ZBUF 0x20000000 + +#define R500FP_CONSTANT_TYPE (1 << 1) +#define R500FP_CONSTANT_CLAMP (1 << 2) + +/* Primitive types + */ +#define RADEON_POINTS 0x1 +#define RADEON_LINES 0x2 +#define RADEON_LINE_STRIP 0x3 +#define RADEON_TRIANGLES 0x4 +#define RADEON_TRIANGLE_FAN 0x5 +#define RADEON_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define RADEON_BUFFER_SIZE 65536 + +/* Byte offsets for indirect buffer data + */ +#define RADEON_INDEX_PRIM_OFFSET 20 + +#define RADEON_SCRATCH_REG_OFFSET 32 + +#define R600_SCRATCH_REG_OFFSET 256 + +#define RADEON_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/GART). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define RADEON_LOCAL_TEX_HEAP 0 +#define RADEON_GART_TEX_HEAP 1 +#define RADEON_NR_TEX_HEAPS 2 +#define RADEON_NR_TEX_REGIONS 64 +#define RADEON_LOG_TEX_GRANULARITY 16 + +#define RADEON_MAX_TEXTURE_LEVELS 12 +#define RADEON_MAX_TEXTURE_UNITS 3 + +#define RADEON_MAX_SURFACES 8 + +/* Blits have strict offset rules. All blit offset must be aligned on + * a 1K-byte boundary. + */ +#define RADEON_OFFSET_SHIFT 10 +#define RADEON_OFFSET_ALIGN (1 << RADEON_OFFSET_SHIFT) +#define RADEON_OFFSET_MASK (RADEON_OFFSET_ALIGN - 1) + +#endif /* __RADEON_SAREA_DEFINES__ */ + +typedef struct { + unsigned int red; + unsigned int green; + unsigned int blue; + unsigned int alpha; +} radeon_color_regs_t; + +typedef struct { + /* Context state */ + unsigned int pp_misc; /* 0x1c14 */ + unsigned int pp_fog_color; + unsigned int re_solid_color; + unsigned int rb3d_blendcntl; + unsigned int rb3d_depthoffset; + unsigned int rb3d_depthpitch; + unsigned int rb3d_zstencilcntl; + + unsigned int pp_cntl; /* 0x1c38 */ + unsigned int rb3d_cntl; + unsigned int rb3d_coloroffset; + unsigned int re_width_height; + unsigned int rb3d_colorpitch; + unsigned int se_cntl; + + /* Vertex format state */ + unsigned int se_coord_fmt; /* 0x1c50 */ + + /* Line state */ + unsigned int re_line_pattern; /* 0x1cd0 */ + unsigned int re_line_state; + + unsigned int se_line_width; /* 0x1db8 */ + + /* Bumpmap state */ + unsigned int pp_lum_matrix; /* 0x1d00 */ + + unsigned int pp_rot_matrix_0; /* 0x1d58 */ + unsigned int pp_rot_matrix_1; + + /* Mask state */ + unsigned int rb3d_stencilrefmask; /* 0x1d7c */ + unsigned int rb3d_ropcntl; + unsigned int rb3d_planemask; + + /* Viewport state */ + unsigned int se_vport_xscale; /* 0x1d98 */ + unsigned int se_vport_xoffset; + unsigned int se_vport_yscale; + unsigned int se_vport_yoffset; + unsigned int se_vport_zscale; + unsigned int se_vport_zoffset; + + /* Setup state */ + unsigned int se_cntl_status; /* 0x2140 */ + + /* Misc state */ + unsigned int re_top_left; /* 0x26c0 */ + unsigned int re_misc; +} drm_radeon_context_regs_t; + +typedef struct { + /* Zbias state */ + unsigned int se_zbias_factor; /* 0x1dac */ + unsigned int se_zbias_constant; +} drm_radeon_context2_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int pp_txfilter; + unsigned int pp_txformat; + unsigned int pp_txoffset; + unsigned int pp_txcblend; + unsigned int pp_txablend; + unsigned int pp_tfactor; + unsigned int pp_border_color; +} drm_radeon_texture_regs_t; + +typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim:8; + unsigned int stateidx:8; + unsigned int numverts:16; /* overloaded as offset/64 for elt prims */ + unsigned int vc_format; /* vertex format */ +} drm_radeon_prim_t; + +typedef struct { + drm_radeon_context_regs_t context; + drm_radeon_texture_regs_t tex[RADEON_MAX_TEXTURE_UNITS]; + drm_radeon_context2_regs_t context2; + unsigned int dirty; +} drm_radeon_state_t; + +typedef struct { + /* The channel for communication of state information to the + * kernel on firing a vertex buffer with either of the + * obsoleted vertex/index ioctls. + */ + drm_radeon_context_regs_t context_state; + drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[RADEON_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + unsigned int last_clear; + + struct drm_tex_region tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[RADEON_NR_TEX_HEAPS]; + int ctx_owner; + int pfState; /* number of 3d windows (0,1,2ormore) */ + int pfCurrentPage; /* which buffer is being displayed? */ + int crtc2_base; /* CRTC2 frame offset */ + int tiling_enabled; /* set by drm, read by 2d + 3d clients */ +} drm_radeon_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmRadeon.h) + * + * KW: actually it's illegal to change any of this (backwards compatibility). + */ + +/* Radeon specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_RADEON_CP_INIT 0x00 +#define DRM_RADEON_CP_START 0x01 +#define DRM_RADEON_CP_STOP 0x02 +#define DRM_RADEON_CP_RESET 0x03 +#define DRM_RADEON_CP_IDLE 0x04 +#define DRM_RADEON_RESET 0x05 +#define DRM_RADEON_FULLSCREEN 0x06 +#define DRM_RADEON_SWAP 0x07 +#define DRM_RADEON_CLEAR 0x08 +#define DRM_RADEON_VERTEX 0x09 +#define DRM_RADEON_INDICES 0x0A +#define DRM_RADEON_NOT_USED +#define DRM_RADEON_STIPPLE 0x0C +#define DRM_RADEON_INDIRECT 0x0D +#define DRM_RADEON_TEXTURE 0x0E +#define DRM_RADEON_VERTEX2 0x0F +#define DRM_RADEON_CMDBUF 0x10 +#define DRM_RADEON_GETPARAM 0x11 +#define DRM_RADEON_FLIP 0x12 +#define DRM_RADEON_ALLOC 0x13 +#define DRM_RADEON_FREE 0x14 +#define DRM_RADEON_INIT_HEAP 0x15 +#define DRM_RADEON_IRQ_EMIT 0x16 +#define DRM_RADEON_IRQ_WAIT 0x17 +#define DRM_RADEON_CP_RESUME 0x18 +#define DRM_RADEON_SETPARAM 0x19 +#define DRM_RADEON_SURF_ALLOC 0x1a +#define DRM_RADEON_SURF_FREE 0x1b +/* KMS ioctl */ +#define DRM_RADEON_GEM_INFO 0x1c +#define DRM_RADEON_GEM_CREATE 0x1d +#define DRM_RADEON_GEM_MMAP 0x1e +#define DRM_RADEON_GEM_PREAD 0x21 +#define DRM_RADEON_GEM_PWRITE 0x22 +#define DRM_RADEON_GEM_SET_DOMAIN 0x23 +#define DRM_RADEON_GEM_WAIT_IDLE 0x24 +#define DRM_RADEON_CS 0x26 +#define DRM_RADEON_INFO 0x27 +#define DRM_RADEON_GEM_SET_TILING 0x28 +#define DRM_RADEON_GEM_GET_TILING 0x29 +#define DRM_RADEON_GEM_BUSY 0x2a +#define DRM_RADEON_GEM_VA 0x2b +#define DRM_RADEON_GEM_OP 0x2c +#define DRM_RADEON_GEM_USERPTR 0x2d + +#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) +#define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) +#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t) +#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESET) +#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_IDLE) +#define DRM_IOCTL_RADEON_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_RESET) +#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FULLSCREEN, drm_radeon_fullscreen_t) +#define DRM_IOCTL_RADEON_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_SWAP) +#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CLEAR, drm_radeon_clear_t) +#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX, drm_radeon_vertex_t) +#define DRM_IOCTL_RADEON_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INDICES, drm_radeon_indices_t) +#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_STIPPLE, drm_radeon_stipple_t) +#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INDIRECT, drm_radeon_indirect_t) +#define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_TEXTURE, drm_radeon_texture_t) +#define DRM_IOCTL_RADEON_VERTEX2 DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX2, drm_radeon_vertex2_t) +#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CMDBUF, drm_radeon_cmd_buffer_t) +#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GETPARAM, drm_radeon_getparam_t) +#define DRM_IOCTL_RADEON_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_FLIP) +#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_ALLOC, drm_radeon_mem_alloc_t) +#define DRM_IOCTL_RADEON_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FREE, drm_radeon_mem_free_t) +#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INIT_HEAP, drm_radeon_mem_init_heap_t) +#define DRM_IOCTL_RADEON_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_IRQ_EMIT, drm_radeon_irq_emit_t) +#define DRM_IOCTL_RADEON_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_IRQ_WAIT, drm_radeon_irq_wait_t) +#define DRM_IOCTL_RADEON_CP_RESUME DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESUME) +#define DRM_IOCTL_RADEON_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SETPARAM, drm_radeon_setparam_t) +#define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t) +#define DRM_IOCTL_RADEON_SURF_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t) +/* KMS */ +#define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) +#define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) +#define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) +#define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) +#define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) +#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) +#define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle) +#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) +#define DRM_IOCTL_RADEON_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INFO, struct drm_radeon_info) +#define DRM_IOCTL_RADEON_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_TILING, struct drm_radeon_gem_set_tiling) +#define DRM_IOCTL_RADEON_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_GET_TILING, struct drm_radeon_gem_get_tiling) +#define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy) +#define DRM_IOCTL_RADEON_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_VA, struct drm_radeon_gem_va) +#define DRM_IOCTL_RADEON_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_OP, struct drm_radeon_gem_op) +#define DRM_IOCTL_RADEON_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_USERPTR, struct drm_radeon_gem_userptr) + +typedef struct drm_radeon_init { + enum { + RADEON_INIT_CP = 0x01, + RADEON_CLEANUP_CP = 0x02, + RADEON_INIT_R200_CP = 0x03, + RADEON_INIT_R300_CP = 0x04, + RADEON_INIT_R600_CP = 0x05 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cp_mode; + int gart_size; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long gart_textures_offset; +} drm_radeon_init_t; + +typedef struct drm_radeon_cp_stop { + int flush; + int idle; +} drm_radeon_cp_stop_t; + +typedef struct drm_radeon_fullscreen { + enum { + RADEON_INIT_FULLSCREEN = 0x01, + RADEON_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_radeon_fullscreen_t; + +#define CLEAR_X1 0 +#define CLEAR_Y1 1 +#define CLEAR_X2 2 +#define CLEAR_Y2 3 +#define CLEAR_DEPTH 4 + +typedef union drm_radeon_clear_rect { + float f[5]; + unsigned int ui[5]; +} drm_radeon_clear_rect_t; + +typedef struct drm_radeon_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; /* misnamed field: should be stencil */ + drm_radeon_clear_rect_t *depth_boxes; +} drm_radeon_clear_t; + +typedef struct drm_radeon_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_radeon_vertex_t; + +typedef struct drm_radeon_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_radeon_indices_t; + +/* v1.2 - obsoletes drm_radeon_vertex and drm_radeon_indices + * - allows multiple primitives and state changes in a single ioctl + * - supports driver change to emit native primitives + */ +typedef struct drm_radeon_vertex2 { + int idx; /* Index of vertex buffer */ + int discard; /* Client finished with buffer? */ + int nr_states; + drm_radeon_state_t *state; + int nr_prims; + drm_radeon_prim_t *prim; +} drm_radeon_vertex2_t; + +/* v1.3 - obsoletes drm_radeon_vertex2 + * - allows arbitrarily large cliprect list + * - allows updating of tcl packet, vector and scalar state + * - allows memory-efficient description of state updates + * - allows state to be emitted without a primitive + * (for clears, ctx switches) + * - allows more than one dma buffer to be referenced per ioctl + * - supports tcl driver + * - may be extended in future versions with new cmd types, packets + */ +typedef struct drm_radeon_cmd_buffer { + int bufsz; + char *buf; + int nbox; + struct drm_clip_rect *boxes; +} drm_radeon_cmd_buffer_t; + +typedef struct drm_radeon_tex_image { + unsigned int x, y; /* Blit coordinates */ + unsigned int width, height; + const void *data; +} drm_radeon_tex_image_t; + +typedef struct drm_radeon_texture { + unsigned int offset; + int pitch; + int format; + int width; /* Texture image coordinates */ + int height; + drm_radeon_tex_image_t *image; +} drm_radeon_texture_t; + +typedef struct drm_radeon_stipple { + unsigned int *mask; +} drm_radeon_stipple_t; + +typedef struct drm_radeon_indirect { + int idx; + int start; + int end; + int discard; +} drm_radeon_indirect_t; + +/* enum for card type parameters */ +#define RADEON_CARD_PCI 0 +#define RADEON_CARD_AGP 1 +#define RADEON_CARD_PCIE 2 + +/* 1.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define RADEON_PARAM_GART_BUFFER_OFFSET 1 /* card offset of 1st GART buffer */ +#define RADEON_PARAM_LAST_FRAME 2 +#define RADEON_PARAM_LAST_DISPATCH 3 +#define RADEON_PARAM_LAST_CLEAR 4 +/* Added with DRM version 1.6. */ +#define RADEON_PARAM_IRQ_NR 5 +#define RADEON_PARAM_GART_BASE 6 /* card offset of GART base */ +/* Added with DRM version 1.8. */ +#define RADEON_PARAM_REGISTER_HANDLE 7 /* for drmMap() */ +#define RADEON_PARAM_STATUS_HANDLE 8 +#define RADEON_PARAM_SAREA_HANDLE 9 +#define RADEON_PARAM_GART_TEX_HANDLE 10 +#define RADEON_PARAM_SCRATCH_OFFSET 11 +#define RADEON_PARAM_CARD_TYPE 12 +#define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ +#define RADEON_PARAM_FB_LOCATION 14 /* FB location */ +#define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ +#define RADEON_PARAM_DEVICE_ID 16 +#define RADEON_PARAM_NUM_Z_PIPES 17 /* num Z pipes */ + +typedef struct drm_radeon_getparam { + int param; + void *value; +} drm_radeon_getparam_t; + +/* 1.6: Set up a memory manager for regions of shared memory: + */ +#define RADEON_MEM_REGION_GART 1 +#define RADEON_MEM_REGION_FB 2 + +typedef struct drm_radeon_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or GART */ +} drm_radeon_mem_alloc_t; + +typedef struct drm_radeon_mem_free { + int region; + int region_offset; +} drm_radeon_mem_free_t; + +typedef struct drm_radeon_mem_init_heap { + int region; + int size; + int start; +} drm_radeon_mem_init_heap_t; + +/* 1.6: Userspace can request & wait on irq's: + */ +typedef struct drm_radeon_irq_emit { + int *irq_seq; +} drm_radeon_irq_emit_t; + +typedef struct drm_radeon_irq_wait { + int irq_seq; +} drm_radeon_irq_wait_t; + +/* 1.10: Clients tell the DRM where they think the framebuffer is located in + * the card's address space, via a new generic ioctl to set parameters + */ + +typedef struct drm_radeon_setparam { + unsigned int param; + __s64 value; +} drm_radeon_setparam_t; + +#define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ +#define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ +#define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ +#define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ +#define RADEON_SETPARAM_PCIGART_TABLE_SIZE 5 /* PCI GART Table Size */ +#define RADEON_SETPARAM_VBLANK_CRTC 6 /* VBLANK CRTC */ +/* 1.14: Clients can allocate/free a surface + */ +typedef struct drm_radeon_surface_alloc { + unsigned int address; + unsigned int size; + unsigned int flags; +} drm_radeon_surface_alloc_t; + +typedef struct drm_radeon_surface_free { + unsigned int address; +} drm_radeon_surface_free_t; + +#define DRM_RADEON_VBLANK_CRTC1 1 +#define DRM_RADEON_VBLANK_CRTC2 2 + +/* + * Kernel modesetting world below. + */ +#define RADEON_GEM_DOMAIN_CPU 0x1 +#define RADEON_GEM_DOMAIN_GTT 0x2 +#define RADEON_GEM_DOMAIN_VRAM 0x4 + +struct drm_radeon_gem_info { + __u64 gart_size; + __u64 vram_size; + __u64 vram_visible; +}; + +#define RADEON_GEM_NO_BACKING_STORE (1 << 0) +#define RADEON_GEM_GTT_UC (1 << 1) +#define RADEON_GEM_GTT_WC (1 << 2) +/* BO is expected to be accessed by the CPU */ +#define RADEON_GEM_CPU_ACCESS (1 << 3) +/* CPU access is not expected to work for this BO */ +#define RADEON_GEM_NO_CPU_ACCESS (1 << 4) + +struct drm_radeon_gem_create { + __u64 size; + __u64 alignment; + __u32 handle; + __u32 initial_domain; + __u32 flags; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define RADEON_GEM_USERPTR_READONLY (1 << 0) +#define RADEON_GEM_USERPTR_ANONONLY (1 << 1) +#define RADEON_GEM_USERPTR_VALIDATE (1 << 2) +#define RADEON_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_radeon_gem_userptr { + __u64 addr; + __u64 size; + __u32 flags; + __u32 handle; +}; + +#define RADEON_TILING_MACRO 0x1 +#define RADEON_TILING_MICRO 0x2 +#define RADEON_TILING_SWAP_16BIT 0x4 +#define RADEON_TILING_R600_NO_SCANOUT RADEON_TILING_SWAP_16BIT +#define RADEON_TILING_SWAP_32BIT 0x8 +/* this object requires a surface when mapped - i.e. front buffer */ +#define RADEON_TILING_SURFACE 0x10 +#define RADEON_TILING_MICRO_SQUARE 0x20 +#define RADEON_TILING_EG_BANKW_SHIFT 8 +#define RADEON_TILING_EG_BANKW_MASK 0xf +#define RADEON_TILING_EG_BANKH_SHIFT 12 +#define RADEON_TILING_EG_BANKH_MASK 0xf +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf +#define RADEON_TILING_EG_TILE_SPLIT_SHIFT 24 +#define RADEON_TILING_EG_TILE_SPLIT_MASK 0xf +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf + +struct drm_radeon_gem_set_tiling { + __u32 handle; + __u32 tiling_flags; + __u32 pitch; +}; + +struct drm_radeon_gem_get_tiling { + __u32 handle; + __u32 tiling_flags; + __u32 pitch; +}; + +struct drm_radeon_gem_mmap { + __u32 handle; + __u32 pad; + __u64 offset; + __u64 size; + __u64 addr_ptr; +}; + +struct drm_radeon_gem_set_domain { + __u32 handle; + __u32 read_domains; + __u32 write_domain; +}; + +struct drm_radeon_gem_wait_idle { + __u32 handle; + __u32 pad; +}; + +struct drm_radeon_gem_busy { + __u32 handle; + __u32 domain; +}; + +struct drm_radeon_gem_pread { + /** Handle for the object being read. */ + __u32 handle; + __u32 pad; + /** Offset into the object to read from */ + __u64 offset; + /** Length of data to read */ + __u64 size; + /** Pointer to write the data into. */ + /* void *, but pointers are not 32/64 compatible */ + __u64 data_ptr; +}; + +struct drm_radeon_gem_pwrite { + /** Handle for the object being written to. */ + __u32 handle; + __u32 pad; + /** Offset into the object to write to */ + __u64 offset; + /** Length of data to write */ + __u64 size; + /** Pointer to read the data from. */ + /* void *, but pointers are not 32/64 compatible */ + __u64 data_ptr; +}; + +/* Sets or returns a value associated with a buffer. */ +struct drm_radeon_gem_op { + __u32 handle; /* buffer */ + __u32 op; /* RADEON_GEM_OP_* */ + __u64 value; /* input or return value */ +}; + +#define RADEON_GEM_OP_GET_INITIAL_DOMAIN 0 +#define RADEON_GEM_OP_SET_INITIAL_DOMAIN 1 + +#define RADEON_VA_MAP 1 +#define RADEON_VA_UNMAP 2 + +#define RADEON_VA_RESULT_OK 0 +#define RADEON_VA_RESULT_ERROR 1 +#define RADEON_VA_RESULT_VA_EXIST 2 + +#define RADEON_VM_PAGE_VALID (1 << 0) +#define RADEON_VM_PAGE_READABLE (1 << 1) +#define RADEON_VM_PAGE_WRITEABLE (1 << 2) +#define RADEON_VM_PAGE_SYSTEM (1 << 3) +#define RADEON_VM_PAGE_SNOOPED (1 << 4) + +struct drm_radeon_gem_va { + __u32 handle; + __u32 operation; + __u32 vm_id; + __u32 flags; + __u64 offset; +}; + +#define RADEON_CHUNK_ID_RELOCS 0x01 +#define RADEON_CHUNK_ID_IB 0x02 +#define RADEON_CHUNK_ID_FLAGS 0x03 +#define RADEON_CHUNK_ID_CONST_IB 0x04 + +/* The first dword of RADEON_CHUNK_ID_FLAGS is a uint32 of these flags: */ +#define RADEON_CS_KEEP_TILING_FLAGS 0x01 +#define RADEON_CS_USE_VM 0x02 +#define RADEON_CS_END_OF_FRAME 0x04 /* a hint from userspace which CS is the last one */ +/* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */ +#define RADEON_CS_RING_GFX 0 +#define RADEON_CS_RING_COMPUTE 1 +#define RADEON_CS_RING_DMA 2 +#define RADEON_CS_RING_UVD 3 +#define RADEON_CS_RING_VCE 4 +/* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */ +/* 0 = normal, + = higher priority, - = lower priority */ + +struct drm_radeon_cs_chunk { + __u32 chunk_id; + __u32 length_dw; + __u64 chunk_data; +}; + +/* drm_radeon_cs_reloc.flags */ +#define RADEON_RELOC_PRIO_MASK (0xf << 0) + +struct drm_radeon_cs_reloc { + __u32 handle; + __u32 read_domains; + __u32 write_domain; + __u32 flags; +}; + +struct drm_radeon_cs { + __u32 num_chunks; + __u32 cs_id; + /* this points to __u64 * which point to cs chunks */ + __u64 chunks; + /* updates to the limits after this CS ioctl */ + __u64 gart_limit; + __u64 vram_limit; +}; + +#define RADEON_INFO_DEVICE_ID 0x00 +#define RADEON_INFO_NUM_GB_PIPES 0x01 +#define RADEON_INFO_NUM_Z_PIPES 0x02 +#define RADEON_INFO_ACCEL_WORKING 0x03 +#define RADEON_INFO_CRTC_FROM_ID 0x04 +#define RADEON_INFO_ACCEL_WORKING2 0x05 +#define RADEON_INFO_TILING_CONFIG 0x06 +#define RADEON_INFO_WANT_HYPERZ 0x07 +#define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */ +#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */ +#define RADEON_INFO_NUM_BACKENDS 0x0a /* DB/backends for r600+ - need for OQ */ +#define RADEON_INFO_NUM_TILE_PIPES 0x0b /* tile pipes for r600+ */ +#define RADEON_INFO_FUSION_GART_WORKING 0x0c /* fusion writes to GTT were broken before this */ +#define RADEON_INFO_BACKEND_MAP 0x0d /* pipe to backend map, needed by mesa */ +/* virtual address start, va < start are reserved by the kernel */ +#define RADEON_INFO_VA_START 0x0e +/* maximum size of ib using the virtual memory cs */ +#define RADEON_INFO_IB_VM_MAX_SIZE 0x0f +/* max pipes - needed for compute shaders */ +#define RADEON_INFO_MAX_PIPES 0x10 +/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ +#define RADEON_INFO_TIMESTAMP 0x11 +/* max shader engines (SE) - needed for geometry shaders, etc. */ +#define RADEON_INFO_MAX_SE 0x12 +/* max SH per SE */ +#define RADEON_INFO_MAX_SH_PER_SE 0x13 +/* fast fb access is enabled */ +#define RADEON_INFO_FASTFB_WORKING 0x14 +/* query if a RADEON_CS_RING_* submission is supported */ +#define RADEON_INFO_RING_WORKING 0x15 +/* SI tile mode array */ +#define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16 +/* query if CP DMA is supported on the compute ring */ +#define RADEON_INFO_SI_CP_DMA_COMPUTE 0x17 +/* CIK macrotile mode array */ +#define RADEON_INFO_CIK_MACROTILE_MODE_ARRAY 0x18 +/* query the number of render backends */ +#define RADEON_INFO_SI_BACKEND_ENABLED_MASK 0x19 +/* max engine clock - needed for OpenCL */ +#define RADEON_INFO_MAX_SCLK 0x1a +/* version of VCE firmware */ +#define RADEON_INFO_VCE_FW_VERSION 0x1b +/* version of VCE feedback */ +#define RADEON_INFO_VCE_FB_VERSION 0x1c +#define RADEON_INFO_NUM_BYTES_MOVED 0x1d +#define RADEON_INFO_VRAM_USAGE 0x1e +#define RADEON_INFO_GTT_USAGE 0x1f +#define RADEON_INFO_ACTIVE_CU_COUNT 0x20 +#define RADEON_INFO_CURRENT_GPU_TEMP 0x21 +#define RADEON_INFO_CURRENT_GPU_SCLK 0x22 +#define RADEON_INFO_CURRENT_GPU_MCLK 0x23 +#define RADEON_INFO_READ_REG 0x24 +#define RADEON_INFO_VA_UNMAP_WORKING 0x25 +#define RADEON_INFO_GPU_RESET_COUNTER 0x26 + +struct drm_radeon_info { + __u32 request; + __u32 pad; + __u64 value; +}; + +/* Those correspond to the tile index to use, this is to explicitly state + * the API that is implicitly defined by the tile mode array. + */ +#define SI_TILE_MODE_COLOR_LINEAR_ALIGNED 8 +#define SI_TILE_MODE_COLOR_1D 13 +#define SI_TILE_MODE_COLOR_1D_SCANOUT 9 +#define SI_TILE_MODE_COLOR_2D_8BPP 14 +#define SI_TILE_MODE_COLOR_2D_16BPP 15 +#define SI_TILE_MODE_COLOR_2D_32BPP 16 +#define SI_TILE_MODE_COLOR_2D_64BPP 17 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP 11 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP 12 +#define SI_TILE_MODE_DEPTH_STENCIL_1D 4 +#define SI_TILE_MODE_DEPTH_STENCIL_2D 0 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_2AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_4AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA 2 + +#define CIK_TILE_MODE_DEPTH_STENCIL_1D 5 + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/savage_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/savage_drm.h new file mode 100644 index 0000000..1a91234 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/savage_drm.h @@ -0,0 +1,220 @@ +/* savage_drm.h -- Public header for the savage driver + * + * Copyright 2004 Felix Kuehling + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SAVAGE_DRM_H__ +#define __SAVAGE_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef __SAVAGE_SAREA_DEFINES__ +#define __SAVAGE_SAREA_DEFINES__ + +/* 2 heaps (1 for card, 1 for agp), each divided into up to 128 + * regions, subject to a minimum region size of (1<<16) == 64k. + * + * Clients may subdivide regions internally, but when sharing between + * clients, the region size is the minimum granularity. + */ + +#define SAVAGE_CARD_HEAP 0 +#define SAVAGE_AGP_HEAP 1 +#define SAVAGE_NR_TEX_HEAPS 2 +#define SAVAGE_NR_TEX_REGIONS 16 +#define SAVAGE_LOG_MIN_TEX_REGION_SIZE 16 + +#endif /* __SAVAGE_SAREA_DEFINES__ */ + +typedef struct _drm_savage_sarea { + /* LRU lists for texture memory in agp space and on the card. + */ + struct drm_tex_region texList[SAVAGE_NR_TEX_HEAPS][SAVAGE_NR_TEX_REGIONS + + 1]; + unsigned int texAge[SAVAGE_NR_TEX_HEAPS]; + + /* Mechanism to validate card state. + */ + int ctxOwner; +} drm_savage_sarea_t, *drm_savage_sarea_ptr; + +/* Savage-specific ioctls + */ +#define DRM_SAVAGE_BCI_INIT 0x00 +#define DRM_SAVAGE_BCI_CMDBUF 0x01 +#define DRM_SAVAGE_BCI_EVENT_EMIT 0x02 +#define DRM_SAVAGE_BCI_EVENT_WAIT 0x03 + +#define DRM_IOCTL_SAVAGE_BCI_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_INIT, drm_savage_init_t) +#define DRM_IOCTL_SAVAGE_BCI_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_CMDBUF, drm_savage_cmdbuf_t) +#define DRM_IOCTL_SAVAGE_BCI_EVENT_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_EMIT, drm_savage_event_emit_t) +#define DRM_IOCTL_SAVAGE_BCI_EVENT_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_WAIT, drm_savage_event_wait_t) + +#define SAVAGE_DMA_PCI 1 +#define SAVAGE_DMA_AGP 3 +typedef struct drm_savage_init { + enum { + SAVAGE_INIT_BCI = 1, + SAVAGE_CLEANUP_BCI = 2 + } func; + unsigned int sarea_priv_offset; + + /* some parameters */ + unsigned int cob_size; + unsigned int bci_threshold_lo, bci_threshold_hi; + unsigned int dma_type; + + /* frame buffer layout */ + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + /* local textures */ + unsigned int texture_offset; + unsigned int texture_size; + + /* physical locations of non-permanent maps */ + unsigned long status_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; + unsigned long cmd_dma_offset; +} drm_savage_init_t; + +typedef union drm_savage_cmd_header drm_savage_cmd_header_t; +typedef struct drm_savage_cmdbuf { + /* command buffer in client's address space */ + drm_savage_cmd_header_t *cmd_addr; + unsigned int size; /* size of the command buffer in 64bit units */ + + unsigned int dma_idx; /* DMA buffer index to use */ + int discard; /* discard DMA buffer when done */ + /* vertex buffer in client's address space */ + unsigned int *vb_addr; + unsigned int vb_size; /* size of client vertex buffer in bytes */ + unsigned int vb_stride; /* stride of vertices in 32bit words */ + /* boxes in client's address space */ + struct drm_clip_rect *box_addr; + unsigned int nbox; /* number of clipping boxes */ +} drm_savage_cmdbuf_t; + +#define SAVAGE_WAIT_2D 0x1 /* wait for 2D idle before updating event tag */ +#define SAVAGE_WAIT_3D 0x2 /* wait for 3D idle before updating event tag */ +#define SAVAGE_WAIT_IRQ 0x4 /* emit or wait for IRQ, not implemented yet */ +typedef struct drm_savage_event { + unsigned int count; + unsigned int flags; +} drm_savage_event_emit_t, drm_savage_event_wait_t; + +/* Commands for the cmdbuf ioctl + */ +#define SAVAGE_CMD_STATE 0 /* a range of state registers */ +#define SAVAGE_CMD_DMA_PRIM 1 /* vertices from DMA buffer */ +#define SAVAGE_CMD_VB_PRIM 2 /* vertices from client vertex buffer */ +#define SAVAGE_CMD_DMA_IDX 3 /* indexed vertices from DMA buffer */ +#define SAVAGE_CMD_VB_IDX 4 /* indexed vertices client vertex buffer */ +#define SAVAGE_CMD_CLEAR 5 /* clear buffers */ +#define SAVAGE_CMD_SWAP 6 /* swap buffers */ + +/* Primitive types +*/ +#define SAVAGE_PRIM_TRILIST 0 /* triangle list */ +#define SAVAGE_PRIM_TRISTRIP 1 /* triangle strip */ +#define SAVAGE_PRIM_TRIFAN 2 /* triangle fan */ +#define SAVAGE_PRIM_TRILIST_201 3 /* reorder verts for correct flat + * shading on s3d */ + +/* Skip flags (vertex format) + */ +#define SAVAGE_SKIP_Z 0x01 +#define SAVAGE_SKIP_W 0x02 +#define SAVAGE_SKIP_C0 0x04 +#define SAVAGE_SKIP_C1 0x08 +#define SAVAGE_SKIP_S0 0x10 +#define SAVAGE_SKIP_T0 0x20 +#define SAVAGE_SKIP_ST0 0x30 +#define SAVAGE_SKIP_S1 0x40 +#define SAVAGE_SKIP_T1 0x80 +#define SAVAGE_SKIP_ST1 0xc0 +#define SAVAGE_SKIP_ALL_S3D 0x3f +#define SAVAGE_SKIP_ALL_S4 0xff + +/* Buffer names for clear command + */ +#define SAVAGE_FRONT 0x1 +#define SAVAGE_BACK 0x2 +#define SAVAGE_DEPTH 0x4 + +/* 64-bit command header + */ +union drm_savage_cmd_header { + struct { + unsigned char cmd; /* command */ + unsigned char pad0; + unsigned short pad1; + unsigned short pad2; + unsigned short pad3; + } cmd; /* generic */ + struct { + unsigned char cmd; + unsigned char global; /* need idle engine? */ + unsigned short count; /* number of consecutive registers */ + unsigned short start; /* first register */ + unsigned short pad3; + } state; /* SAVAGE_CMD_STATE */ + struct { + unsigned char cmd; + unsigned char prim; /* primitive type */ + unsigned short skip; /* vertex format (skip flags) */ + unsigned short count; /* number of vertices */ + unsigned short start; /* first vertex in DMA/vertex buffer */ + } prim; /* SAVAGE_CMD_DMA_PRIM, SAVAGE_CMD_VB_PRIM */ + struct { + unsigned char cmd; + unsigned char prim; + unsigned short skip; + unsigned short count; /* number of indices that follow */ + unsigned short pad3; + } idx; /* SAVAGE_CMD_DMA_IDX, SAVAGE_CMD_VB_IDX */ + struct { + unsigned char cmd; + unsigned char pad0; + unsigned short pad1; + unsigned int flags; + } clear0; /* SAVAGE_CMD_CLEAR */ + struct { + unsigned int mask; + unsigned int value; + } clear1; /* SAVAGE_CMD_CLEAR data */ +}; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/sis_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/sis_drm.h new file mode 100644 index 0000000..8e51bb9 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/sis_drm.h @@ -0,0 +1,77 @@ +/* sis_drv.h -- Private header for sis driver -*- linux-c -*- */ +/* + * Copyright 2005 Eric Anholt + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __SIS_DRM_H__ +#define __SIS_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* SiS specific ioctls */ +#define NOT_USED_0_3 +#define DRM_SIS_FB_ALLOC 0x04 +#define DRM_SIS_FB_FREE 0x05 +#define NOT_USED_6_12 +#define DRM_SIS_AGP_INIT 0x13 +#define DRM_SIS_AGP_ALLOC 0x14 +#define DRM_SIS_AGP_FREE 0x15 +#define DRM_SIS_FB_INIT 0x16 + +#define DRM_IOCTL_SIS_FB_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_FB_ALLOC, drm_sis_mem_t) +#define DRM_IOCTL_SIS_FB_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_FB_FREE, drm_sis_mem_t) +#define DRM_IOCTL_SIS_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_AGP_INIT, drm_sis_agp_t) +#define DRM_IOCTL_SIS_AGP_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_AGP_ALLOC, drm_sis_mem_t) +#define DRM_IOCTL_SIS_AGP_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_AGP_FREE, drm_sis_mem_t) +#define DRM_IOCTL_SIS_FB_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_FB_INIT, drm_sis_fb_t) +/* +#define DRM_IOCTL_SIS_FLIP DRM_IOW( 0x48, drm_sis_flip_t) +#define DRM_IOCTL_SIS_FLIP_INIT DRM_IO( 0x49) +#define DRM_IOCTL_SIS_FLIP_FINAL DRM_IO( 0x50) +*/ + +typedef struct { + int context; + unsigned int offset; + unsigned int size; + unsigned long free; +} drm_sis_mem_t; + +typedef struct { + unsigned int offset, size; +} drm_sis_agp_t; + +typedef struct { + unsigned int offset, size; +} drm_sis_fb_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* __SIS_DRM_H__ */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/tegra_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/tegra_drm.h new file mode 100644 index 0000000..6c07919 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/tegra_drm.h @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _TEGRA_DRM_H_ +#define _TEGRA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0) +#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1) + +/** + * struct drm_tegra_gem_create - parameters for the GEM object creation IOCTL + */ +struct drm_tegra_gem_create { + /** + * @size: + * + * The size, in bytes, of the buffer object to be created. + */ + __u64 size; + + /** + * @flags: + * + * A bitmask of flags that influence the creation of GEM objects: + * + * DRM_TEGRA_GEM_CREATE_TILED + * Use the 16x16 tiling format for this buffer. + * + * DRM_TEGRA_GEM_CREATE_BOTTOM_UP + * The buffer has a bottom-up layout. + */ + __u32 flags; + + /** + * @handle: + * + * The handle of the created GEM object. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u32 handle; +}; + +/** + * struct drm_tegra_gem_mmap - parameters for the GEM mmap IOCTL + */ +struct drm_tegra_gem_mmap { + /** + * @handle: + * + * Handle of the GEM object to obtain an mmap offset for. + */ + __u32 handle; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; + + /** + * @offset: + * + * The mmap offset for the given GEM object. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u64 offset; +}; + +/** + * struct drm_tegra_syncpt_read - parameters for the read syncpoint IOCTL + */ +struct drm_tegra_syncpt_read { + /** + * @id: + * + * ID of the syncpoint to read the current value from. + */ + __u32 id; + + /** + * @value: + * + * The current syncpoint value. Set by the kernel upon successful + * completion of the IOCTL. + */ + __u32 value; +}; + +/** + * struct drm_tegra_syncpt_incr - parameters for the increment syncpoint IOCTL + */ +struct drm_tegra_syncpt_incr { + /** + * @id: + * + * ID of the syncpoint to increment. + */ + __u32 id; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_syncpt_wait - parameters for the wait syncpoint IOCTL + */ +struct drm_tegra_syncpt_wait { + /** + * @id: + * + * ID of the syncpoint to wait on. + */ + __u32 id; + + /** + * @thresh: + * + * Threshold value for which to wait. + */ + __u32 thresh; + + /** + * @timeout: + * + * Timeout, in milliseconds, to wait. + */ + __u32 timeout; + + /** + * @value: + * + * The new syncpoint value after the wait. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u32 value; +}; + +#define DRM_TEGRA_NO_TIMEOUT (0xffffffff) + +/** + * struct drm_tegra_open_channel - parameters for the open channel IOCTL + */ +struct drm_tegra_open_channel { + /** + * @client: + * + * The client ID for this channel. + */ + __u32 client; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; + + /** + * @context: + * + * The application context of this channel. Set by the kernel upon + * successful completion of the IOCTL. This context needs to be passed + * to the DRM_TEGRA_CHANNEL_CLOSE or the DRM_TEGRA_SUBMIT IOCTLs. + */ + __u64 context; +}; + +/** + * struct drm_tegra_close_channel - parameters for the close channel IOCTL + */ +struct drm_tegra_close_channel { + /** + * @context: + * + * The application context of this channel. This is obtained from the + * DRM_TEGRA_OPEN_CHANNEL IOCTL. + */ + __u64 context; +}; + +/** + * struct drm_tegra_get_syncpt - parameters for the get syncpoint IOCTL + */ +struct drm_tegra_get_syncpt { + /** + * @context: + * + * The application context identifying the channel for which to obtain + * the syncpoint ID. + */ + __u64 context; + + /** + * @index: + * + * Index of the client syncpoint for which to obtain the ID. + */ + __u32 index; + + /** + * @id: + * + * The ID of the given syncpoint. Set by the kernel upon successful + * completion of the IOCTL. + */ + __u32 id; +}; + +/** + * struct drm_tegra_get_syncpt_base - parameters for the get wait base IOCTL + */ +struct drm_tegra_get_syncpt_base { + /** + * @context: + * + * The application context identifying for which channel to obtain the + * wait base. + */ + __u64 context; + + /** + * @syncpt: + * + * ID of the syncpoint for which to obtain the wait base. + */ + __u32 syncpt; + + /** + * @id: + * + * The ID of the wait base corresponding to the client syncpoint. Set + * by the kernel upon successful completion of the IOCTL. + */ + __u32 id; +}; + +/** + * struct drm_tegra_syncpt - syncpoint increment operation + */ +struct drm_tegra_syncpt { + /** + * @id: + * + * ID of the syncpoint to operate on. + */ + __u32 id; + + /** + * @incrs: + * + * Number of increments to perform for the syncpoint. + */ + __u32 incrs; +}; + +/** + * struct drm_tegra_cmdbuf - structure describing a command buffer + */ +struct drm_tegra_cmdbuf { + /** + * @handle: + * + * Handle to a GEM object containing the command buffer. + */ + __u32 handle; + + /** + * @offset: + * + * Offset, in bytes, into the GEM object identified by @handle at + * which the command buffer starts. + */ + __u32 offset; + + /** + * @words: + * + * Number of 32-bit words in this command buffer. + */ + __u32 words; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_reloc - GEM object relocation structure + */ +struct drm_tegra_reloc { + struct { + /** + * @cmdbuf.handle: + * + * Handle to the GEM object containing the command buffer for + * which to perform this GEM object relocation. + */ + __u32 handle; + + /** + * @cmdbuf.offset: + * + * Offset, in bytes, into the command buffer at which to + * insert the relocated address. + */ + __u32 offset; + } cmdbuf; + struct { + /** + * @target.handle: + * + * Handle to the GEM object to be relocated. + */ + __u32 handle; + + /** + * @target.offset: + * + * Offset, in bytes, into the target GEM object at which the + * relocated data starts. + */ + __u32 offset; + } target; + + /** + * @shift: + * + * The number of bits by which to shift relocated addresses. + */ + __u32 shift; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_waitchk - wait check structure + */ +struct drm_tegra_waitchk { + /** + * @handle: + * + * Handle to the GEM object containing a command stream on which to + * perform the wait check. + */ + __u32 handle; + + /** + * @offset: + * + * Offset, in bytes, of the location in the command stream to perform + * the wait check on. + */ + __u32 offset; + + /** + * @syncpt: + * + * ID of the syncpoint to wait check. + */ + __u32 syncpt; + + /** + * @thresh: + * + * Threshold value for which to check. + */ + __u32 thresh; +}; + +/** + * struct drm_tegra_submit - job submission structure + */ +struct drm_tegra_submit { + /** + * @context: + * + * The application context identifying the channel to use for the + * execution of this job. + */ + __u64 context; + + /** + * @num_syncpts: + * + * The number of syncpoints operated on by this job. This defines the + * length of the array pointed to by @syncpts. + */ + __u32 num_syncpts; + + /** + * @num_cmdbufs: + * + * The number of command buffers to execute as part of this job. This + * defines the length of the array pointed to by @cmdbufs. + */ + __u32 num_cmdbufs; + + /** + * @num_relocs: + * + * The number of relocations to perform before executing this job. + * This defines the length of the array pointed to by @relocs. + */ + __u32 num_relocs; + + /** + * @num_waitchks: + * + * The number of wait checks to perform as part of this job. This + * defines the length of the array pointed to by @waitchks. + */ + __u32 num_waitchks; + + /** + * @waitchk_mask: + * + * Bitmask of valid wait checks. + */ + __u32 waitchk_mask; + + /** + * @timeout: + * + * Timeout, in milliseconds, before this job is cancelled. + */ + __u32 timeout; + + /** + * @syncpts: + * + * A pointer to an array of &struct drm_tegra_syncpt structures that + * specify the syncpoint operations performed as part of this job. + * The number of elements in the array must be equal to the value + * given by @num_syncpts. + */ + __u64 syncpts; + + /** + * @cmdbufs: + * + * A pointer to an array of &struct drm_tegra_cmdbuf structures that + * define the command buffers to execute as part of this job. The + * number of elements in the array must be equal to the value given + * by @num_syncpts. + */ + __u64 cmdbufs; + + /** + * @relocs: + * + * A pointer to an array of &struct drm_tegra_reloc structures that + * specify the relocations that need to be performed before executing + * this job. The number of elements in the array must be equal to the + * value given by @num_relocs. + */ + __u64 relocs; + + /** + * @waitchks: + * + * A pointer to an array of &struct drm_tegra_waitchk structures that + * specify the wait checks to be performed while executing this job. + * The number of elements in the array must be equal to the value + * given by @num_waitchks. + */ + __u64 waitchks; + + /** + * @fence: + * + * The threshold of the syncpoint associated with this job after it + * has been completed. Set by the kernel upon successful completion of + * the IOCTL. This can be used with the DRM_TEGRA_SYNCPT_WAIT IOCTL to + * wait for this job to be finished. + */ + __u32 fence; + + /** + * @reserved: + * + * This field is reserved for future use. Must be 0. + */ + __u32 reserved[5]; +}; + +#define DRM_TEGRA_GEM_TILING_MODE_PITCH 0 +#define DRM_TEGRA_GEM_TILING_MODE_TILED 1 +#define DRM_TEGRA_GEM_TILING_MODE_BLOCK 2 + +/** + * struct drm_tegra_gem_set_tiling - parameters for the set tiling IOCTL + */ +struct drm_tegra_gem_set_tiling { + /** + * @handle: + * + * Handle to the GEM object for which to set the tiling parameters. + */ + __u32 handle; + + /** + * @mode: + * + * The tiling mode to set. Must be one of: + * + * DRM_TEGRA_GEM_TILING_MODE_PITCH + * pitch linear format + * + * DRM_TEGRA_GEM_TILING_MODE_TILED + * 16x16 tiling format + * + * DRM_TEGRA_GEM_TILING_MODE_BLOCK + * 16Bx2 tiling format + */ + __u32 mode; + + /** + * @value: + * + * The value to set for the tiling mode parameter. + */ + __u32 value; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_gem_get_tiling - parameters for the get tiling IOCTL + */ +struct drm_tegra_gem_get_tiling { + /** + * @handle: + * + * Handle to the GEM object for which to query the tiling parameters. + */ + __u32 handle; + + /** + * @mode: + * + * The tiling mode currently associated with the GEM object. Set by + * the kernel upon successful completion of the IOCTL. + */ + __u32 mode; + + /** + * @value: + * + * The tiling mode parameter currently associated with the GEM object. + * Set by the kernel upon successful completion of the IOCTL. + */ + __u32 value; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +#define DRM_TEGRA_GEM_BOTTOM_UP (1 << 0) +#define DRM_TEGRA_GEM_FLAGS (DRM_TEGRA_GEM_BOTTOM_UP) + +/** + * struct drm_tegra_gem_set_flags - parameters for the set flags IOCTL + */ +struct drm_tegra_gem_set_flags { + /** + * @handle: + * + * Handle to the GEM object for which to set the flags. + */ + __u32 handle; + + /** + * @flags: + * + * The flags to set for the GEM object. + */ + __u32 flags; +}; + +/** + * struct drm_tegra_gem_get_flags - parameters for the get flags IOCTL + */ +struct drm_tegra_gem_get_flags { + /** + * @handle: + * + * Handle to the GEM object for which to query the flags. + */ + __u32 handle; + + /** + * @flags: + * + * The flags currently associated with the GEM object. Set by the + * kernel upon successful completion of the IOCTL. + */ + __u32 flags; +}; + +#define DRM_TEGRA_GEM_CREATE 0x00 +#define DRM_TEGRA_GEM_MMAP 0x01 +#define DRM_TEGRA_SYNCPT_READ 0x02 +#define DRM_TEGRA_SYNCPT_INCR 0x03 +#define DRM_TEGRA_SYNCPT_WAIT 0x04 +#define DRM_TEGRA_OPEN_CHANNEL 0x05 +#define DRM_TEGRA_CLOSE_CHANNEL 0x06 +#define DRM_TEGRA_GET_SYNCPT 0x07 +#define DRM_TEGRA_SUBMIT 0x08 +#define DRM_TEGRA_GET_SYNCPT_BASE 0x09 +#define DRM_TEGRA_GEM_SET_TILING 0x0a +#define DRM_TEGRA_GEM_GET_TILING 0x0b +#define DRM_TEGRA_GEM_SET_FLAGS 0x0c +#define DRM_TEGRA_GEM_GET_FLAGS 0x0d + +#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create) +#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap) +#define DRM_IOCTL_TEGRA_SYNCPT_READ DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_READ, struct drm_tegra_syncpt_read) +#define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr) +#define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait) +#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel) +#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_close_channel) +#define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt) +#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit) +#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base) +#define DRM_IOCTL_TEGRA_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_TILING, struct drm_tegra_gem_set_tiling) +#define DRM_IOCTL_TEGRA_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_TILING, struct drm_tegra_gem_get_tiling) +#define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags) +#define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/vc4_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/vc4_drm.h new file mode 100644 index 0000000..31f50de --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/vc4_drm.h @@ -0,0 +1,442 @@ +/* + * Copyright © 2014-2015 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _VC4_DRM_H_ +#define _VC4_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_VC4_SUBMIT_CL 0x00 +#define DRM_VC4_WAIT_SEQNO 0x01 +#define DRM_VC4_WAIT_BO 0x02 +#define DRM_VC4_CREATE_BO 0x03 +#define DRM_VC4_MMAP_BO 0x04 +#define DRM_VC4_CREATE_SHADER_BO 0x05 +#define DRM_VC4_GET_HANG_STATE 0x06 +#define DRM_VC4_GET_PARAM 0x07 +#define DRM_VC4_SET_TILING 0x08 +#define DRM_VC4_GET_TILING 0x09 +#define DRM_VC4_LABEL_BO 0x0a +#define DRM_VC4_GEM_MADVISE 0x0b +#define DRM_VC4_PERFMON_CREATE 0x0c +#define DRM_VC4_PERFMON_DESTROY 0x0d +#define DRM_VC4_PERFMON_GET_VALUES 0x0e + +#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) +#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) +#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) +#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) +#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) +#define DRM_IOCTL_VC4_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_PARAM, struct drm_vc4_get_param) +#define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) +#define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) +#define DRM_IOCTL_VC4_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_LABEL_BO, struct drm_vc4_label_bo) +#define DRM_IOCTL_VC4_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GEM_MADVISE, struct drm_vc4_gem_madvise) +#define DRM_IOCTL_VC4_PERFMON_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_CREATE, struct drm_vc4_perfmon_create) +#define DRM_IOCTL_VC4_PERFMON_DESTROY DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_DESTROY, struct drm_vc4_perfmon_destroy) +#define DRM_IOCTL_VC4_PERFMON_GET_VALUES DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_GET_VALUES, struct drm_vc4_perfmon_get_values) + +struct drm_vc4_submit_rcl_surface { + __u32 hindex; /* Handle index, or ~0 if not present. */ + __u32 offset; /* Offset to start of buffer. */ + /* + * Bits for either render config (color_write) or load/store packet. + * Bits should all be 0 for MSAA load/stores. + */ + __u16 bits; + +#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) + __u16 flags; +}; + +/** + * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D + * engine. + * + * Drivers typically use GPU BOs to store batchbuffers / command lists and + * their associated state. However, because the VC4 lacks an MMU, we have to + * do validation of memory accesses by the GPU commands. If we were to store + * our commands in BOs, we'd need to do uncached readback from them to do the + * validation process, which is too expensive. Instead, userspace accumulates + * commands and associated state in plain memory, then the kernel copies the + * data to its own address space, and then validates and stores it in a GPU + * BO. + */ +struct drm_vc4_submit_cl { + /* Pointer to the binner command list. + * + * This is the first set of commands executed, which runs the + * coordinate shader to determine where primitives land on the screen, + * then writes out the state updates and draw calls necessary per tile + * to the tile allocation BO. + */ + __u64 bin_cl; + + /* Pointer to the shader records. + * + * Shader records are the structures read by the hardware that contain + * pointers to uniforms, shaders, and vertex attributes. The + * reference to the shader record has enough information to determine + * how many pointers are necessary (fixed number for shaders/uniforms, + * and an attribute count), so those BO indices into bo_handles are + * just stored as __u32s before each shader record passed in. + */ + __u64 shader_rec; + + /* Pointer to uniform data and texture handles for the textures + * referenced by the shader. + * + * For each shader state record, there is a set of uniform data in the + * order referenced by the record (FS, VS, then CS). Each set of + * uniform data has a __u32 index into bo_handles per texture + * sample operation, in the order the QPU_W_TMUn_S writes appear in + * the program. Following the texture BO handle indices is the actual + * uniform data. + * + * The individual uniform state blocks don't have sizes passed in, + * because the kernel has to determine the sizes anyway during shader + * code validation. + */ + __u64 uniforms; + __u64 bo_handles; + + /* Size in bytes of the binner command list. */ + __u32 bin_cl_size; + /* Size in bytes of the set of shader records. */ + __u32 shader_rec_size; + /* Number of shader records. + * + * This could just be computed from the contents of shader_records and + * the address bits of references to them from the bin CL, but it + * keeps the kernel from having to resize some allocations it makes. + */ + __u32 shader_rec_count; + /* Size in bytes of the uniform state. */ + __u32 uniforms_size; + + /* Number of BO handles passed in (size is that times 4). */ + __u32 bo_handle_count; + + /* RCL setup: */ + __u16 width; + __u16 height; + __u8 min_x_tile; + __u8 min_y_tile; + __u8 max_x_tile; + __u8 max_y_tile; + struct drm_vc4_submit_rcl_surface color_read; + struct drm_vc4_submit_rcl_surface color_write; + struct drm_vc4_submit_rcl_surface zs_read; + struct drm_vc4_submit_rcl_surface zs_write; + struct drm_vc4_submit_rcl_surface msaa_color_write; + struct drm_vc4_submit_rcl_surface msaa_zs_write; + __u32 clear_color[2]; + __u32 clear_z; + __u8 clear_s; + + __u32 pad:24; + +#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) +/* By default, the kernel gets to choose the order that the tiles are + * rendered in. If this is set, then the tiles will be rendered in a + * raster order, with the right-to-left vs left-to-right and + * top-to-bottom vs bottom-to-top dictated by + * VC4_SUBMIT_CL_RCL_ORDER_INCREASING_*. This allows overlapping + * blits to be implemented using the 3D engine. + */ +#define VC4_SUBMIT_CL_FIXED_RCL_ORDER (1 << 1) +#define VC4_SUBMIT_CL_RCL_ORDER_INCREASING_X (1 << 2) +#define VC4_SUBMIT_CL_RCL_ORDER_INCREASING_Y (1 << 3) + __u32 flags; + + /* Returned value of the seqno of this render job (for the + * wait ioctl). + */ + __u64 seqno; + + /* ID of the perfmon to attach to this job. 0 means no perfmon. */ + __u32 perfmonid; + + /* Syncobj handle to wait on. If set, processing of this render job + * will not start until the syncobj is signaled. 0 means ignore. + */ + __u32 in_sync; + + /* Syncobj handle to export fence to. If set, the fence in the syncobj + * will be replaced with a fence that signals upon completion of this + * render job. 0 means ignore. + */ + __u32 out_sync; + + __u32 pad2; +}; + +/** + * struct drm_vc4_wait_seqno - ioctl argument for waiting for + * DRM_VC4_SUBMIT_CL completion using its returned seqno. + * + * timeout_ns is the timeout in nanoseconds, where "0" means "don't + * block, just return the status." + */ +struct drm_vc4_wait_seqno { + __u64 seqno; + __u64 timeout_ns; +}; + +/** + * struct drm_vc4_wait_bo - ioctl argument for waiting for + * completion of the last DRM_VC4_SUBMIT_CL on a BO. + * + * This is useful for cases where multiple processes might be + * rendering to a BO and you want to wait for all rendering to be + * completed. + */ +struct drm_vc4_wait_bo { + __u32 handle; + __u32 pad; + __u64 timeout_ns; +}; + +/** + * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_create_bo { + __u32 size; + __u32 flags; + /** Returned GEM handle for the BO. */ + __u32 handle; + __u32 pad; +}; + +/** + * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. + * + * This doesn't actually perform an mmap. Instead, it returns the + * offset you need to use in an mmap on the DRM device node. This + * means that tools like valgrind end up knowing about the mapped + * memory. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_mmap_bo { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 flags; + /** offset into the drm node to use for subsequent mmap call. */ + __u64 offset; +}; + +/** + * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 + * shader BOs. + * + * Since allowing a shader to be overwritten while it's also being + * executed from would allow privlege escalation, shaders must be + * created using this ioctl, and they can't be mmapped later. + */ +struct drm_vc4_create_shader_bo { + /* Size of the data argument. */ + __u32 size; + /* Flags, currently must be 0. */ + __u32 flags; + + /* Pointer to the data. */ + __u64 data; + + /** Returned GEM handle for the BO. */ + __u32 handle; + /* Pad, must be 0. */ + __u32 pad; +}; + +struct drm_vc4_get_hang_state_bo { + __u32 handle; + __u32 paddr; + __u32 size; + __u32 pad; +}; + +/** + * struct drm_vc4_hang_state - ioctl argument for collecting state + * from a GPU hang for analysis. +*/ +struct drm_vc4_get_hang_state { + /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ + __u64 bo; + /** + * On input, the size of the bo array. Output is the number + * of bos to be returned. + */ + __u32 bo_count; + + __u32 start_bin, start_render; + + __u32 ct0ca, ct0ea; + __u32 ct1ca, ct1ea; + __u32 ct0cs, ct1cs; + __u32 ct0ra0, ct1ra0; + + __u32 bpca, bpcs; + __u32 bpoa, bpos; + + __u32 vpmbase; + + __u32 dbge; + __u32 fdbgo; + __u32 fdbgb; + __u32 fdbgr; + __u32 fdbgs; + __u32 errstat; + + /* Pad that we may save more registers into in the future. */ + __u32 pad[16]; +}; + +#define DRM_VC4_PARAM_V3D_IDENT0 0 +#define DRM_VC4_PARAM_V3D_IDENT1 1 +#define DRM_VC4_PARAM_V3D_IDENT2 2 +#define DRM_VC4_PARAM_SUPPORTS_BRANCHES 3 +#define DRM_VC4_PARAM_SUPPORTS_ETC1 4 +#define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 +#define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 +#define DRM_VC4_PARAM_SUPPORTS_MADVISE 7 +#define DRM_VC4_PARAM_SUPPORTS_PERFMON 8 + +struct drm_vc4_get_param { + __u32 param; + __u32 pad; + __u64 value; +}; + +struct drm_vc4_get_tiling { + __u32 handle; + __u32 flags; + __u64 modifier; +}; + +struct drm_vc4_set_tiling { + __u32 handle; + __u32 flags; + __u64 modifier; +}; + +/** + * struct drm_vc4_label_bo - Attach a name to a BO for debug purposes. + */ +struct drm_vc4_label_bo { + __u32 handle; + __u32 len; + __u64 name; +}; + +/* + * States prefixed with '__' are internal states and cannot be passed to the + * DRM_IOCTL_VC4_GEM_MADVISE ioctl. + */ +#define VC4_MADV_WILLNEED 0 +#define VC4_MADV_DONTNEED 1 +#define __VC4_MADV_PURGED 2 +#define __VC4_MADV_NOTSUPP 3 + +struct drm_vc4_gem_madvise { + __u32 handle; + __u32 madv; + __u32 retained; + __u32 pad; +}; + +enum { + VC4_PERFCNT_FEP_VALID_PRIMS_NO_RENDER, + VC4_PERFCNT_FEP_VALID_PRIMS_RENDER, + VC4_PERFCNT_FEP_CLIPPED_QUADS, + VC4_PERFCNT_FEP_VALID_QUADS, + VC4_PERFCNT_TLB_QUADS_NOT_PASSING_STENCIL, + VC4_PERFCNT_TLB_QUADS_NOT_PASSING_Z_AND_STENCIL, + VC4_PERFCNT_TLB_QUADS_PASSING_Z_AND_STENCIL, + VC4_PERFCNT_TLB_QUADS_ZERO_COVERAGE, + VC4_PERFCNT_TLB_QUADS_NON_ZERO_COVERAGE, + VC4_PERFCNT_TLB_QUADS_WRITTEN_TO_COLOR_BUF, + VC4_PERFCNT_PLB_PRIMS_OUTSIDE_VIEWPORT, + VC4_PERFCNT_PLB_PRIMS_NEED_CLIPPING, + VC4_PERFCNT_PSE_PRIMS_REVERSED, + VC4_PERFCNT_QPU_TOTAL_IDLE_CYCLES, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_VERTEX_COORD_SHADING, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_FRAGMENT_SHADING, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_EXEC_VALID_INST, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_TMUS, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_SCOREBOARD, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_VARYINGS, + VC4_PERFCNT_QPU_TOTAL_INST_CACHE_HIT, + VC4_PERFCNT_QPU_TOTAL_INST_CACHE_MISS, + VC4_PERFCNT_QPU_TOTAL_UNIFORM_CACHE_HIT, + VC4_PERFCNT_QPU_TOTAL_UNIFORM_CACHE_MISS, + VC4_PERFCNT_TMU_TOTAL_TEXT_QUADS_PROCESSED, + VC4_PERFCNT_TMU_TOTAL_TEXT_CACHE_MISS, + VC4_PERFCNT_VPM_TOTAL_CLK_CYCLES_VDW_STALLED, + VC4_PERFCNT_VPM_TOTAL_CLK_CYCLES_VCD_STALLED, + VC4_PERFCNT_L2C_TOTAL_L2_CACHE_HIT, + VC4_PERFCNT_L2C_TOTAL_L2_CACHE_MISS, + VC4_PERFCNT_NUM_EVENTS, +}; + +#define DRM_VC4_MAX_PERF_COUNTERS 16 + +struct drm_vc4_perfmon_create { + __u32 id; + __u32 ncounters; + __u8 events[DRM_VC4_MAX_PERF_COUNTERS]; +}; + +struct drm_vc4_perfmon_destroy { + __u32 id; +}; + +/* + * Returns the values of the performance counters tracked by this + * perfmon (as an array of ncounters u64 values). + * + * No implicit synchronization is performed, so the user has to + * guarantee that any jobs using this perfmon have already been + * completed (probably by blocking on the seqno returned by the + * last exec that used the perfmon). + */ +struct drm_vc4_perfmon_get_values { + __u32 id; + __u64 values_ptr; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VC4_DRM_H_ */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/via_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/via_drm.h new file mode 100644 index 0000000..8b69e81 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/via_drm.h @@ -0,0 +1,283 @@ +/* + * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef _VIA_DRM_H_ +#define _VIA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _VIA_DEFINES_ +#define _VIA_DEFINES_ + +#include "via_drmclient.h" + +#define VIA_NR_SAREA_CLIPRECTS 8 +#define VIA_NR_XVMC_PORTS 10 +#define VIA_NR_XVMC_LOCKS 5 +#define VIA_MAX_CACHELINE_SIZE 64 +#define XVMCLOCKPTR(saPriv,lockNo) \ + ((__volatile__ struct drm_hw_lock *)(((((unsigned long) (saPriv)->XvMCLockArea) + \ + (VIA_MAX_CACHELINE_SIZE - 1)) & \ + ~(VIA_MAX_CACHELINE_SIZE - 1)) + \ + VIA_MAX_CACHELINE_SIZE*(lockNo))) + +/* Each region is a minimum of 64k, and there are at most 64 of them. + */ +#define VIA_NR_TEX_REGIONS 64 +#define VIA_LOG_MIN_TEX_REGION_SIZE 16 +#endif + +#define VIA_UPLOAD_TEX0IMAGE 0x1 /* handled clientside */ +#define VIA_UPLOAD_TEX1IMAGE 0x2 /* handled clientside */ +#define VIA_UPLOAD_CTX 0x4 +#define VIA_UPLOAD_BUFFERS 0x8 +#define VIA_UPLOAD_TEX0 0x10 +#define VIA_UPLOAD_TEX1 0x20 +#define VIA_UPLOAD_CLIPRECTS 0x40 +#define VIA_UPLOAD_ALL 0xff + +/* VIA specific ioctls */ +#define DRM_VIA_ALLOCMEM 0x00 +#define DRM_VIA_FREEMEM 0x01 +#define DRM_VIA_AGP_INIT 0x02 +#define DRM_VIA_FB_INIT 0x03 +#define DRM_VIA_MAP_INIT 0x04 +#define DRM_VIA_DEC_FUTEX 0x05 +#define NOT_USED +#define DRM_VIA_DMA_INIT 0x07 +#define DRM_VIA_CMDBUFFER 0x08 +#define DRM_VIA_FLUSH 0x09 +#define DRM_VIA_PCICMD 0x0a +#define DRM_VIA_CMDBUF_SIZE 0x0b +#define NOT_USED +#define DRM_VIA_WAIT_IRQ 0x0d +#define DRM_VIA_DMA_BLIT 0x0e +#define DRM_VIA_BLIT_SYNC 0x0f + +#define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_AGP_INIT, drm_via_agp_t) +#define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) +#define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) +#define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) +#define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) +#define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) +#define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ + drm_via_cmdbuf_size_t) +#define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) +#define DRM_IOCTL_VIA_DMA_BLIT DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_DMA_BLIT, drm_via_dmablit_t) +#define DRM_IOCTL_VIA_BLIT_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_BLIT_SYNC, drm_via_blitsync_t) + +/* Indices into buf.Setup where various bits of state are mirrored per + * context and per buffer. These can be fired at the card as a unit, + * or in a piecewise fashion as required. + */ + +#define VIA_TEX_SETUP_SIZE 8 + +/* Flags for clear ioctl + */ +#define VIA_FRONT 0x1 +#define VIA_BACK 0x2 +#define VIA_DEPTH 0x4 +#define VIA_STENCIL 0x8 +#define VIA_MEM_VIDEO 0 /* matches drm constant */ +#define VIA_MEM_AGP 1 /* matches drm constant */ +#define VIA_MEM_SYSTEM 2 +#define VIA_MEM_MIXED 3 +#define VIA_MEM_UNKNOWN 4 + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_agp_t; + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_fb_t; + +typedef struct { + __u32 context; + __u32 type; + __u32 size; + unsigned long index; + unsigned long offset; +} drm_via_mem_t; + +typedef struct _drm_via_init { + enum { + VIA_INIT_MAP = 0x01, + VIA_CLEANUP_MAP = 0x02 + } func; + + unsigned long sarea_priv_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long agpAddr; +} drm_via_init_t; + +typedef struct _drm_via_futex { + enum { + VIA_FUTEX_WAIT = 0x00, + VIA_FUTEX_WAKE = 0X01 + } func; + __u32 ms; + __u32 lock; + __u32 val; +} drm_via_futex_t; + +typedef struct _drm_via_dma_init { + enum { + VIA_INIT_DMA = 0x01, + VIA_CLEANUP_DMA = 0x02, + VIA_DMA_INITIALIZED = 0x03 + } func; + + unsigned long offset; + unsigned long size; + unsigned long reg_pause_addr; +} drm_via_dma_init_t; + +typedef struct _drm_via_cmdbuffer { + char *buf; + unsigned long size; +} drm_via_cmdbuffer_t; + +/* Warning: If you change the SAREA structure you must change the Xserver + * structure as well */ + +typedef struct _drm_via_tex_region { + unsigned char next, prev; /* indices to form a circular LRU */ + unsigned char inUse; /* owned by a client, or free? */ + int age; /* tracked by clients to update local LRU's */ +} drm_via_tex_region_t; + +typedef struct _drm_via_sarea { + unsigned int dirty; + unsigned int nbox; + struct drm_clip_rect boxes[VIA_NR_SAREA_CLIPRECTS]; + drm_via_tex_region_t texList[VIA_NR_TEX_REGIONS + 1]; + int texAge; /* last time texture was uploaded */ + int ctxOwner; /* last context to upload state */ + int vertexPrim; + + /* + * Below is for XvMC. + * We want the lock integers alone on, and aligned to, a cache line. + * Therefore this somewhat strange construct. + */ + + char XvMCLockArea[VIA_MAX_CACHELINE_SIZE * (VIA_NR_XVMC_LOCKS + 1)]; + + unsigned int XvMCDisplaying[VIA_NR_XVMC_PORTS]; + unsigned int XvMCSubPicOn[VIA_NR_XVMC_PORTS]; + unsigned int XvMCCtxNoGrabbed; /* Last context to hold decoder */ + + /* Used by the 3d driver only at this point, for pageflipping: + */ + unsigned int pfCurrentOffset; +} drm_via_sarea_t; + +typedef struct _drm_via_cmdbuf_size { + enum { + VIA_CMDBUF_SPACE = 0x01, + VIA_CMDBUF_LAG = 0x02 + } func; + int wait; + __u32 size; +} drm_via_cmdbuf_size_t; + +typedef enum { + VIA_IRQ_ABSOLUTE = 0x0, + VIA_IRQ_RELATIVE = 0x1, + VIA_IRQ_SIGNAL = 0x10000000, + VIA_IRQ_FORCE_SEQUENCE = 0x20000000 +} via_irq_seq_type_t; + +#define VIA_IRQ_FLAGS_MASK 0xF0000000 + +enum drm_via_irqs { + drm_via_irq_hqv0 = 0, + drm_via_irq_hqv1, + drm_via_irq_dma0_dd, + drm_via_irq_dma0_td, + drm_via_irq_dma1_dd, + drm_via_irq_dma1_td, + drm_via_irq_num +}; + +struct drm_via_wait_irq_request { + unsigned irq; + via_irq_seq_type_t type; + __u32 sequence; + __u32 signal; +}; + +typedef union drm_via_irqwait { + struct drm_via_wait_irq_request request; + struct drm_wait_vblank_reply reply; +} drm_via_irqwait_t; + +typedef struct drm_via_blitsync { + __u32 sync_handle; + unsigned engine; +} drm_via_blitsync_t; + +/* - * Below,"flags" is currently unused but will be used for possible future + * extensions like kernel space bounce buffers for bad alignments and + * blit engine busy-wait polling for better latency in the absence of + * interrupts. + */ + +typedef struct drm_via_dmablit { + __u32 num_lines; + __u32 line_length; + + __u32 fb_addr; + __u32 fb_stride; + + unsigned char *mem_addr; + __u32 mem_stride; + + __u32 flags; + int to_fb; + + drm_via_blitsync_t sync; +} drm_via_dmablit_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VIA_DRM_H_ */ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/virtgpu_drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/virtgpu_drm.h new file mode 100644 index 0000000..f06a789 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/virtgpu_drm.h @@ -0,0 +1,182 @@ +/* + * Copyright 2013 Red Hat + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VIRTGPU_DRM_H +#define VIRTGPU_DRM_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + * + * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel + * compatibility Keep fields aligned to their size + */ + +#define DRM_VIRTGPU_MAP 0x01 +#define DRM_VIRTGPU_EXECBUFFER 0x02 +#define DRM_VIRTGPU_GETPARAM 0x03 +#define DRM_VIRTGPU_RESOURCE_CREATE 0x04 +#define DRM_VIRTGPU_RESOURCE_INFO 0x05 +#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06 +#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07 +#define DRM_VIRTGPU_WAIT 0x08 +#define DRM_VIRTGPU_GET_CAPS 0x09 + +#define VIRTGPU_EXECBUF_FENCE_FD_IN 0x01 +#define VIRTGPU_EXECBUF_FENCE_FD_OUT 0x02 +#define VIRTGPU_EXECBUF_FLAGS (\ + VIRTGPU_EXECBUF_FENCE_FD_IN |\ + VIRTGPU_EXECBUF_FENCE_FD_OUT |\ + 0) + +struct drm_virtgpu_map { + __u64 offset; /* use for mmap system call */ + __u32 handle; + __u32 pad; +}; + +struct drm_virtgpu_execbuffer { + __u32 flags; + __u32 size; + __u64 command; /* void* */ + __u64 bo_handles; + __u32 num_bo_handles; + __s32 fence_fd; /* in/out fence fd (see VIRTGPU_EXECBUF_FENCE_FD_IN/OUT) */ +}; + +#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */ +#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */ + +struct drm_virtgpu_getparam { + __u64 param; + __u64 value; +}; + +/* NO_BO flags? NO resource flag? */ +/* resource flag for y_0_top */ +struct drm_virtgpu_resource_create { + __u32 target; + __u32 format; + __u32 bind; + __u32 width; + __u32 height; + __u32 depth; + __u32 array_size; + __u32 last_level; + __u32 nr_samples; + __u32 flags; + __u32 bo_handle; /* if this is set - recreate a new resource attached to this bo ? */ + __u32 res_handle; /* returned by kernel */ + __u32 size; /* validate transfer in the host */ + __u32 stride; /* validate transfer in the host */ +}; + +struct drm_virtgpu_resource_info { + __u32 bo_handle; + __u32 res_handle; + __u32 size; + __u32 stride; +}; + +struct drm_virtgpu_3d_box { + __u32 x; + __u32 y; + __u32 z; + __u32 w; + __u32 h; + __u32 d; +}; + +struct drm_virtgpu_3d_transfer_to_host { + __u32 bo_handle; + struct drm_virtgpu_3d_box box; + __u32 level; + __u32 offset; +}; + +struct drm_virtgpu_3d_transfer_from_host { + __u32 bo_handle; + struct drm_virtgpu_3d_box box; + __u32 level; + __u32 offset; +}; + +#define VIRTGPU_WAIT_NOWAIT 1 /* like it */ +struct drm_virtgpu_3d_wait { + __u32 handle; /* 0 is an invalid handle */ + __u32 flags; +}; + +struct drm_virtgpu_get_caps { + __u32 cap_set_id; + __u32 cap_set_ver; + __u64 addr; + __u32 size; + __u32 pad; +}; + +#define DRM_IOCTL_VIRTGPU_MAP \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map) + +#define DRM_IOCTL_VIRTGPU_EXECBUFFER \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\ + struct drm_virtgpu_execbuffer) + +#define DRM_IOCTL_VIRTGPU_GETPARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\ + struct drm_virtgpu_getparam) + +#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE, \ + struct drm_virtgpu_resource_create) + +#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \ + struct drm_virtgpu_resource_info) + +#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST, \ + struct drm_virtgpu_3d_transfer_from_host) + +#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST, \ + struct drm_virtgpu_3d_transfer_to_host) + +#define DRM_IOCTL_VIRTGPU_WAIT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT, \ + struct drm_virtgpu_3d_wait) + +#define DRM_IOCTL_VIRTGPU_GET_CAPS \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \ + struct drm_virtgpu_get_caps) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/tool.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/tool.h new file mode 100644 index 0000000..c7b334d --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/tool.h @@ -0,0 +1,145 @@ +/* + + */ + +#ifndef TOOL_H_ +#define TOOL_H_ +//ڴ˴ͷļ +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +#ifdef __cplusplus +extern "C" { +#endif +#define YUV420 0 +#define YUV422 YUV420 + 1 +#define YUV444 YUV422 + 1 +/************************************************************* +Function: ReadBmpFile +Description: ȡbmpͼڴ +Input: pFilePathbmp· + pDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgrrgbֵΪ8bit + widthͼ + heightͼ +Output: +*************************************************************/ +extern void ReadBmpFile(char *pFilePath, unsigned char *pData, int & width, int & height); +/************************************************************* +Function: SaveBmpFile +Description: rgbͼݱΪbmp +Input: pFilePathbmp· + pDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgrrgbֵΪ8bit + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveBmpFile(char *pFilePath, unsigned char *pData, int width, int height); +/************************************************************* +Function: SaveRaw +Description: rawͼ +Input: pSavePathraw· + pDataraw + widthrawͼ + heightrawͼ +Output: +*************************************************************/ +extern void SaveRaw(char *pSavePath, short *pRawData, int width, int height); + +extern void SaveRaw32bit(char *pSavePath, long *pRawData, int width, int height); + +/************************************************************* +Function: SaveBmpFile2 +Description: λ8bitbmpͼ +Input: pFilePathbmp· + widthͼ + heightͼ + bitValueͼλ + pRGBDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgr +Output: +*************************************************************/ +extern void SaveBmpFile2(char *pFilePath, int width, int height, int bitValue, short *pRGBData); + +/************************************************************* +Function: SaveYUVData +Description: 8bit YUVͼ +Input: pSavePath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData(char *pSavePath, unsigned char *pData, int width, int height); + + + +/************************************************************* +Function: SaveYUVData2 +Description: λ8bitYUVͼ +Input: pSavePath· + pDatayuvݣλ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData2(char *pSavePath, short *pData, int width, int height, int bitValue); +/************************************************************* +Function: SaveYUVData1 +Description: 8bit YUV420ͼ +Input: pSavePath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData1(char *pSavePath, unsigned char *pData, int width, int height, int fmt); +/************************************************************* +Function: ReadYUVData1 +Description: ȡ8bit YUV420ͼ +Input: pReadPath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void ReadYUVData1(char *pReadPath, unsigned char *pData, int width, int height, int fmt); +/************************************************************* +Function: Yuvfmtconv +Description: yuv fmt conversion.444 420 422 to 444 420 422 +Input: pDatain 뻺 + pDataout + width + height + fmt_in ʽ + fmt_out ʽ +Output: +*************************************************************/ +extern void Yuvfmtconv(void *pDatain, void *pDataout, int width, int height, int fmt_in, int fmt_out, int size); +/************************************************************* +Function: Yuvbitstochar +Description: save yuv to 8 bitdepth +Input: pDatain 뻺 + pDataout + size yuv + height λ +Output: +*************************************************************/ +extern void Yuvbitstochar(short *pDatain, unsigned char *pDataout, int size, int bitdepth); + +/************************************************************* +Function: SaveCfaBmp +Description: rawcfaͼ +Input: pRawDatarawͼ + widthrawͼ + heightrawͼߣ + bayerPatternbayer patternʽȡֵΧ[03] + bitValuerawλ +Output: +*************************************************************/ +extern void SaveCfaBmp(char *pFilePath, short *pRawData, int width, int height, int bayerPattern, int bitValue); + +#ifdef __cplusplus +} +#endif + +#endif // TOOL_H_ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/xf86drm.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/xf86drm.h new file mode 100644 index 0000000..d86428a --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/xf86drm.h @@ -0,0 +1,930 @@ +/** + * \file xf86drm.h + * OS-independent header for DRM user-level library interface. + * + * \author Rickard E. (Rik) Faith + */ + +/* + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _XF86DRM_H_ +#define _XF86DRM_H_ + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef DRM_MAX_MINOR +#define DRM_MAX_MINOR 16 +#endif + +#if defined(__linux__) + +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IOC_VOID _IOC_NONE +#define DRM_IOC_READ _IOC_READ +#define DRM_IOC_WRITE _IOC_WRITE +#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#else /* One of the *BSDs */ + +#include +#define DRM_IOCTL_NR(n) ((n) & 0xff) +#define DRM_IOC_VOID IOC_VOID +#define DRM_IOC_READ IOC_OUT +#define DRM_IOC_WRITE IOC_IN +#define DRM_IOC_READWRITE IOC_INOUT +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#endif + + /* Defaults, if nothing set in xf86config */ +#define DRM_DEV_UID 0 +#define DRM_DEV_GID 0 +/* Default /dev/dri directory permissions 0755 */ +#define DRM_DEV_DIRMODE \ + (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) +#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) + +#ifdef __OpenBSD__ +#define DRM_DIR_NAME "/dev" +#define DRM_PRIMARY_MINOR_NAME "drm" +#define DRM_CONTROL_MINOR_NAME "drmC" +#define DRM_RENDER_MINOR_NAME "drmR" +#else +#define DRM_DIR_NAME "/dev/dri" +#define DRM_PRIMARY_MINOR_NAME "card" +#define DRM_CONTROL_MINOR_NAME "controlD" +#define DRM_RENDER_MINOR_NAME "renderD" +#define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */ +#endif + +#define DRM_DEV_NAME "%s/" DRM_PRIMARY_MINOR_NAME "%d" +#define DRM_CONTROL_DEV_NAME "%s/" DRM_CONTROL_MINOR_NAME "%d" +#define DRM_RENDER_DEV_NAME "%s/" DRM_RENDER_MINOR_NAME "%d" + +#define DRM_NODE_NAME_MAX \ + (sizeof(DRM_DIR_NAME) + 1 /* slash */ \ + + MAX3(sizeof(DRM_PRIMARY_MINOR_NAME), \ + sizeof(DRM_CONTROL_MINOR_NAME), \ + sizeof(DRM_RENDER_MINOR_NAME)) \ + + sizeof("144") /* highest possible node number */ \ + + 1) /* NULL-terminator */ + +#define DRM_ERR_NO_DEVICE (-1001) +#define DRM_ERR_NO_ACCESS (-1002) +#define DRM_ERR_NOT_ROOT (-1003) +#define DRM_ERR_INVALID (-1004) +#define DRM_ERR_NO_FD (-1005) + +#define DRM_AGP_NO_HANDLE 0 + +typedef unsigned int drmSize, *drmSizePtr; /**< For mapped regions */ +typedef void *drmAddress, **drmAddressPtr; /**< For mapped regions */ + +#if (__GNUC__ >= 3) +#define DRM_PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a))) +#else +#define DRM_PRINTFLIKE(f, a) +#endif + +typedef struct _drmServerInfo { + int (*debug_print)(const char *format, va_list ap) DRM_PRINTFLIKE(1,0); + int (*load_module)(const char *name); + void (*get_perms)(gid_t *, mode_t *); +} drmServerInfo, *drmServerInfoPtr; + +typedef struct drmHashEntry { + int fd; + void (*f)(int, void *, void *); + void *tagTable; +} drmHashEntry; + +extern int drmIoctl(int fd, unsigned long request, void *arg); +extern void *drmGetHashTable(void); +extern drmHashEntry *drmGetEntry(int fd); + +/** + * Driver version information. + * + * \sa drmGetVersion() and drmSetVersion(). + */ +typedef struct _drmVersion { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + int name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + int date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + int desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ +} drmVersion, *drmVersionPtr; + +typedef struct _drmStats { + unsigned long count; /**< Number of data */ + struct { + unsigned long value; /**< Value from kernel */ + const char *long_format; /**< Suggested format for long_name */ + const char *long_name; /**< Long name for value */ + const char *rate_format; /**< Suggested format for rate_name */ + const char *rate_name; /**< Short name for value per second */ + int isvalue; /**< True if value (vs. counter) */ + const char *mult_names; /**< Multiplier names (e.g., "KGM") */ + int mult; /**< Multiplier value (e.g., 1024) */ + int verbose; /**< Suggest only in verbose output */ + } data[15]; +} drmStatsT; + + + /* All of these enums *MUST* match with the + kernel implementation -- so do *NOT* + change them! (The drmlib implementation + will just copy the flags instead of + translating them.) */ +typedef enum { + DRM_FRAME_BUFFER = 0, /**< WC, no caching, no core dump */ + DRM_REGISTERS = 1, /**< no caching, no core dump */ + DRM_SHM = 2, /**< shared, cached */ + DRM_AGP = 3, /**< AGP/GART */ + DRM_SCATTER_GATHER = 4, /**< PCI scatter/gather */ + DRM_CONSISTENT = 5 /**< PCI consistent */ +} drmMapType; + +typedef enum { + DRM_RESTRICTED = 0x0001, /**< Cannot be mapped to client-virtual */ + DRM_READ_ONLY = 0x0002, /**< Read-only in client-virtual */ + DRM_LOCKED = 0x0004, /**< Physical pages locked */ + DRM_KERNEL = 0x0008, /**< Kernel requires access */ + DRM_WRITE_COMBINING = 0x0010, /**< Use write-combining, if available */ + DRM_CONTAINS_LOCK = 0x0020, /**< SHM page that contains lock */ + DRM_REMOVABLE = 0x0040 /**< Removable mapping */ +} drmMapFlags; + +/** + * \warning These values *MUST* match drm.h + */ +typedef enum { + /** \name Flags for DMA buffer dispatch */ + /*@{*/ + DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note the buffer may not yet have been + * processed by the hardware -- getting a + * hardware lock with the hardware quiescent + * will ensure that the buffer has been + * processed. + */ + DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + /*@}*/ + + /** \name Flags for DMA buffer request */ + /*@{*/ + DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ + /*@}*/ +} drmDMAFlags; + +typedef enum { + DRM_PAGE_ALIGN = 0x01, + DRM_AGP_BUFFER = 0x02, + DRM_SG_BUFFER = 0x04, + DRM_FB_BUFFER = 0x08, + DRM_PCI_BUFFER_RO = 0x10 +} drmBufDescFlags; + +typedef enum { + DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +} drmLockFlags; + +typedef enum { + DRM_CONTEXT_PRESERVED = 0x01, /**< This context is preserved and + never swapped. */ + DRM_CONTEXT_2DONLY = 0x02 /**< This context is for 2D rendering only. */ +} drm_context_tFlags, *drm_context_tFlagsPtr; + +typedef struct _drmBufDesc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ +} drmBufDesc, *drmBufDescPtr; + +typedef struct _drmBufInfo { + int count; /**< Number of buffers described in list */ + drmBufDescPtr list; /**< List of buffer descriptions */ +} drmBufInfo, *drmBufInfoPtr; + +typedef struct _drmBuf { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + drmAddress address; /**< Address */ +} drmBuf, *drmBufPtr; + +/** + * Buffer mapping information. + * + * Used by drmMapBufs() and drmUnmapBufs() to store information about the + * mapped buffers. + */ +typedef struct _drmBufMap { + int count; /**< Number of buffers mapped */ + drmBufPtr list; /**< Buffers */ +} drmBufMap, *drmBufMapPtr; + +typedef struct _drmLock { + volatile unsigned int lock; + char padding[60]; + /* This is big enough for most current (and future?) architectures: + DEC Alpha: 32 bytes + Intel Merced: ? + Intel P5/PPro/PII/PIII: 32 bytes + Intel StrongARM: 32 bytes + Intel i386/i486: 16 bytes + MIPS: 32 bytes (?) + Motorola 68k: 16 bytes + Motorola PowerPC: 32 bytes + Sun SPARC: 32 bytes + */ +} drmLock, *drmLockPtr; + +/** + * Indices here refer to the offset into + * list in drmBufInfo + */ +typedef struct _drmDMAReq { + drm_context_t context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_list; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send, in bytes */ + drmDMAFlags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size of buffers requested */ + int *request_list; /**< Buffer information */ + int *request_sizes; /**< Minimum acceptable sizes */ + int granted_count; /**< Number of buffers granted at this size */ +} drmDMAReq, *drmDMAReqPtr; + +typedef struct _drmRegion { + drm_handle_t handle; + unsigned int offset; + drmSize size; + drmAddress map; +} drmRegion, *drmRegionPtr; + +typedef struct _drmTextureRegion { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; /**< Explicitly pad this out */ + unsigned int age; +} drmTextureRegion, *drmTextureRegionPtr; + + +typedef enum { + DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + /* bits 1-6 are reserved for high crtcs */ + DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, + DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + DRM_VBLANK_SIGNAL = 0x40000000 /* Send signal instead of blocking */ +} drmVBlankSeqType; +#define DRM_VBLANK_HIGH_CRTC_SHIFT 1 + +typedef struct _drmVBlankReq { + drmVBlankSeqType type; + unsigned int sequence; + unsigned long signal; +} drmVBlankReq, *drmVBlankReqPtr; + +typedef struct _drmVBlankReply { + drmVBlankSeqType type; + unsigned int sequence; + long tval_sec; + long tval_usec; +} drmVBlankReply, *drmVBlankReplyPtr; + +typedef union _drmVBlank { + drmVBlankReq request; + drmVBlankReply reply; +} drmVBlank, *drmVBlankPtr; + +typedef struct _drmSetVersion { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +} drmSetVersion, *drmSetVersionPtr; + +#define __drm_dummy_lock(lock) (*(__volatile__ unsigned int *)lock) + +#define DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ + +#if defined(__GNUC__) && (__GNUC__ >= 2) +# if defined(__i386) || defined(__AMD64__) || defined(__x86_64__) || defined(__amd64__) + /* Reflect changes here to drmP.h */ +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + int __dummy; /* Can't mark eax as clobbered */ \ + __asm__ __volatile__( \ + "lock ; cmpxchg %4,%1\n\t" \ + "setnz %0" \ + : "=d" (__ret), \ + "=m" (__drm_dummy_lock(lock)), \ + "=a" (__dummy) \ + : "2" (old), \ + "r" (new)); \ + } while (0) + +#elif defined(__alpha__) + +#define DRM_CAS(lock, old, new, ret) \ + do { \ + int tmp, old32; \ + __asm__ __volatile__( \ + " addl $31, %5, %3\n" \ + "1: ldl_l %0, %2\n" \ + " cmpeq %0, %3, %1\n" \ + " beq %1, 2f\n" \ + " mov %4, %0\n" \ + " stl_c %0, %2\n" \ + " beq %0, 3f\n" \ + " mb\n" \ + "2: cmpeq %1, 0, %1\n" \ + ".subsection 2\n" \ + "3: br 1b\n" \ + ".previous" \ + : "=&r"(tmp), "=&r"(ret), \ + "=m"(__drm_dummy_lock(lock)), \ + "=&r"(old32) \ + : "r"(new), "r"(old) \ + : "memory"); \ + } while (0) + +#elif defined(__sparc__) + +#define DRM_CAS(lock,old,new,__ret) \ +do { register unsigned int __old __asm("o0"); \ + register unsigned int __new __asm("o1"); \ + register volatile unsigned int *__lock __asm("o2"); \ + __old = old; \ + __new = new; \ + __lock = (volatile unsigned int *)lock; \ + __asm__ __volatile__( \ + /*"cas [%2], %3, %0"*/ \ + ".word 0xd3e29008\n\t" \ + /*"membar #StoreStore | #StoreLoad"*/ \ + ".word 0x8143e00a" \ + : "=&r" (__new) \ + : "0" (__new), \ + "r" (__lock), \ + "r" (__old) \ + : "memory"); \ + __ret = (__new != __old); \ +} while(0) + +#elif defined(__ia64__) + +#ifdef __INTEL_COMPILER +/* this currently generates bad code (missing stop bits)... */ +#include + +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + unsigned long __result, __old = (old) & 0xffffffff; \ + __mf(); \ + __result = _InterlockedCompareExchange_acq(&__drm_dummy_lock(lock), (new), __old);\ + __ret = (__result) != (__old); \ +/* __ret = (__sync_val_compare_and_swap(&__drm_dummy_lock(lock), \ + (old), (new)) \ + != (old)); */\ + } while (0) + +#else +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + unsigned int __result, __old = (old); \ + __asm__ __volatile__( \ + "mf\n" \ + "mov ar.ccv=%2\n" \ + ";;\n" \ + "cmpxchg4.acq %0=%1,%3,ar.ccv" \ + : "=r" (__result), "=m" (__drm_dummy_lock(lock)) \ + : "r" ((unsigned long)__old), "r" (new) \ + : "memory"); \ + __ret = (__result) != (__old); \ + } while (0) + +#endif + +#elif defined(__powerpc__) + +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + __asm__ __volatile__( \ + "sync;" \ + "0: lwarx %0,0,%1;" \ + " xor. %0,%3,%0;" \ + " bne 1f;" \ + " stwcx. %2,0,%1;" \ + " bne- 0b;" \ + "1: " \ + "sync;" \ + : "=&r"(__ret) \ + : "r"(lock), "r"(new), "r"(old) \ + : "cr0", "memory"); \ + } while (0) + +# elif defined (__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined (__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ + || defined (__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) \ + || defined (__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7EM__) + #undef DRM_DEV_MODE + #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) + + #define DRM_CAS(lock,old,new,__ret) \ + do { \ + __asm__ __volatile__ ( \ + "1: ldrex %0, [%1]\n" \ + " teq %0, %2\n" \ + " ite eq\n" \ + " strexeq %0, %3, [%1]\n" \ + " movne %0, #1\n" \ + : "=&r" (__ret) \ + : "r" (lock), "r" (old), "r" (new) \ + : "cc","memory"); \ + } while (0) + +#endif /* architecture */ +#endif /* __GNUC__ >= 2 */ + +#ifndef DRM_CAS +#define DRM_CAS(lock,old,new,ret) do { ret=1; } while (0) /* FAST LOCK FAILS */ +#endif + +#if defined(__alpha__) +#define DRM_CAS_RESULT(_result) long _result +#elif defined(__powerpc__) +#define DRM_CAS_RESULT(_result) int _result +#else +#define DRM_CAS_RESULT(_result) char _result +#endif + +#define DRM_LIGHT_LOCK(fd,lock,context) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ + if (__ret) drmGetLock(fd,context,0); \ + } while(0) + + /* This one counts fast locks -- for + benchmarking only. */ +#define DRM_LIGHT_LOCK_COUNT(fd,lock,context,count) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ + if (__ret) drmGetLock(fd,context,0); \ + else ++count; \ + } while(0) + +#define DRM_LOCK(fd,lock,context,flags) \ + do { \ + if (flags) drmGetLock(fd,context,flags); \ + else DRM_LIGHT_LOCK(fd,lock,context); \ + } while(0) + +#define DRM_UNLOCK(fd,lock,context) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,DRM_LOCK_HELD|context,context,__ret); \ + if (__ret) drmUnlock(fd,context); \ + } while(0) + + /* Simple spin locks */ +#define DRM_SPINLOCK(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + do { \ + DRM_CAS(spin,0,val,__ret); \ + if (__ret) while ((spin)->lock); \ + } while (__ret); \ + } while(0) + +#define DRM_SPINLOCK_TAKE(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + int cur; \ + do { \ + cur = (*spin).lock; \ + DRM_CAS(spin,cur,val,__ret); \ + } while (__ret); \ + } while(0) + +#define DRM_SPINLOCK_COUNT(spin,val,count,__ret) \ + do { \ + int __i; \ + __ret = 1; \ + for (__i = 0; __ret && __i < count; __i++) { \ + DRM_CAS(spin,0,val,__ret); \ + if (__ret) for (;__i < count && (spin)->lock; __i++); \ + } \ + } while(0) + +#define DRM_SPINUNLOCK(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + if ((*spin).lock == val) { /* else server stole lock */ \ + do { \ + DRM_CAS(spin,val,0,__ret); \ + } while (__ret); \ + } \ + } while(0) + + + +/* General user-level programmer's API: unprivileged */ +extern int drmAvailable(void); +extern int drmOpen(const char *name, const char *busid); + +#define DRM_NODE_PRIMARY 0 +#define DRM_NODE_CONTROL 1 +#define DRM_NODE_RENDER 2 +#define DRM_NODE_MAX 3 + +extern int drmOpenWithType(const char *name, const char *busid, + int type); + +extern int drmOpenControl(int minor); +extern int drmOpenRender(int minor); +extern int drmClose(int fd); +extern drmVersionPtr drmGetVersion(int fd); +extern drmVersionPtr drmGetLibVersion(int fd); +extern int drmGetCap(int fd, uint64_t capability, uint64_t *value); +extern void drmFreeVersion(drmVersionPtr); +extern int drmGetMagic(int fd, drm_magic_t * magic); +extern char *drmGetBusid(int fd); +extern int drmGetInterruptFromBusID(int fd, int busnum, int devnum, + int funcnum); +extern int drmGetMap(int fd, int idx, drm_handle_t *offset, + drmSize *size, drmMapType *type, + drmMapFlags *flags, drm_handle_t *handle, + int *mtrr); +extern int drmGetClient(int fd, int idx, int *auth, int *pid, + int *uid, unsigned long *magic, + unsigned long *iocs); +extern int drmGetStats(int fd, drmStatsT *stats); +extern int drmSetInterfaceVersion(int fd, drmSetVersion *version); +extern int drmCommandNone(int fd, unsigned long drmCommandIndex); +extern int drmCommandRead(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); +extern int drmCommandWrite(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); +extern int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); + +/* General user-level programmer's API: X server (root) only */ +extern void drmFreeBusid(const char *busid); +extern int drmSetBusid(int fd, const char *busid); +extern int drmAuthMagic(int fd, drm_magic_t magic); +extern int drmAddMap(int fd, + drm_handle_t offset, + drmSize size, + drmMapType type, + drmMapFlags flags, + drm_handle_t * handle); +extern int drmRmMap(int fd, drm_handle_t handle); +extern int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id, + drm_handle_t handle); + +extern int drmAddBufs(int fd, int count, int size, + drmBufDescFlags flags, + int agp_offset); +extern int drmMarkBufs(int fd, double low, double high); +extern int drmCreateContext(int fd, drm_context_t * handle); +extern int drmSetContextFlags(int fd, drm_context_t context, + drm_context_tFlags flags); +extern int drmGetContextFlags(int fd, drm_context_t context, + drm_context_tFlagsPtr flags); +extern int drmAddContextTag(int fd, drm_context_t context, void *tag); +extern int drmDelContextTag(int fd, drm_context_t context); +extern void *drmGetContextTag(int fd, drm_context_t context); +extern drm_context_t * drmGetReservedContextList(int fd, int *count); +extern void drmFreeReservedContextList(drm_context_t *); +extern int drmSwitchToContext(int fd, drm_context_t context); +extern int drmDestroyContext(int fd, drm_context_t handle); +extern int drmCreateDrawable(int fd, drm_drawable_t * handle); +extern int drmDestroyDrawable(int fd, drm_drawable_t handle); +extern int drmUpdateDrawableInfo(int fd, drm_drawable_t handle, + drm_drawable_info_type_t type, + unsigned int num, void *data); +extern int drmCtlInstHandler(int fd, int irq); +extern int drmCtlUninstHandler(int fd); +extern int drmSetClientCap(int fd, uint64_t capability, + uint64_t value); + +extern int drmCrtcGetSequence(int fd, uint32_t crtcId, + uint64_t *sequence, uint64_t *ns); +extern int drmCrtcQueueSequence(int fd, uint32_t crtcId, + uint32_t flags, uint64_t sequence, + uint64_t *sequence_queued, + uint64_t user_data); +/* General user-level programmer's API: authenticated client and/or X */ +extern int drmMap(int fd, + drm_handle_t handle, + drmSize size, + drmAddressPtr address); +extern int drmUnmap(drmAddress address, drmSize size); +extern drmBufInfoPtr drmGetBufInfo(int fd); +extern drmBufMapPtr drmMapBufs(int fd); +extern int drmUnmapBufs(drmBufMapPtr bufs); +extern int drmDMA(int fd, drmDMAReqPtr request); +extern int drmFreeBufs(int fd, int count, int *list); +extern int drmGetLock(int fd, + drm_context_t context, + drmLockFlags flags); +extern int drmUnlock(int fd, drm_context_t context); +extern int drmFinish(int fd, int context, drmLockFlags flags); +extern int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id, + drm_handle_t * handle); + +/* AGP/GART support: X server (root) only */ +extern int drmAgpAcquire(int fd); +extern int drmAgpRelease(int fd); +extern int drmAgpEnable(int fd, unsigned long mode); +extern int drmAgpAlloc(int fd, unsigned long size, + unsigned long type, unsigned long *address, + drm_handle_t *handle); +extern int drmAgpFree(int fd, drm_handle_t handle); +extern int drmAgpBind(int fd, drm_handle_t handle, + unsigned long offset); +extern int drmAgpUnbind(int fd, drm_handle_t handle); + +/* AGP/GART info: authenticated client and/or X */ +extern int drmAgpVersionMajor(int fd); +extern int drmAgpVersionMinor(int fd); +extern unsigned long drmAgpGetMode(int fd); +extern unsigned long drmAgpBase(int fd); /* Physical location */ +extern unsigned long drmAgpSize(int fd); /* Bytes */ +extern unsigned long drmAgpMemoryUsed(int fd); +extern unsigned long drmAgpMemoryAvail(int fd); +extern unsigned int drmAgpVendorId(int fd); +extern unsigned int drmAgpDeviceId(int fd); + +/* PCI scatter/gather support: X server (root) only */ +extern int drmScatterGatherAlloc(int fd, unsigned long size, + drm_handle_t *handle); +extern int drmScatterGatherFree(int fd, drm_handle_t handle); + +extern int drmWaitVBlank(int fd, drmVBlankPtr vbl); + +/* Support routines */ +extern void drmSetServerInfo(drmServerInfoPtr info); +extern int drmError(int err, const char *label); +extern void *drmMalloc(int size); +extern void drmFree(void *pt); + +/* Hash table routines */ +extern void *drmHashCreate(void); +extern int drmHashDestroy(void *t); +extern int drmHashLookup(void *t, unsigned long key, void **value); +extern int drmHashInsert(void *t, unsigned long key, void *value); +extern int drmHashDelete(void *t, unsigned long key); +extern int drmHashFirst(void *t, unsigned long *key, void **value); +extern int drmHashNext(void *t, unsigned long *key, void **value); + +/* PRNG routines */ +extern void *drmRandomCreate(unsigned long seed); +extern int drmRandomDestroy(void *state); +extern unsigned long drmRandom(void *state); +extern double drmRandomDouble(void *state); + +/* Skip list routines */ + +extern void *drmSLCreate(void); +extern int drmSLDestroy(void *l); +extern int drmSLLookup(void *l, unsigned long key, void **value); +extern int drmSLInsert(void *l, unsigned long key, void *value); +extern int drmSLDelete(void *l, unsigned long key); +extern int drmSLNext(void *l, unsigned long *key, void **value); +extern int drmSLFirst(void *l, unsigned long *key, void **value); +extern void drmSLDump(void *l); +extern int drmSLLookupNeighbors(void *l, unsigned long key, + unsigned long *prev_key, void **prev_value, + unsigned long *next_key, void **next_value); + +extern int drmOpenOnce(void *unused, const char *BusID, int *newlyopened); +extern int drmOpenOnceWithType(const char *BusID, int *newlyopened, int type); +extern void drmCloseOnce(int fd); +extern void drmMsg(const char *format, ...) DRM_PRINTFLIKE(1, 2); + +extern int drmSetMaster(int fd); +extern int drmDropMaster(int fd); +extern int drmIsMaster(int fd); + +#define DRM_EVENT_CONTEXT_VERSION 4 + +typedef struct _drmEventContext { + + /* This struct is versioned so we can add more pointers if we + * add more events. */ + int version; + + void (*vblank_handler)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + + void (*page_flip_handler)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + + void (*page_flip_handler2)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + unsigned int crtc_id, + void *user_data); + + void (*sequence_handler)(int fd, + uint64_t sequence, + uint64_t ns, + uint64_t user_data); +} drmEventContext, *drmEventContextPtr; + +extern int drmHandleEvent(int fd, drmEventContextPtr evctx); + +extern char *drmGetDeviceNameFromFd(int fd); + +/* Improved version of drmGetDeviceNameFromFd which attributes for any type of + * device/node - card, control or renderD. + */ +extern char *drmGetDeviceNameFromFd2(int fd); +extern int drmGetNodeTypeFromFd(int fd); + +extern int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd); +extern int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle); + +extern char *drmGetPrimaryDeviceNameFromFd(int fd); +extern char *drmGetRenderDeviceNameFromFd(int fd); + +#define DRM_BUS_PCI 0 +#define DRM_BUS_USB 1 +#define DRM_BUS_PLATFORM 2 +#define DRM_BUS_HOST1X 3 + +typedef struct _drmPciBusInfo { + uint16_t domain; + uint8_t bus; + uint8_t dev; + uint8_t func; +} drmPciBusInfo, *drmPciBusInfoPtr; + +typedef struct _drmPciDeviceInfo { + uint16_t vendor_id; + uint16_t device_id; + uint16_t subvendor_id; + uint16_t subdevice_id; + uint8_t revision_id; +} drmPciDeviceInfo, *drmPciDeviceInfoPtr; + +typedef struct _drmUsbBusInfo { + uint8_t bus; + uint8_t dev; +} drmUsbBusInfo, *drmUsbBusInfoPtr; + +typedef struct _drmUsbDeviceInfo { + uint16_t vendor; + uint16_t product; +} drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; + +#define DRM_PLATFORM_DEVICE_NAME_LEN 512 + +typedef struct _drmPlatformBusInfo { + char fullname[DRM_PLATFORM_DEVICE_NAME_LEN]; +} drmPlatformBusInfo, *drmPlatformBusInfoPtr; + +typedef struct _drmPlatformDeviceInfo { + char **compatible; /* NULL terminated list of compatible strings */ +} drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr; + +#define DRM_HOST1X_DEVICE_NAME_LEN 512 + +typedef struct _drmHost1xBusInfo { + char fullname[DRM_HOST1X_DEVICE_NAME_LEN]; +} drmHost1xBusInfo, *drmHost1xBusInfoPtr; + +typedef struct _drmHost1xDeviceInfo { + char **compatible; /* NULL terminated list of compatible strings */ +} drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr; + +typedef struct _drmDevice { + char **nodes; /* DRM_NODE_MAX sized array */ + int available_nodes; /* DRM_NODE_* bitmask */ + int bustype; + union { + drmPciBusInfoPtr pci; + drmUsbBusInfoPtr usb; + drmPlatformBusInfoPtr platform; + drmHost1xBusInfoPtr host1x; + } businfo; + union { + drmPciDeviceInfoPtr pci; + drmUsbDeviceInfoPtr usb; + drmPlatformDeviceInfoPtr platform; + drmHost1xDeviceInfoPtr host1x; + } deviceinfo; +} drmDevice, *drmDevicePtr; + +extern int drmGetDevice(int fd, drmDevicePtr *device); +extern void drmFreeDevice(drmDevicePtr *device); + +extern int drmGetDevices(drmDevicePtr devices[], int max_devices); +extern void drmFreeDevices(drmDevicePtr devices[], int count); + +#define DRM_DEVICE_GET_PCI_REVISION (1 << 0) +extern int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device); +extern int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices); + +extern int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b); + +extern int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle); +extern int drmSyncobjDestroy(int fd, uint32_t handle); +extern int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd); +extern int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle); + +extern int drmSyncobjImportSyncFile(int fd, uint32_t handle, int sync_file_fd); +extern int drmSyncobjExportSyncFile(int fd, uint32_t handle, int *sync_file_fd); +extern int drmSyncobjWait(int fd, uint32_t *handles, unsigned num_handles, + int64_t timeout_nsec, unsigned flags, + uint32_t *first_signaled); +extern int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t handle_count); +extern int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t handle_count); +extern int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, + uint64_t *points, uint32_t handle_count); +extern int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, + unsigned num_handles, + int64_t timeout_nsec, unsigned flags, + uint32_t *first_signaled); +extern int drmSyncobjQuery(int fd, uint32_t *handles, uint64_t *points, + uint32_t handle_count); +extern int drmSyncobjTransfer(int fd, + uint32_t dst_handle, uint64_t dst_point, + uint32_t src_handle, uint64_t src_point, + uint32_t flags); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/xf86drmMode.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/xf86drmMode.h new file mode 100644 index 0000000..a32902f --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/drm/include/xf86drmMode.h @@ -0,0 +1,551 @@ +/* + * \file xf86drmMode.h + * Header for DRM modesetting interface. + * + * \author Jakob Bornecrantz + * + * \par Acknowledgements: + * Feb 2007, Dave Airlie + */ + +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2007-2008 Dave Airlie + * Copyright (c) 2007-2008 Jakob Bornecrantz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef _XF86DRMMODE_H_ +#define _XF86DRMMODE_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +/* + * This is the interface for modesetting for drm. + * + * In order to use this interface you must include either or another + * header defining uint32_t, int32_t and uint16_t. + * + * It aims to provide a randr1.2 compatible interface for modesettings in the + * kernel, the interface is also meant to be used by libraries like EGL. + * + * More information can be found in randrproto.txt which can be found here: + * http://gitweb.freedesktop.org/?p=xorg/proto/randrproto.git + * + * There are some major differences to be noted. Unlike the randr1.2 proto you + * need to create the memory object of the framebuffer yourself with the ttm + * buffer object interface. This object needs to be pinned. + */ + +/* + * If we pickup an old version of drm.h which doesn't include drm_mode.h + * we should redefine defines. This is so that builds doesn't breaks with + * new libdrm on old kernels. + */ +#ifndef _DRM_MODE_H + +#define DRM_DISPLAY_INFO_LEN 32 +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +/* Video mode flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) +#define DRM_MODE_FLAG_PIXMUX (1<<11) +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) +#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) +#define DRM_MODE_FLAG_3D_NONE (0<<14) +#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) +#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) +#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) +#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NON_GPU 0 +#define DRM_MODE_SCALE_FULLSCREEN 1 +#define DRM_MODE_SCALE_NO_SCALE 2 +#define DRM_MODE_SCALE_ASPECT 3 + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 +#define DRM_MODE_ENCODER_VIRTUAL 5 +#define DRM_MODE_ENCODER_DSI 6 +#define DRM_MODE_ENCODER_DPMST 7 +#define DRM_MODE_ENCODER_DPI 8 + +#define DRM_MODE_SUBCONNECTOR_Automatic 0 +#define DRM_MODE_SUBCONNECTOR_Unknown 0 +#define DRM_MODE_SUBCONNECTOR_DVID 3 +#define DRM_MODE_SUBCONNECTOR_DVIA 4 +#define DRM_MODE_SUBCONNECTOR_Composite 5 +#define DRM_MODE_SUBCONNECTOR_SVIDEO 6 +#define DRM_MODE_SUBCONNECTOR_Component 8 +#define DRM_MODE_SUBCONNECTOR_SCART 9 + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 +#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_eDP 14 +#define DRM_MODE_CONNECTOR_VIRTUAL 15 +#define DRM_MODE_CONNECTOR_DSI 16 +#define DRM_MODE_CONNECTOR_DPI 17 + +#define DRM_MODE_PROP_PENDING (1<<0) +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) + +#define DRM_MODE_CURSOR_BO (1<<0) +#define DRM_MODE_CURSOR_MOVE (1<<1) + +#endif /* _DRM_MODE_H */ + + +/* + * Feature defines + * + * Just because these are defined doesn't mean that the kernel + * can do that feature, its just for new code vs old libdrm. + */ +#define DRM_MODE_FEATURE_KMS 1 +#define DRM_MODE_FEATURE_DIRTYFB 1 + + +typedef struct _drmModeRes { + + int count_fbs; + uint32_t *fbs; + + int count_crtcs; + uint32_t *crtcs; + + int count_connectors; + uint32_t *connectors; + + int count_encoders; + uint32_t *encoders; + + uint32_t min_width, max_width; + uint32_t min_height, max_height; +} drmModeRes, *drmModeResPtr; + +typedef struct _drmModeModeInfo { + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t vrefresh; + + uint32_t flags; + uint32_t type; + char name[DRM_DISPLAY_MODE_LEN]; +} drmModeModeInfo, *drmModeModeInfoPtr; + +typedef struct _drmModeFB { + uint32_t fb_id; + uint32_t width, height; + uint32_t pitch; + uint32_t bpp; + uint32_t depth; + /* driver specific handle */ + uint32_t handle; +} drmModeFB, *drmModeFBPtr; + +typedef struct drm_clip_rect drmModeClip, *drmModeClipPtr; + +typedef struct _drmModePropertyBlob { + uint32_t id; + uint32_t length; + void *data; +} drmModePropertyBlobRes, *drmModePropertyBlobPtr; + +typedef struct _drmModeProperty { + uint32_t prop_id; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + int count_values; + uint64_t *values; /* store the blob lengths */ + int count_enums; + struct drm_mode_property_enum *enums; + int count_blobs; + uint32_t *blob_ids; /* store the blob IDs */ +} drmModePropertyRes, *drmModePropertyPtr; + +static __inline int drm_property_type_is(drmModePropertyPtr property, + uint32_t type) +{ + /* instanceof for props.. handles extended type vs original types: */ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; + return property->flags & type; +} + +typedef struct _drmModeCrtc { + uint32_t crtc_id; + uint32_t buffer_id; /**< FB id to connect to 0 = disconnect */ + + uint32_t x, y; /**< Position on the framebuffer */ + uint32_t width, height; + int mode_valid; + drmModeModeInfo mode; + + int gamma_size; /**< Number of gamma stops */ + +} drmModeCrtc, *drmModeCrtcPtr; + +typedef struct _drmModeEncoder { + uint32_t encoder_id; + uint32_t encoder_type; + uint32_t crtc_id; + uint32_t possible_crtcs; + uint32_t possible_clones; +} drmModeEncoder, *drmModeEncoderPtr; + +typedef enum { + DRM_MODE_CONNECTED = 1, + DRM_MODE_DISCONNECTED = 2, + DRM_MODE_UNKNOWNCONNECTION = 3 +} drmModeConnection; + +typedef enum { + DRM_MODE_SUBPIXEL_UNKNOWN = 1, + DRM_MODE_SUBPIXEL_HORIZONTAL_RGB = 2, + DRM_MODE_SUBPIXEL_HORIZONTAL_BGR = 3, + DRM_MODE_SUBPIXEL_VERTICAL_RGB = 4, + DRM_MODE_SUBPIXEL_VERTICAL_BGR = 5, + DRM_MODE_SUBPIXEL_NONE = 6 +} drmModeSubPixel; + +typedef struct _drmModeConnector { + uint32_t connector_id; + uint32_t encoder_id; /**< Encoder currently connected to */ + uint32_t connector_type; + uint32_t connector_type_id; + drmModeConnection connection; + uint32_t mmWidth, mmHeight; /**< HxW in millimeters */ + drmModeSubPixel subpixel; + + int count_modes; + drmModeModeInfoPtr modes; + + int count_props; + uint32_t *props; /**< List of property ids */ + uint64_t *prop_values; /**< List of property values */ + + int count_encoders; + uint32_t *encoders; /**< List of encoder ids */ +} drmModeConnector, *drmModeConnectorPtr; + +#define DRM_PLANE_TYPE_OVERLAY 0 +#define DRM_PLANE_TYPE_PRIMARY 1 +#define DRM_PLANE_TYPE_CURSOR 2 + +typedef struct _drmModeObjectProperties { + uint32_t count_props; + uint32_t *props; + uint64_t *prop_values; +} drmModeObjectProperties, *drmModeObjectPropertiesPtr; + +typedef struct _drmModePlane { + uint32_t count_formats; + uint32_t *formats; + uint32_t plane_id; + + uint32_t crtc_id; + uint32_t fb_id; + + uint32_t crtc_x, crtc_y; + uint32_t x, y; + + uint32_t possible_crtcs; + uint32_t gamma_size; +} drmModePlane, *drmModePlanePtr; + +typedef struct _drmModePlaneRes { + uint32_t count_planes; + uint32_t *planes; +} drmModePlaneRes, *drmModePlaneResPtr; + +extern void drmModeFreeModeInfo( drmModeModeInfoPtr ptr ); +extern void drmModeFreeResources( drmModeResPtr ptr ); +extern void drmModeFreeFB( drmModeFBPtr ptr ); +extern void drmModeFreeCrtc( drmModeCrtcPtr ptr ); +extern void drmModeFreeConnector( drmModeConnectorPtr ptr ); +extern void drmModeFreeEncoder( drmModeEncoderPtr ptr ); +extern void drmModeFreePlane( drmModePlanePtr ptr ); +extern void drmModeFreePlaneResources(drmModePlaneResPtr ptr); + +/** + * Retrieves all of the resources associated with a card. + */ +extern drmModeResPtr drmModeGetResources(int fd); + +/* + * FrameBuffer manipulation. + */ + +/** + * Retrieve information about framebuffer bufferId + */ +extern drmModeFBPtr drmModeGetFB(int fd, uint32_t bufferId); + +/** + * Creates a new framebuffer with an buffer object as its scanout buffer. + */ +extern int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle, + uint32_t *buf_id); +/* ...with a specific pixel format */ +extern int drmModeAddFB2(int fd, uint32_t width, uint32_t height, + uint32_t pixel_format, const uint32_t bo_handles[4], + const uint32_t pitches[4], const uint32_t offsets[4], + uint32_t *buf_id, uint32_t flags); + +/* ...with format modifiers */ +int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height, + uint32_t pixel_format, const uint32_t bo_handles[4], + const uint32_t pitches[4], const uint32_t offsets[4], + const uint64_t modifier[4], uint32_t *buf_id, + uint32_t flags); + +/** + * Destroies the given framebuffer. + */ +extern int drmModeRmFB(int fd, uint32_t bufferId); + +/** + * Mark a region of a framebuffer as dirty. + */ +extern int drmModeDirtyFB(int fd, uint32_t bufferId, + drmModeClipPtr clips, uint32_t num_clips); + + +/* + * Crtc functions + */ + +/** + * Retrieve information about the ctrt crtcId + */ +extern drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId); + +/** + * Set the mode on a crtc crtcId with the given mode modeId. + */ +int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, + uint32_t x, uint32_t y, uint32_t *connectors, int count, + drmModeModeInfoPtr mode); + +/* + * Cursor functions + */ + +/** + * Set the cursor on crtc + */ +int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height); + +int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); +/** + * Move the cursor on crtc + */ +int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y); + +/** + * Encoder functions + */ +drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id); + +/* + * Connector manipulation + */ + +/** + * Retrieve all information about the connector connectorId. This will do a + * forced probe on the connector to retrieve remote information such as EDIDs + * from the display device. + */ +extern drmModeConnectorPtr drmModeGetConnector(int fd, + uint32_t connectorId); + +/** + * Retrieve current information, i.e the currently active mode and encoder, + * about the connector connectorId. This will not do any probing on the + * connector or remote device, and only reports what is currently known. + * For the complete set of modes and encoders associated with the connector + * use drmModeGetConnector() which will do a probe to determine any display + * link changes first. + */ +extern drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, + uint32_t connector_id); + +/** + * Attaches the given mode to an connector. + */ +extern int drmModeAttachMode(int fd, uint32_t connectorId, drmModeModeInfoPtr mode_info); + +/** + * Detaches a mode from the connector + * must be unused, by the given mode. + */ +extern int drmModeDetachMode(int fd, uint32_t connectorId, drmModeModeInfoPtr mode_info); + +extern drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId); +extern void drmModeFreeProperty(drmModePropertyPtr ptr); + +extern drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id); +extern void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr); +extern int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, + uint64_t value); +extern int drmCheckModesettingSupported(const char *busid); + +extern int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, + uint32_t flags, void *user_data); +extern int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id, + uint32_t flags, void *user_data, + uint32_t target_vblank); + +extern drmModePlaneResPtr drmModeGetPlaneResources(int fd); +extern drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id); +extern int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id, + uint32_t fb_id, uint32_t flags, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + +extern drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd, + uint32_t object_id, + uint32_t object_type); +extern void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr); +extern int drmModeObjectSetProperty(int fd, uint32_t object_id, + uint32_t object_type, uint32_t property_id, + uint64_t value); + + +typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr; + +extern drmModeAtomicReqPtr drmModeAtomicAlloc(void); +extern drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr req); +extern int drmModeAtomicMerge(drmModeAtomicReqPtr base, + drmModeAtomicReqPtr augment); +extern void drmModeAtomicFree(drmModeAtomicReqPtr req); +extern int drmModeAtomicGetCursor(drmModeAtomicReqPtr req); +extern void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor); +extern int drmModeAtomicAddProperty(drmModeAtomicReqPtr req, + uint32_t object_id, + uint32_t property_id, + uint64_t value); +extern int drmModeAtomicCommit(int fd, + drmModeAtomicReqPtr req, + uint32_t flags, + void *user_data); + +extern int drmModeCreatePropertyBlob(int fd, const void *data, size_t size, + uint32_t *id); +extern int drmModeDestroyPropertyBlob(int fd, uint32_t id); + +/* + * DRM mode lease APIs. These create and manage new drm_masters with + * access to a subset of the available DRM resources + */ + +extern int drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id); + +typedef struct drmModeLesseeList { + uint32_t count; + uint32_t lessees[0]; +} drmModeLesseeListRes, *drmModeLesseeListPtr; + +extern drmModeLesseeListPtr drmModeListLessees(int fd); + +typedef struct drmModeObjectList { + uint32_t count; + uint32_t objects[0]; +} drmModeObjectListRes, *drmModeObjectListPtr; + +extern drmModeObjectListPtr drmModeGetLease(int fd); + +extern int drmModeRevokeLease(int fd, uint32_t lessee_id); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RgaApi.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RgaApi.h new file mode 100644 index 0000000..c484f4d --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RgaApi.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "drmrga.h" + +#include "RockchipRgaMacro.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + +int c_RkRgaInit(); +void c_RkRgaDeInit(); +int c_RkRgaGetAllocBuffer(bo_t *bo_info, int width, int height, int bpp); +int c_RkRgaGetMmap(bo_t *bo_info); +int c_RkRgaUnmap(bo_t *bo_info); +int c_RkRgaGetBufferFd(bo_t *bo_info, int *fd); +int c_RkRgaBlit(rga_info_t *src, rga_info_t *dst, rga_info_t *src1); +int c_RkRgaColorFill(rga_info_t *dst); +#ifdef __cplusplus +} +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipFileOps.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipFileOps.h new file mode 100644 index 0000000..6d123ec --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipFileOps.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _rk_file_ops_h_ +#define _rk_file_ops_h_ + +// ------------------------------------------------------------------------------- +int get_buf_from_file(void *buf, int f, int sw, int sh, int index); +int output_buf_data_to_file(void *buf, int f, int sw, int sh, int index); +#endif + diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRga.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRga.h new file mode 100644 index 0000000..c49c467 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRga.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +//#include +#include + +////////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "stdio.h" + +////////////////////////////////////////////////////////////////////////////////// + +// ------------------------------------------------------------------------------- + +class RockchipRga +{ +/************************************public**************************************/ + +public: + + RockchipRga(); + ~RockchipRga(); + + int RkRgaInit(); + void RkRgaDeInit(); + int RkRgaAllocBuffer(int drm_fd /* input */, bo_t *bo_info, + int width, int height, int bpp); + int RkRgaFreeBuffer(int drm_fd /* input */, bo_t *bo_info); + int RkRgaGetAllocBuffer(bo_t *bo_info, int width, int height, int bpp); + int RkRgaGetMmap(bo_t *bo_info); + int RkRgaUnmap(bo_t *bo_info); + int RkRgaFree(bo_t *bo_info); + int RkRgaGetBufferFd(bo_t *bo_info, int *fd); + int RkRgaBlit(rga_info *src, rga_info *dst, rga_info *src1); + int RkRgaCollorFill(rga_info *dst); + + + void RkRgaSetLogOnceFlag(int log) {mLogOnce = log;} + void RkRgaSetAlwaysLogFlag(bool log) {mLogAlways = log;} + void RkRgaLogOutRgaReq(struct rga_req rgaReg); + int RkRgaLogOutUserPara(rga_info *rgaInfo); + inline bool RkRgaIsReady() { return mSupportRga; } + +/************************************private***********************************/ +private: + bool mSupportRga; + int mLogOnce; + int mLogAlways; + void * mContext; +}; + +// --------------------------------------------------------------------------- + + diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRgaMacro.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRgaMacro.h new file mode 100644 index 0000000..3c1fff0 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRgaMacro.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __ROCKCHIP_RGA_MACRO_H__ +#define __ROCKCHIP_RGA_MACRO_H__ + +/* flip source image horizontally (around the vertical axis) */ +#define HAL_TRANSFORM_FLIP_H 0x01 +/* flip source image vertically (around the horizontal axis)*/ +#define HAL_TRANSFORM_FLIP_V 0x02 +/* rotate source image 90 degrees clockwise */ +#define HAL_TRANSFORM_ROT_90 0x04 +/* rotate source image 180 degrees */ +#define HAL_TRANSFORM_ROT_180 0x03 +/* rotate source image 270 degrees clockwise */ +#define HAL_TRANSFORM_ROT_270 0x07 + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/drmrga.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/drmrga.h new file mode 100644 index 0000000..abd8b97 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/drmrga.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _rk_drm_rga_ +#define _rk_drm_rga_ + +#include +#include +#include "rga.h" +#include "RockchipRgaMacro.h" + +/*****************************************************************************/ + +/* for compatibility */ +#define DRM_RGA_MODULE_API_VERSION HWC_MODULE_API_VERSION_0_1 +#define DRM_RGA_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_0_1 +#define DRM_RGA_API_VERSION HWC_DEVICE_API_VERSION + +#define DRM_RGA_TRANSFORM_ROT_MASK 0x0000000F +#define DRM_RGA_TRANSFORM_ROT_0 0x00000000 +#define DRM_RGA_TRANSFORM_ROT_90 HAL_TRANSFORM_ROT_90 +#define DRM_RGA_TRANSFORM_ROT_180 HAL_TRANSFORM_ROT_180 +#define DRM_RGA_TRANSFORM_ROT_270 HAL_TRANSFORM_ROT_270 + +#define DRM_RGA_TRANSFORM_FLIP_MASK 0x00000003 +#define DRM_RGA_TRANSFORM_FLIP_H HAL_TRANSFORM_FLIP_H +#define DRM_RGA_TRANSFORM_FLIP_V HAL_TRANSFORM_FLIP_V + +enum { + AWIDTH = 0, + AHEIGHT, + ASTRIDE, + AFORMAT, + ASIZE, + ATYPE, +}; +/*****************************************************************************/ + + +typedef struct bo { + int fd; + void *ptr; + size_t size; + size_t offset; + size_t pitch; + unsigned handle; + }bo_t; + +/* + @value size: user not need care about.For avoid read/write out of memory + */ +typedef struct rga_rect { + int xoffset; + int yoffset; + int width; + int height; + int wstride; + int hstride; + int format; + int size; +} rga_rect_t; + +typedef struct rga_nn { + int nn_flag; + int scale_r; + int scale_g; + int scale_b; + int offset_r; + int offset_g; + int offset_b; +} rga_nn_t; + +typedef struct rga_dither { + int enable; + int mode; + int lut0_l; + int lut0_h; + int lut1_l; + int lut1_h; +} rga_dither_t; +/* + @value fd: use fd to share memory, it can be ion shard fd,and dma fd. + @value virAddr:userspace address + @value phyAddr:use phy address + @value hnd: use buffer_handle_t + */ +typedef struct rga_info { + int fd; + void *virAddr; + void *phyAddr; + unsigned hnd; + int format; + rga_rect_t rect; + unsigned int blend; + int bufferSize; + int rotation; + int color; + int testLog; + int mmuFlag; + int scale_mode; + rga_nn_t nn; + rga_dither_t dither; + int reserve[124]; +} rga_info_t; + + +typedef struct drm_rga { + rga_rect_t src; + rga_rect_t dst; +} drm_rga_t; + +/* + @fun rga_set_rect:For use to set the rects esayly + + @param rect:The rect user want to set,like setting the src rect: + drm_rga_t rects; + rga_set_rect(rects.src,0,0,1920,1080,1920,NV12); + mean to set the src rect to the value. + */ +static inline int rga_set_rect(rga_rect_t *rect, + int x, int y, int w, int h, int sw, int sh, int f) +{ + if (!rect) + return -EINVAL; + + rect->xoffset = x; + rect->yoffset = y; + rect->width = w; + rect->height = h; + rect->wstride = sw; + rect->hstride = sh; + rect->format = f; + + return 0; +} + +static inline void rga_set_rotation(rga_info_t *info, int angle) +{ + if (angle == 90) + info->rotation = HAL_TRANSFORM_ROT_90; + else if (angle == 180) + info->rotation = HAL_TRANSFORM_ROT_180; + else if (angle == 270) + info->rotation = HAL_TRANSFORM_ROT_270; +} +/*****************************************************************************/ + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/rga.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/rga.h new file mode 100644 index 0000000..7737e7a --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/rga/include/rga.h @@ -0,0 +1,752 @@ +#ifndef _RGA_DRIVER_H_ +#define _RGA_DRIVER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +#define RGA_BLIT_SYNC 0x5017 +#define RGA_BLIT_ASYNC 0x5018 +#define RGA_FLUSH 0x5019 +#define RGA_GET_RESULT 0x501a +#define RGA_GET_VERSION 0x501b + + +#define RGA_REG_CTRL_LEN 0x8 /* 8 */ +#define RGA_REG_CMD_LEN 0x1c /* 28 */ +#define RGA_CMD_BUF_SIZE 0x700 /* 16*28*4 */ + + + +#ifndef ENABLE +#define ENABLE 1 +#endif + + +#ifndef DISABLE +#define DISABLE 0 +#endif + + + +/* RGA process mode enum */ +enum +{ + bitblt_mode = 0x0, + color_palette_mode = 0x1, + color_fill_mode = 0x2, + line_point_drawing_mode = 0x3, + blur_sharp_filter_mode = 0x4, + pre_scaling_mode = 0x5, + update_palette_table_mode = 0x6, + update_patten_buff_mode = 0x7, +}; + + +enum +{ + rop_enable_mask = 0x2, + dither_enable_mask = 0x8, + fading_enable_mask = 0x10, + PD_enbale_mask = 0x20, +}; + +enum +{ + yuv2rgb_mode0 = 0x0, /* BT.601 MPEG */ + yuv2rgb_mode1 = 0x1, /* BT.601 JPEG */ + yuv2rgb_mode2 = 0x2, /* BT.709 */ +}; + + +/* RGA rotate mode */ +enum +{ + rotate_mode0 = 0x0, /* no rotate */ + rotate_mode1 = 0x1, /* rotate */ + rotate_mode2 = 0x2, /* x_mirror */ + rotate_mode3 = 0x3, /* y_mirror */ +}; + +enum +{ + color_palette_mode0 = 0x0, /* 1K */ + color_palette_mode1 = 0x1, /* 2K */ + color_palette_mode2 = 0x2, /* 4K */ + color_palette_mode3 = 0x3, /* 8K */ +}; + +enum +{ + BB_BYPASS = 0x0, /* no rotate */ + BB_ROTATE = 0x1, /* rotate */ + BB_X_MIRROR = 0x2, /* x_mirror */ + BB_Y_MIRROR = 0x3 /* y_mirror */ +}; + +enum +{ + nearby = 0x0, /* no rotate */ + bilinear = 0x1, /* rotate */ + bicubic = 0x2, /* x_mirror */ +}; + + + + + +/* +// Alpha Red Green Blue +{ 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // RK_FORMAT_RGBA_8888 +{ 4, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // RK_FORMAT_RGBX_8888 +{ 3, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // RK_FORMAT_RGB_888 +{ 4, 32, {{32,24, 24,16, 16, 8, 8, 0 }}, GGL_BGRA }, // RK_FORMAT_BGRA_8888 +{ 2, 16, {{ 0, 0, 16,11, 11, 5, 5, 0 }}, GGL_RGB }, // RK_FORMAT_RGB_565 +{ 2, 16, {{ 1, 0, 16,11, 11, 6, 6, 1 }}, GGL_RGBA }, // RK_FORMAT_RGBA_5551 +{ 2, 16, {{ 4, 0, 16,12, 12, 8, 8, 4 }}, GGL_RGBA }, // RK_FORMAT_RGBA_4444 +{ 3, 24, {{ 0, 0, 24,16, 16, 8, 8, 0 }}, GGL_BGR }, // RK_FORMAT_BGB_888 + +*/ + +typedef enum _Rga_SURF_FORMAT +{ + RK_FORMAT_RGBA_8888 = 0x0, + RK_FORMAT_RGBX_8888 = 0x1, + RK_FORMAT_RGB_888 = 0x2, + RK_FORMAT_BGRA_8888 = 0x3, + RK_FORMAT_BGRX_8888 = 0x4, + RK_FORMAT_BGR_888 = 0x5, + RK_FORMAT_RGB_565 = 0x6, + RK_FORMAT_RGBA_5551 = 0x7, + RK_FORMAT_RGBA_4444 = 0x8, + RK_FORMAT_BGR_565 = 0x9, + RK_FORMAT_BGRA_5551 = 0xa, + RK_FORMAT_BGRA_4444 = 0xb, + + RK_FORMAT_Y4 = 0xe, + RK_FORMAT_YCbCr_400 = 0xf, + RK_FORMAT_YCbCr_422_SP = 0x10, + RK_FORMAT_YCbCr_422_P = 0x11, + RK_FORMAT_YCbCr_420_SP = 0x12, + RK_FORMAT_YCbCr_420_P = 0x13, + RK_FORMAT_YCrCb_422_SP = 0x14, + RK_FORMAT_YCrCb_422_P = 0x15, + RK_FORMAT_YCrCb_420_SP = 0x16, + RK_FORMAT_YCrCb_420_P = 0x17, + + RK_FORMAT_YVYU_422 = 0x18, + RK_FORMAT_YVYU_420 = 0x19, + RK_FORMAT_VYUY_422 = 0x1a, + RK_FORMAT_VYUY_420 = 0x1b, + RK_FORMAT_YUYV_422 = 0x1c, + RK_FORMAT_YUYV_420 = 0x1d, + RK_FORMAT_UYVY_422 = 0x1e, + RK_FORMAT_UYVY_420 = 0x1f, + + RK_FORMAT_YCbCr_422_10b_SP = 0x20, + RK_FORMAT_YCbCr_420_10b_SP = 0x21, + RK_FORMAT_YCrCb_422_10b_SP = 0x22, + RK_FORMAT_YCrCb_420_10b_SP = 0x23, + + RK_FORMAT_UNKNOWN = 0x100, +} RgaSURF_FORMAT; + +typedef struct rga_img_info_t +{ +#if defined(__arm64__) || defined(__aarch64__) + unsigned long yrgb_addr; /* yrgb mem addr */ + unsigned long uv_addr; /* cb/cr mem addr */ + unsigned long v_addr; /* cr mem addr */ +#else + unsigned int yrgb_addr; /* yrgb mem addr */ + unsigned int uv_addr; /* cb/cr mem addr */ + unsigned int v_addr; /* cr mem addr */ +#endif + unsigned int format; //definition by RK_FORMAT + unsigned short act_w; + unsigned short act_h; + unsigned short x_offset; + unsigned short y_offset; + + unsigned short vir_w; + unsigned short vir_h; + + unsigned short endian_mode; //for BPP + unsigned short alpha_swap; +} +rga_img_info_t; + + +typedef struct mdp_img_act +{ + unsigned short w; // width + unsigned short h; // height + short x_off; // x offset for the vir + short y_off; // y offset for the vir +} +mdp_img_act; + + + +typedef struct RANGE +{ + unsigned short min; + unsigned short max; +} +RANGE; + +typedef struct POINT_t +{ + unsigned short x; + unsigned short y; +} +POINT_t; + +typedef struct RECT_t +{ + unsigned short xmin; + unsigned short xmax; // width - 1 + unsigned short ymin; + unsigned short ymax; // height - 1 +} RECT_t; + +typedef struct RGT_t +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char res; +}RGB_t; + + +typedef struct MMU +{ + unsigned char mmu_en; +#if defined(__arm64__) || defined(__aarch64__) + unsigned long base_addr; +#else + unsigned int base_addr; +#endif + unsigned int mmu_flag; /* [0] mmu enable [1] src_flush [2] dst_flush [3] CMD_flush [4~5] page size*/ +} MMU; + + + + +typedef struct COLOR_FILL +{ + short gr_x_a; + short gr_y_a; + short gr_x_b; + short gr_y_b; + short gr_x_g; + short gr_y_g; + short gr_x_r; + short gr_y_r; + + //u8 cp_gr_saturation; +} +COLOR_FILL; + +typedef struct FADING +{ + unsigned char b; + unsigned char g; + unsigned char r; + unsigned char res; +} +FADING; + + +typedef struct line_draw_t +{ + POINT_t start_point; /* LineDraw_start_point */ + POINT_t end_point; /* LineDraw_end_point */ + unsigned int color; /* LineDraw_color */ + unsigned int flag; /* (enum) LineDrawing mode sel */ + unsigned int line_width; /* range 1~16 */ +} +line_draw_t; + + + + struct rga_req { + unsigned char render_mode; /* (enum) process mode sel */ + + rga_img_info_t src; /* src image info */ + rga_img_info_t dst; /* dst image info */ + rga_img_info_t pat; /* patten image info */ + +#if defined(__arm64__) || defined(__aarch64__) + unsigned long rop_mask_addr; /* rop4 mask addr */ + unsigned long LUT_addr; /* LUT addr */ +#else + unsigned int rop_mask_addr; /* rop4 mask addr */ + unsigned int LUT_addr; /* LUT addr */ +#endif + + RECT_t clip; /* dst clip window default value is dst_vir */ + /* value from [0, w-1] / [0, h-1]*/ + + int sina; /* dst angle default value 0 16.16 scan from table */ + int cosa; /* dst angle default value 0 16.16 scan from table */ + + unsigned short alpha_rop_flag; /* alpha rop process flag */ + /* ([0] = 1 alpha_rop_enable) */ + /* ([1] = 1 rop enable) */ + /* ([2] = 1 fading_enable) */ + /* ([3] = 1 PD_enable) */ + /* ([4] = 1 alpha cal_mode_sel) */ + /* ([5] = 1 dither_enable) */ + /* ([6] = 1 gradient fill mode sel) */ + /* ([7] = 1 AA_enable) */ + + unsigned char scale_mode; /* 0 nearst / 1 bilnear / 2 bicubic */ + + unsigned int color_key_max; /* color key max */ + unsigned int color_key_min; /* color key min */ + + unsigned int fg_color; /* foreground color */ + unsigned int bg_color; /* background color */ + + COLOR_FILL gr_color; /* color fill use gradient */ + + line_draw_t line_draw_info; + + FADING fading; + + unsigned char PD_mode; /* porter duff alpha mode sel */ + + unsigned char alpha_global_value; /* global alpha value */ + + unsigned short rop_code; /* rop2/3/4 code scan from rop code table*/ + + unsigned char bsfilter_flag; /* [2] 0 blur 1 sharp / [1:0] filter_type*/ + + unsigned char palette_mode; /* (enum) color palatte 0/1bpp, 1/2bpp 2/4bpp 3/8bpp*/ + + unsigned char yuv2rgb_mode; /* (enum) BT.601 MPEG / BT.601 JPEG / BT.709 */ + + unsigned char endian_mode; /* 0/big endian 1/little endian*/ + + unsigned char rotate_mode; /* (enum) rotate mode */ + /* 0x0, no rotate */ + /* 0x1, rotate */ + /* 0x2, x_mirror */ + /* 0x3, y_mirror */ + + unsigned char color_fill_mode; /* 0 solid color / 1 patten color */ + + MMU mmu_info; /* mmu information */ + + unsigned char alpha_rop_mode; /* ([0~1] alpha mode) */ + /* ([2~3] rop mode) */ + /* ([4] zero mode en) */ + /* ([5] dst alpha mode) */ + + unsigned char src_trans_mode; + + unsigned char dither_mode; + + unsigned char CMD_fin_int_enable; + + /* completion is reported through a callback */ + void (*complete)(int retval); +}; + +#if 0 +typedef struct TILE_INFO +{ + int64_t matrix[4]; + + uint16_t tile_x_num; /* x axis tile num / tile size is 8x8 pixel */ + uint16_t tile_y_num; /* y axis tile num */ + + int16_t dst_x_tmp; /* dst pos x = (xstart - xoff) default value 0 */ + int16_t dst_y_tmp; /* dst pos y = (ystart - yoff) default value 0 */ + + uint16_t tile_w; + uint16_t tile_h; + int16_t tile_start_x_coor; + int16_t tile_start_y_coor; + int32_t tile_xoff; + int32_t tile_yoff; + + int32_t tile_temp_xstart; + int32_t tile_temp_ystart; + + /* src tile incr */ + int32_t x_dx; + int32_t x_dy; + int32_t y_dx; + int32_t y_dy; + + mdp_img_act dst_ctrl; + +} +TILE_INFO; +#endif + +#if 0 + +#define RGA_BASE 0x10114000 + +//General Registers +#define RGA_SYS_CTRL 0x000 +#define RGA_CMD_CTRL 0x004 +#define RGA_CMD_ADDR 0x008 +#define RGA_STATUS 0x00c +#define RGA_INT 0x010 +#define RGA_AXI_ID 0x014 +#define RGA_MMU_STA_CTRL 0x018 +#define RGA_MMU_STA 0x01c + +//Command code start +#define RGA_MODE_CTRL 0x100 + +//Source Image Registers +#define RGA_SRC_Y_MST 0x104 +#define RGA_SRC_CB_MST 0x108 +#define RGA_MASK_READ_MST 0x108 //repeat +#define RGA_SRC_CR_MST 0x10c +#define RGA_SRC_VIR_INFO 0x110 +#define RGA_SRC_ACT_INFO 0x114 +#define RGA_SRC_X_PARA 0x118 +#define RGA_SRC_Y_PARA 0x11c +#define RGA_SRC_TILE_XINFO 0x120 +#define RGA_SRC_TILE_YINFO 0x124 +#define RGA_SRC_TILE_H_INCR 0x128 +#define RGA_SRC_TILE_V_INCR 0x12c +#define RGA_SRC_TILE_OFFSETX 0x130 +#define RGA_SRC_TILE_OFFSETY 0x134 +#define RGA_SRC_BG_COLOR 0x138 +#define RGA_SRC_FG_COLOR 0x13c +#define RGA_LINE_DRAWING_COLOR 0x13c //repeat +#define RGA_SRC_TR_COLOR0 0x140 +#define RGA_CP_GR_A 0x140 //repeat +#define RGA_SRC_TR_COLOR1 0x144 +#define RGA_CP_GR_B 0x144 //repeat + +#define RGA_LINE_DRAW 0x148 +#define RGA_PAT_START_POINT 0x148 //repeat + +//Destination Image Registers +#define RGA_DST_MST 0x14c +#define RGA_LUT_MST 0x14c //repeat +#define RGA_PAT_MST 0x14c //repeat +#define RGA_LINE_DRAWING_MST 0x14c //repeat + +#define RGA_DST_VIR_INFO 0x150 + +#define RGA_DST_CTR_INFO 0x154 +#define RGA_LINE_DRAW_XY_INFO 0x154 //repeat + +//Alpha/ROP Registers +#define RGA_ALPHA_CON 0x158 + +#define RGA_PAT_CON 0x15c +#define RGA_DST_VIR_WIDTH_PIX 0x15c //repeat + +#define RGA_ROP_CON0 0x160 +#define RGA_CP_GR_G 0x160 //repeat +#define RGA_PRESCL_CB_MST 0x160 //repeat + +#define RGA_ROP_CON1 0x164 +#define RGA_CP_GR_R 0x164 //repeat +#define RGA_PRESCL_CR_MST 0x164 //repeat + +//MMU Register +#define RGA_FADING_CON 0x168 +#define RGA_MMU_CTRL 0x168 //repeat + +#define RGA_MMU_TBL 0x16c //repeat + + +#define RGA_BLIT_COMPLETE_EVENT 1 + +#endif + +int +RGA_set_src_act_info( + struct rga_req *req, + unsigned int width, /* act width */ + unsigned int height, /* act height */ + unsigned int x_off, /* x_off */ + unsigned int y_off /* y_off */ + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_src_vir_info( + struct rga_req *req, + unsigned long yrgb_addr, /* yrgb_addr */ + unsigned long uv_addr, /* uv_addr */ + unsigned long v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + unsigned char format, /* format */ + unsigned char a_swap_en /* only for 32bit RGB888 format */ + ); +#else +int +RGA_set_src_vir_info( + struct rga_req *req, + unsigned int yrgb_addr, /* yrgb_addr */ + unsigned int uv_addr, /* uv_addr */ + unsigned int v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + unsigned char format, /* format */ + unsigned char a_swap_en /* only for 32bit RGB888 format */ + ); +#endif + +int +RGA_set_dst_act_info( + struct rga_req *req, + unsigned int width, /* act width */ + unsigned int height, /* act height */ + unsigned int x_off, /* x_off */ + unsigned int y_off /* y_off */ + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_dst_vir_info( + struct rga_req *msg, + unsigned long yrgb_addr, /* yrgb_addr */ + unsigned long uv_addr, /* uv_addr */ + unsigned long v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + RECT_t *clip, /* clip window */ + unsigned char format, /* format */ + unsigned char a_swap_en + ); +#else +int +RGA_set_dst_vir_info( + struct rga_req *msg, + unsigned int yrgb_addr, /* yrgb_addr */ + unsigned int uv_addr, /* uv_addr */ + unsigned int v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + RECT_t *clip, /* clip window */ + unsigned char format, /* format */ + unsigned char a_swap_en + ); +#endif + +int +RGA_set_pat_info( + struct rga_req *msg, + unsigned int width, + unsigned int height, + unsigned int x_off, + unsigned int y_off, + unsigned int pat_format + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_rop_mask_info( + struct rga_req *msg, + unsigned long rop_mask_addr, + unsigned int rop_mask_endian_mode + ); +#else +int +RGA_set_rop_mask_info( + struct rga_req *msg, + unsigned int rop_mask_addr, + unsigned int rop_mask_endian_mode + ); +#endif + +int RGA_set_alpha_en_info( + struct rga_req *msg, + unsigned int alpha_cal_mode, /* 0:alpha' = alpha + (alpha>>7) | alpha' = alpha */ + unsigned int alpha_mode, /* 0 global alpha / 1 per pixel alpha / 2 mix mode */ + unsigned int global_a_value, + unsigned int PD_en, /* porter duff alpha mode en */ + unsigned int PD_mode, + unsigned int dst_alpha_en ); /* use dst alpha */ + +int +RGA_set_rop_en_info( + struct rga_req *msg, + unsigned int ROP_mode, + unsigned int ROP_code, + unsigned int color_mode, + unsigned int solid_color + ); + + +int +RGA_set_fading_en_info( + struct rga_req *msg, + unsigned char r, + unsigned char g, + unsigned char b + ); + +int +RGA_set_src_trans_mode_info( + struct rga_req *msg, + unsigned char trans_mode, + unsigned char a_en, + unsigned char b_en, + unsigned char g_en, + unsigned char r_en, + unsigned char color_key_min, + unsigned char color_key_max, + unsigned char zero_mode_en + ); + + +int +RGA_set_bitblt_mode( + struct rga_req *msg, + unsigned char scale_mode, // 0/near 1/bilnear 2/bicubic + unsigned char rotate_mode, // 0/copy 1/rotate_scale 2/x_mirror 3/y_mirror + unsigned int angle, // rotate angle + unsigned int dither_en, // dither en flag + unsigned int AA_en, // AA flag + unsigned int yuv2rgb_mode + ); + + +int +RGA_set_color_palette_mode( + struct rga_req *msg, + unsigned char palette_mode, /* 1bpp/2bpp/4bpp/8bpp */ + unsigned char endian_mode, /* src endian mode sel */ + unsigned int bpp1_0_color, /* BPP1 = 0 */ + unsigned int bpp1_1_color /* BPP1 = 1 */ + ); + + +int +RGA_set_color_fill_mode( + struct rga_req *msg, + COLOR_FILL *gr_color, /* gradient color part */ + unsigned char gr_satur_mode, /* saturation mode */ + unsigned char cf_mode, /* patten fill or solid fill */ + unsigned int color, /* solid color */ + unsigned short pat_width, /* pattern width */ + unsigned short pat_height, /* pattern height */ + unsigned char pat_x_off, /* pattern x offset */ + unsigned char pat_y_off, /* pattern y offset */ + unsigned char aa_en /* alpha en */ + ); + + +int +RGA_set_line_point_drawing_mode( + struct rga_req *msg, + POINT_t sp, /* start point */ + POINT_t ep, /* end point */ + unsigned int color, /* line point drawing color */ + unsigned int line_width, /* line width */ + unsigned char AA_en, /* AA en */ + unsigned char last_point_en /* last point en */ + ); + + +int +RGA_set_blur_sharp_filter_mode( + struct rga_req *msg, + unsigned char filter_mode, /* blur/sharpness */ + unsigned char filter_type, /* filter intensity */ + unsigned char dither_en /* dither_en flag */ + ); + +int +RGA_set_pre_scaling_mode( + struct rga_req *msg, + unsigned char dither_en + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_update_palette_table_mode( + struct rga_req *msg, + unsigned long LUT_addr, /* LUT table addr */ + unsigned int palette_mode /* 1bpp/2bpp/4bpp/8bpp */ + ); +#else +int +RGA_update_palette_table_mode( + struct rga_req *msg, + unsigned int LUT_addr, /* LUT table addr */ + unsigned int palette_mode /* 1bpp/2bpp/4bpp/8bpp */ + ); +#endif + +int +RGA_set_update_patten_buff_mode( + struct rga_req *msg, + unsigned int pat_addr, /* patten addr */ + unsigned int w, /* patten width */ + unsigned int h, /* patten height */ + unsigned int format /* patten format */ + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_mmu_info( + struct rga_req *msg, + unsigned char mmu_en, + unsigned char src_flush, + unsigned char dst_flush, + unsigned char cmd_flush, + unsigned long base_addr, + unsigned char page_size + ); +#else +int +RGA_set_mmu_info( + struct rga_req *msg, + unsigned char mmu_en, + unsigned char src_flush, + unsigned char dst_flush, + unsigned char cmd_flush, + unsigned int base_addr, + unsigned char page_size + ); +#endif + +void rga_set_fds_offsets( + struct rga_req *rga_request, + unsigned short src_fd, + unsigned short dst_fd, + unsigned int src_offset, + unsigned int dst_offset); + +int +RGA_set_src_fence_flag( + struct rga_req *msg, + int acq_fence, + int src_flag +); + + +int +RGA_set_dst_fence_flag( + struct rga_req *msg, + int dst_flag +); + +int +RGA_get_dst_fence( + struct rga_req *msg +); +#ifdef __cplusplus +} +#endif + +#endif /*_RK29_IPP_DRIVER_H_*/ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image.h new file mode 100644 index 0000000..196dfd5 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image.h @@ -0,0 +1,7559 @@ +/* stb_image - v2.23 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt JR Smith github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image_resize.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image_resize.h new file mode 100644 index 0000000..4f6ad35 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image_resize.h @@ -0,0 +1,2630 @@ +/* stb_image_resize - v0.96 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb + + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + // WRAP/REFLECT/ZERO + + FULL API + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + SRGB & FLOATING POINT REPRESENTATION + The sRGB functions presume IEEE floating point. If you do not have + IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use + a slower implementation. + + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: + + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... + + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) some_func(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the 50/50 average of 99% transparent bright green + and 1% transparent black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source image.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + CONTRIBUTORS + Jorge L Rodriguez: Implementation + Sean Barrett: API design, optimizations + Aras Pranckevicius: bugfix + Nathan Reed: warning fixes + + REVISIONS + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. + + TODO + Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When processing wide images, break processing into tiles so data fits in L1 cache + Installable filters? + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + + +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. + +#define STBIR_ALPHA_CHANNEL_NONE -1 + +// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Alpha-channel can be processed separately +// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE +// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 +} stbir_filter; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + + + +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_datatype; + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset); + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1); +// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +// For memset +#include + +#include + +#ifndef STBIR_MALLOC +#include +// use comma operator to evaluate c, to avoid "unused parameter" warnings +#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) +#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) +#endif + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR__NOTUSED(v) (void)(v) +#else +#define STBIR__NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); + +typedef struct +{ + stbir__kernel_fn* kernel; + stbir__support_fn* support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors* vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; +} stbir__info; + + +static const float stbir__max_uint8_as_float = 255.0f; +static const float stbir__max_uint16_as_float = 65535.0f; +static const double stbir__max_uint32_as_float = 4294967295.0; + + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else +// sRGB transition values, scaled by 1<<28 +static int stbir__srgb_offset_to_linear_scaled[256] = +{ + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + + return (stbir_uint8) v; +} +#endif + +static float stbir__filter_trapezoid(float x, float scale) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + + x = (float)fabs(x); + + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale) +{ + STBIR_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_triangle(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_cubic(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + + return (0.0f); +} + +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s); + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s); + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s); + return 2; +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +{ + return stbir__get_filter_pixel_width(filter, scale) / 2; +} + +static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +{ + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); +} + +static int stbir__get_total_horizontal_coefficients(stbir__info* info) +{ + return info->horizontal_num_contributors + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); +} + +static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return info->vertical_num_contributors + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +{ + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + // NOTREACHED + + default: + STBIR_ASSERT(!"Unimplemented edge type"); + return 0; + } +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + + STBIR_ASSERT(total_filter > 0.9); + STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + + STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) +{ + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; + + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } + + STBIR_ASSERT(total > 0.9f); + STBIR_ASSERT(total < 1.1f); + + scale = 1 / total; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } + + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; + + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; + + contributors[j].n0 += skip; + + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } + + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; + + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } + + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); +} + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + + stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + size_t input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } + + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + { + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +#ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +#endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + stbir_info->ring_buffer_last_scanline = n; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR_ASSERT(n1 >= n0); + STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; + + STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; + + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). + } + } + + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x = 0, num_nonalpha = 0; x < channels; ++x) + { + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + { + nonalpha[num_nonalpha++] = (stbir_uint16)x; + } + } + + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) + #endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +{ + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + + info->horizontal_shift = s0 * info->output_w / (s1 - s0); + info->vertical_shift = t0 * info->output_h / (t1 - t0); + } +} + +static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) +{ + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; +} + +static stbir_uint32 stbir__calculate_memory(stbir__info *info) +{ + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + info->ring_buffer_num_entries = filter_height + 1; + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; + + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + } + + if (alpha_channel >= info->channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +static int stbir__resize_arbitrary( + void *alloc_context, + const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory, alloc_context); + + return result; +} + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image_write.h b/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image_write.h new file mode 100644 index 0000000..c117344 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/3rdparty/stb/stb_image_write.h @@ -0,0 +1,1619 @@ +/* stb_image_write - v1.13 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBI_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a; arr[1] = b; arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_WANT_SECURE_LIB__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = snprintf(buffer, 128, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + float r, g, b; + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/CMakeLists.txt new file mode 100644 index 0000000..b3b9339 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_batch_inference_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +# utils +include_directories(${CMAKE_SOURCE_DIR}/src/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_batch_inference_demo + src/main.cc +) + +target_link_libraries(rknn_batch_inference_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_batch_inference_demo) +install(TARGETS rknn_batch_inference_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/README.md new file mode 100644 index 0000000..4a4f668 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_batch_inference_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_batch_inference_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_batch_inference_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/build.sh new file mode 100644 index 0000000..70781f8 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_batch_inference_demo/ +cp run_rv1109_rv1126.sh install/rknn_batch_inference_demo/ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/dog_224x224.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/dog_224x224.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rk180x/mobilenet_v1_rk1808_batch_4.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rk180x/mobilenet_v1_rk1808_batch_4.rknn new file mode 100644 index 0000000..a0f9b4b Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rk180x/mobilenet_v1_rk1808_batch_4.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rv1109_rv1126/mobilenet_v1_rv1109_rv1126_batch_4.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rv1109_rv1126/mobilenet_v1_rv1109_rv1126_batch_4.rknn new file mode 100644 index 0000000..8103d58 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rv1109_rv1126/mobilenet_v1_rv1109_rv1126_batch_4.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rk180x.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rk180x.sh new file mode 100644 index 0000000..2ff2d2a --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rk180x.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rk180x" +./rknn_batch_inference_demo model/${chip_dir}/mobilenet_v1_rk1808_batch_4.rknn model/dog_224x224.jpg \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..efac954 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rv1109_rv1126.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +./rknn_batch_inference_demo model/${chip_dir}/mobilenet_v1_rv1109_rv1126_batch_4.rknn model/dog_224x224.jpg \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/src/main.cc new file mode 100644 index 0000000..6cd5a6b --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_batch_inference_demo/src/main.cc @@ -0,0 +1,330 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "rknn_api.h" + +using namespace std; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static int rknn_GetTop( + float *pfProb, + float *pfMaxProb, + uint32_t *pMaxClass, + uint32_t outputCount, + uint32_t topNum) +{ + uint32_t i, j; + +#define MAX_TOP_NUM 20 + if (topNum > MAX_TOP_NUM) + return 0; + + memset(pfMaxProb, 0, sizeof(float) * topNum); + memset(pMaxClass, 0xff, sizeof(float) * topNum); + + for (j = 0; j < topNum; j++) + { + for (i = 0; i < outputCount; i++) + { + if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || + (i == *(pMaxClass + 3)) || (i == *(pMaxClass + 4))) + { + continue; + } + + if (pfProb[i] > *(pfMaxProb + j)) + { + *(pfMaxProb + j) = pfProb[i]; + *(pMaxClass + j) = i; + } + } + } + + return 1; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + const int MODEL_IN_WIDTH = 224; + const int MODEL_IN_HEIGHT = 224; + const int MODEL_IN_CHANNELS = 3; + const int MODEL_IN_BATCHSIZE = 4; + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + + // Load RKNN Model + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image + unsigned char *input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) + { + return -1; + } + + //concat img to batch + int img_size = input_attrs[0].dims[0] * input_attrs[0].dims[1] * input_attrs[0].dims[2] * sizeof(uint8_t); + unsigned char *in_data_batch = NULL; + in_data_batch = (unsigned char *)malloc(MODEL_IN_BATCHSIZE * img_size); + if (!in_data_batch) + { + return -1; + } + for (int i = 0; i < MODEL_IN_BATCHSIZE; ++i) + { + unsigned char *in_data_ptr = in_data_batch + img_size * i; + memcpy(in_data_ptr, input_data, img_size); + } + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = in_data_batch; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + for (int n = 0; n < MODEL_IN_BATCHSIZE; ++n) + { + for (int i = 0; i < io_num.n_output; i++) + { + uint32_t MaxClass[5]; + float fMaxProb[5]; + float *buffer_f32 = (float *)outputs[i].buf; + float *cur_batch_buf = buffer_f32 + 1001 * n; + uint32_t cur_batch_sz = outputs[i].size / (4 * MODEL_IN_BATCHSIZE); + rknn_GetTop(cur_batch_buf, fMaxProb, MaxClass, cur_batch_sz, 5); + + printf(" --- Top5 ---\n"); + for (int i = 0; i < 5; i++) + { + printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]); + } + } + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + if (input_data) + { + free(input_data); + } + if (in_data_batch) + { + stbi_image_free(in_data_batch); + } + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/CMakeLists.txt new file mode 100644 index 0000000..c2d48ec --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_mobilenet_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_mobilenet_demo + src/main.cc +) + +target_link_libraries(rknn_mobilenet_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_mobilenet_demo) +install(TARGETS rknn_mobilenet_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/README.md new file mode 100644 index 0000000..7a302a2 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_mobilenet_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_mobilenet_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_mobilenet_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/build.sh new file mode 100644 index 0000000..c869f2a --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +# GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/cat_224x224.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/cat_224x224.jpg new file mode 100644 index 0000000..11c5ba4 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/cat_224x224.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/dog_224x224.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/dog_224x224.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rk180x.rknn new file mode 100644 index 0000000..b02f728 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rv1109_rv1126.rknn new file mode 100644 index 0000000..b0f6735 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/src/main.cc new file mode 100644 index 0000000..b0f7de2 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_mobilenet_demo/src/main.cc @@ -0,0 +1,327 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "rknn_api.h" + +using namespace std; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static inline int64_t getCurrentTimeUs() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static int rknn_GetTop( + float *pfProb, + float *pfMaxProb, + uint32_t *pMaxClass, + uint32_t outputCount, + uint32_t topNum) +{ + uint32_t i, j; + +#define MAX_TOP_NUM 20 + if (topNum > MAX_TOP_NUM) + return 0; + + memset(pfMaxProb, 0, sizeof(float) * topNum); + memset(pMaxClass, 0xff, sizeof(float) * topNum); + + for (j = 0; j < topNum; j++) + { + for (i = 0; i < outputCount; i++) + { + if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || + (i == *(pMaxClass + 3)) || (i == *(pMaxClass + 4))) + { + continue; + } + + if (pfProb[i] > *(pfMaxProb + j)) + { + *(pfMaxProb + j) = pfProb[i]; + *(pMaxClass + j) = i; + } + } + } + + return 1; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + int loop_count = 1; + if (argc > 3) + { + loop_count = atoi(argv[3]); + } + + // Load RKNN Model + model = load_model(model_path, &model_len); + + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image + unsigned char *input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) + { + return -1; + } + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = input_data; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + for (int n = 0; n < loop_count; ++n) + { + int64_t start_us = getCurrentTimeUs(); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + int64_t elapse_us = getCurrentTimeUs() - start_us; + printf("%4d: Elapse Time = %.2fms, FPS = %.2f\n", n, elapse_us / 1000.f, 1000.f * 1000.f / elapse_us); + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + for (int i = 0; i < io_num.n_output; i++) + { + uint32_t MaxClass[5]; + float fMaxProb[5]; + float *buffer = (float *)outputs[i].buf; + uint32_t sz = outputs[i].size / 4; + + rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5); + + printf(" --- Top5 ---\n"); + for (int i = 0; i < 5; i++) + { + printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]); + } + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + + if (input_data) + { + stbi_image_free(input_data); + } + + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/CMakeLists.txt new file mode 100644 index 0000000..3326955 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_multi_input_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +# utils +include_directories(${CMAKE_SOURCE_DIR}/src/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_multi_input_demo + src/main.cc +) + +target_link_libraries(rknn_multi_input_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_multi_input_demo) +install(TARGETS rknn_multi_input_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/README.md new file mode 100644 index 0000000..6782968 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_multi_input_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_multi_input_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_multi_input_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/build.sh new file mode 100644 index 0000000..cbcd900 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_multi_input_demo/ +cp run_rv1109_rv1126.sh install/rknn_multi_input_demo/ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/dog_128x128_gray.png b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/dog_128x128_gray.png new file mode 100644 index 0000000..3b3c325 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/dog_128x128_gray.png differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/rk180x/multi_input_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/rk180x/multi_input_rk180x.rknn new file mode 100644 index 0000000..c3c5921 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/rk180x/multi_input_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/rv1109_rv1126/multi_input_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/rv1109_rv1126/multi_input_rv1109_rv1126.rknn new file mode 100644 index 0000000..4232100 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/model/rv1109_rv1126/multi_input_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/run_rk180x.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/run_rk180x.sh new file mode 100644 index 0000000..1f61582 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/run_rk180x.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rk180x" +./rknn_multi_input_demo model/${chip_dir}/multi_input_rk180x.rknn model/dog_128x128_gray.png \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..4b364fd --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/run_rv1109_rv1126.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +./rknn_multi_input_demo model/${chip_dir}/multi_input_rv1109_rv1126.rknn model/dog_128x128_gray.png \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/src/main.cc new file mode 100644 index 0000000..1c26d20 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_multi_input_demo/src/main.cc @@ -0,0 +1,284 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "rknn_api.h" + +using namespace std; + +#define DEMO_INPUT_NUM 4 + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + const int MODEL_IN_WIDTH = 128; + const int MODEL_IN_HEIGHT = 128; + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + + // Load RKNN Model + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + assert(DEMO_INPUT_NUM == io_num.n_input); + // Set Input struct + rknn_input inputs[DEMO_INPUT_NUM]; + memset(inputs, 0, DEMO_INPUT_NUM * sizeof(rknn_input)); + std::vector imgs; + for (int i = 0; i < io_num.n_input; ++i) + { + int channel = 0; + if (input_attrs[i].fmt == RKNN_TENSOR_NCHW) + { + channel = input_attrs[i].dims[2]; + } + else + { + channel = input_attrs[i].dims[0]; + } + //convert single channel to multi channels + // Load image + unsigned char *input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) + { + return -1; + } + imgs.push_back(input_data); + int img_size = input_attrs[0].size; + inputs[i].index = i; + inputs[i].type = RKNN_TENSOR_UINT8; + inputs[i].size = img_size; + inputs[i].fmt = RKNN_TENSOR_NHWC; + } + for (int i = 0; i < io_num.n_input; ++i) + { + inputs[i].buf = imgs[i]; + } + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + float ref_data[5] = {24.482, 20.453, 20.453, 20.453, 20.453}; + float *buffer = (float *)outputs[0].buf; + printf("first 5 result:\n"); + for (int i = 0; i < 5; ++i) + { + printf("[%d]:%.3f vs %.3f\n", i, buffer[i], ref_data[i]); + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + for (auto d : imgs) + { + stbi_image_free(d); + } + + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/CMakeLists.txt new file mode 100644 index 0000000..1cf20d4 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_pass_through_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +# utils +include_directories(${CMAKE_SOURCE_DIR}/src/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_pass_through_demo + src/main.cc + src/quant_utils.cc +) + +target_link_libraries(rknn_pass_through_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_pass_through_demo) +install(TARGETS rknn_pass_through_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/README.md new file mode 100644 index 0000000..80d9556 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_pass_through_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_pass_through_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_pass_through_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh int8 +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh int8 +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/build.sh new file mode 100644 index 0000000..b39e372 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_pass_through_demo/ +cp run_rv1109_rv1126.sh install/rknn_pass_through_demo/ \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/cat_224x224.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/cat_224x224.jpg new file mode 100644 index 0000000..11c5ba4 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/cat_224x224.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/dog_224x224.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/dog_224x224.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_FP_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_FP_rk180x.rknn new file mode 100644 index 0000000..a6c328a Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_FP_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT16_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT16_rk180x.rknn new file mode 100644 index 0000000..61dc82d Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT16_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT8_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT8_rk180x.rknn new file mode 100644 index 0000000..b622a8e Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT8_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_UINT8_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_UINT8_rk180x.rknn new file mode 100644 index 0000000..c86d218 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_UINT8_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_FP_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_FP_rv1109_rv1126.rknn new file mode 100644 index 0000000..651fcac Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_FP_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT16_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT16_rv1109_rv1126.rknn new file mode 100644 index 0000000..46b5aaf Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT16_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT8_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT8_rv1109_rv1126.rknn new file mode 100644 index 0000000..eed99da Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT8_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_UINT8_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_UINT8_rv1109_rv1126.rknn new file mode 100644 index 0000000..3308572 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_UINT8_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/run_rk180x.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/run_rk180x.sh new file mode 100644 index 0000000..ffb9880 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/run_rk180x.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +chip_dir="rk180x" +case $1 in + uint8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_UINT8_rk180x.rknn model/dog_224x224.jpg + ;; + int8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT8_rk180x.rknn model/dog_224x224.jpg + ;; + int16) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT16_rk180x.rknn model/dog_224x224.jpg + ;; + fp) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_FP_rk180x.rknn model/dog_224x224.jpg + ;; + *) + echo './run.sh uint8 int8 int16 fp' + ;; +esac + + diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..4f7166e --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/run_rv1109_rv1126.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +case $1 in + uint8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_UINT8_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + int8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT8_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + int16) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT16_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + fp) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_FP_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + *) + echo './run.sh uint8 int8 int16 fp' + ;; +esac + + diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/main.cc new file mode 100644 index 0000000..59108c4 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/main.cc @@ -0,0 +1,484 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "quant_utils.h" +#include "rknn_api.h" + +using namespace std; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d " + "fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], + attr->dims[1], attr->dims[0], attr->n_elems, attr->size, 0, attr->type, + attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static int GetElementByte(rknn_tensor_attr *in_attr) +{ + int byte = 0; + switch (in_attr->type) + { + case RKNN_TENSOR_FLOAT32: + byte = 4; + break; + case RKNN_TENSOR_FLOAT16: + case RKNN_TENSOR_INT16: + byte = 2; + break; + case RKNN_TENSOR_INT8: + case RKNN_TENSOR_UINT8: + byte = 1; + break; + default: + break; + } + return byte; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +static int ProcessInput(unsigned char *src_buf, void **dst_buf, rknn_tensor_attr *in_attr, + std::vector mean, std::vector scale, + bool isReorder210, bool isNCHW) +{ + // check + if (3 != mean.size() || src_buf == NULL) + { + return -1; + } + int img_height, img_width, img_channels = 0; + if (isNCHW) + { + img_width = in_attr->dims[0]; + img_height = in_attr->dims[1]; + img_channels = in_attr->dims[2]; + } + else + { + img_channels = in_attr->dims[0]; + img_width = in_attr->dims[1]; + img_height = in_attr->dims[2]; + } + int HW = img_height * img_width; + int ele_count = HW * img_channels; + int ele_bytes = GetElementByte(in_attr); + printf("total element count = %d, bytes per element = %d\n", ele_count, + ele_bytes); + float *pixel_f = (float *)malloc(ele_count * sizeof(float)); + + // RGB2BGR + if (isReorder210 == true) + { + printf("perform RGB2BGR\n"); + float *f_ptr = pixel_f; + unsigned char *u_ptr = src_buf; + for (int i = 0; i < HW; ++i) + { + f_ptr[2] = u_ptr[0]; + f_ptr[1] = u_ptr[1]; + f_ptr[0] = u_ptr[2]; + u_ptr += img_channels; + f_ptr += img_channels; + } + } + else + { + float *f_ptr = pixel_f; + unsigned char *u_ptr = src_buf; + for (int i = 0; i < ele_count; ++i) + { + f_ptr[i] = u_ptr[i]; + } + } + + // normalize + printf("perform normalize\n"); + float *f_ptr = pixel_f; + for (int i = 0; i < HW; ++i) + { + for (int j = 0; j < img_channels; ++j) + { + f_ptr[j] -= mean[j]; + f_ptr[j] /= scale[j]; + } + f_ptr += img_channels; + } + + // quantize + printf("perform quantize\n"); + uint8_t *qnt_buf = (uint8_t *)malloc(ele_count * ele_bytes); + switch (in_attr->type) + { + case RKNN_TENSOR_FLOAT32: + memcpy(qnt_buf, pixel_f, ele_count * ele_bytes); + break; + case RKNN_TENSOR_FLOAT16: + f32_to_f16((uint16_t *)(qnt_buf), pixel_f, ele_count); + break; + case RKNN_TENSOR_INT16: + case RKNN_TENSOR_INT8: + case RKNN_TENSOR_UINT8: + switch (in_attr->qnt_type) + { + case RKNN_TENSOR_QNT_DFP: + qnt_f32_to_dfp(qnt_buf, in_attr->type, in_attr->fl, pixel_f, ele_count); + break; + case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + qnt_f32_to_affine(qnt_buf, in_attr->type, in_attr->zp, in_attr->scale, pixel_f, ele_count); + break; + case RKNN_TENSOR_QNT_NONE: + qnt_f32_to_none(qnt_buf, in_attr->type, pixel_f, ele_count); + default: + break; + } + break; + default: + break; + } + + // NHWC ==> NCHW + if (isNCHW) + { + printf("perform NHWC to NCHW\n"); + uint8_t *nchw_buf = (uint8_t *)malloc(ele_count * ele_bytes); + uint8_t *dst_ptr = nchw_buf; + uint8_t *src_ptr = qnt_buf; + for (int i = 0; i < img_channels; ++i) + { + src_ptr = qnt_buf + i * ele_bytes; + dst_ptr = nchw_buf + i * HW * ele_bytes; + for (int j = 0; j < HW; ++j) + { + // dst_ptr[i*HW+j] = src_ptr[j*C+i]; + memcpy(dst_ptr, src_ptr, ele_bytes); + src_ptr += img_channels * ele_bytes; + dst_ptr += ele_bytes; + } + } + *dst_buf = nchw_buf; + free(qnt_buf); + } + else + { + *dst_buf = qnt_buf; + } + + free(pixel_f); + + return 0; +} + +static int rknn_GetTop( + float *pfProb, + float *pfMaxProb, + uint32_t *pMaxClass, + uint32_t outputCount, + uint32_t topNum) +{ + uint32_t i, j; + +#define MAX_TOP_NUM 20 + if (topNum > MAX_TOP_NUM) + return 0; + + memset(pfMaxProb, 0, sizeof(float) * topNum); + memset(pMaxClass, 0xff, sizeof(float) * topNum); + + for (j = 0; j < topNum; j++) + { + for (i = 0; i < outputCount; i++) + { + if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || + (i == *(pMaxClass + 3)) || (i == *(pMaxClass + 4))) + { + continue; + } + + if (pfProb[i] > *(pfMaxProb + j)) + { + *(pfMaxProb + j) = pfProb[i]; + *(pMaxClass + j) = i; + } + } + } + + return 1; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + const int MODEL_IN_WIDTH = 224; + const int MODEL_IN_HEIGHT = 224; + const int MODEL_IN_CHANNELS = 3; + bool isNCHW = false; + // mobilenet v2 mean/scale in rknn.config() + bool isReorder210 = true; /* reorder= '2 1 0' */ + std::vector mean({103.94, 116.78, 123.68}); + std::vector scale({58.82, 58.82, 58.82}); + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + + // Load RKNN Model + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, + io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image + unsigned char *img_data = NULL; + img_data = load_image(img_path, &input_attrs[0]); + if (!img_data) + { + return -1; + } + + // rknn model need nchw layout input + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) + { + isNCHW = true; + } + + // process input when pass_through = 1 + void *in_data = NULL; + ProcessInput(img_data, &in_data, &(input_attrs[0]), mean, scale, isReorder210, + isNCHW); + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NCHW; + inputs[0].buf = in_data; + inputs[0].pass_through = 1; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + for (int i = 0; i < io_num.n_output; i++) + { + uint32_t MaxClass[5]; + float fMaxProb[5]; + float *buffer = (float *)outputs[i].buf; + uint32_t sz = outputs[i].size / 4; + + rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5); + + printf(" --- Top5 ---\n"); + for (int i = 0; i < 5; i++) + { + printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]); + } + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + stbi_image_free(img_data); + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.cc new file mode 100644 index 0000000..d59e11c --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.cc @@ -0,0 +1,181 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "quant_utils.h" +#include "rknn_api.h" + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + // return (int32_t)((f > 0.0) ? (f + 0.5) : (f - 0.5)); //四舍五入 + return f; +} + +void f32_to_f16(uint16_t *f16, float *f32, int num) +{ + float *src = f32; + uint16_t *dst = f16; + int i = 0; + + for (; i < num; i++) + { + float in = *src; + + uint32_t fp32 = *((uint32_t *)&in); + uint32_t t1 = (fp32 & 0x80000000u) >> 16; /* sign bit. */ + uint32_t t2 = (fp32 & 0x7F800000u) >> 13; /* Exponent bits */ + uint32_t t3 = (fp32 & 0x007FE000u) >> 13; /* Mantissa bits, no rounding */ + uint32_t fp16 = 0u; + + if (t2 >= 0x023c00u) + { + fp16 = t1 | 0x7BFF; /* Don't round to infinity. */ + } + else if (t2 <= 0x01c000u) + { + fp16 = t1; + } + else + { + t2 -= 0x01c000u; + fp16 = t1 | t2 | t3; + } + + *dst = (uint16_t)fp16; + + src++; + dst++; + } +} + +void qnt_f32_to_dfp(uint8_t *qnt, uint8_t type, int8_t fl, float *f32, + int num) +{ + float *src_ptr = f32; + int i = 0; + float dst_val = 0.0; + + switch (type) + { + case RKNN_TENSOR_INT8: + for (; i < num; i++) + { + dst_val = (fl > 0) ? ((*src_ptr) * ((float)(1 << fl))) + : ((*src_ptr) / (float)(1 << -fl)); + *((int8_t *)qnt) = (int8_t)__clip(dst_val, -128, 127); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_UINT8: + for (; i < num; i++) + { + dst_val = (fl > 0) ? ((*src_ptr) * ((float)(1 << fl))) + : ((*src_ptr) / (float)(1 << -fl)); + *qnt = (uint8_t)__clip(dst_val, 0, 255); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_INT16: + for (; i < num; i++) + { + dst_val = (fl > 0) ? ((*src_ptr) * ((float)(1 << fl))) + : ((*src_ptr) / (float)(1 << -fl)); + *((int16_t *)qnt) = (int16_t)__clip(dst_val, -32768, 32767); + src_ptr++; + qnt += 2; + } + break; + default: + break; + } +} + +void qnt_f32_to_affine(uint8_t *qnt, uint8_t type, uint8_t zp, float scale, + float *f32, int num) +{ + float *src_ptr = f32; + int i = 0; + float dst_val = 0.0; + + switch (type) + { + case RKNN_TENSOR_INT8: + for (; i < num; i++) + { + dst_val = ((*src_ptr) / scale) + zp; + *((int8_t *)qnt) = (int8_t)__clip(dst_val, -128, 127); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_UINT8: + for (; i < num; i++) + { + dst_val = ((*src_ptr) / scale) + zp; + *qnt = (uint8_t)__clip(dst_val, 0, 255); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_INT16: + for (; i < num; i++) + { + dst_val = ((*src_ptr) / scale) + zp; + *((int16_t *)qnt) = (int16_t)__clip(dst_val, -32768, 32767); + src_ptr++; + qnt += 2; + } + break; + default: + break; + } +} + +void qnt_f32_to_none(uint8_t *qnt, uint8_t type, float *f32, int num) +{ + float *src_ptr = f32; + int i = 0; + + switch (type) + { + case RKNN_TENSOR_INT8: + for (; i < num; i++) + { + *((int8_t *)qnt) = (int8_t)__clip(*src_ptr, -128, 127); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_UINT8: + for (; i < num; i++) + { + *qnt = (uint8_t)__clip(*src_ptr, 0, 255); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_INT16: + for (; i < num; i++) + { + *((int16_t *)qnt) = (int16_t)__clip(*src_ptr, -32768, 32767); + src_ptr++; + qnt += 2; + } + break; + default: + break; + } +} \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.h b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.h new file mode 100644 index 0000000..f6fa12e --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.h @@ -0,0 +1,28 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _RKNN_DEMO_QUANT_UTILS_H_ +#define _RKNN_DEMO_QUANT_UTILS_H_ + +#include + +void f32_to_f16(uint16_t *f16, float *f32, int num); + +void qnt_f32_to_dfp(uint8_t *qnt, uint8_t type, int8_t fl, float *f32, int num); + +void qnt_f32_to_affine(uint8_t *qnt, uint8_t type, uint8_t zp, float scale, float *f32, int num); + +void qnt_f32_to_none(uint8_t *qnt, uint8_t type, float *f32, int num); + +#endif /*_RKNN_DEMO_QUANT_UTILS_H_ */ \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/CMakeLists.txt new file mode 100644 index 0000000..0fb7f63 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_ssd_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_ssd_demo + src/main.cc + src/ssd.cc + ) + +target_link_libraries(rknn_ssd_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_ssd_demo) +install(TARGETS rknn_ssd_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/README.md new file mode 100644 index 0000000..bae5217 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/README.md @@ -0,0 +1,39 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_ssd_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_ssd_demo /userdata/ +``` + +- If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + + +## Run + +``` +adb shell +cd /userdata/rknn_ssd_demo/ +``` + +- RK1808/RK1806 +``` +./rknn_ssd_demo model/ssd_inception_v2_rk180x.rknn model/road.bmp +``` + +- RV1109/RV1126 +``` +./rknn_ssd_demo model/ssd_inception_v2_rv1109_rv1126.rknn model/road.bmp +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/build.sh new file mode 100644 index 0000000..6ddeae5 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=/work/projects/rv1109/git/rv1109/prebuilts/gcc/linux-x86/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=~/opts/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/box_priors.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/box_priors.txt new file mode 100644 index 0000000..efc3be4 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/box_priors.txt @@ -0,0 +1,4 @@ + 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.16666667 0.16666667 0.16666666 0.16666667 0.16666669 0.16666667 0.16666667 0.16666667 0.16666666 0.16666667 0.16666669 0.16666667 0.16666667 0.16666667 0.16666666 0.16666667 0.16666669 0.16666667 0.5 0.5 0.49999997 0.5 0.5 0.5 0.5 0.5 0.49999997 0.5 0.5 0.5 0.5 0.5 0.49999997 0.5 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.25 0.25 0.25 0.24999999 0.25 0.25 0.25 0.25 0.25 0.24999999 0.25 0.25 0.75 0.75 0.75 0.75 0.74999994 0.75 0.75 0.75 0.75 0.75 0.74999994 0.75 0.5 0.5 0.5 0.5 0.5 0.5 + 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.16666667 0.16666669 0.16666667 0.16666669 0.16666667 0.16666667 0.49999997 0.5 0.5 0.50000006 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333333 0.8333334 0.8333334 0.16666667 0.16666669 0.16666667 0.16666669 0.16666667 0.16666667 0.49999997 0.5 0.5 0.50000006 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333333 0.8333334 0.8333334 0.16666667 0.16666669 0.16666667 0.16666669 0.16666667 0.16666667 0.49999997 0.5 0.5 0.50000006 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333333 0.8333334 0.8333334 0.25 0.25 0.25 0.25 0.25 0.25 0.75 0.75 0.75 0.75 0.75 0.75 0.25 0.25 0.25 0.25 0.25 0.25 0.75 0.75 0.75 0.75 0.75 0.75 0.5 0.5 0.5 0.5 0.5 0.5 + 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.65000004 0.45961943 0.91923887 0.37527767 1.1258893 0.7211102 0.65000004 0.45961943 0.91923887 0.37527767 1.1258893 0.7211102 0.65000004 0.45961943 0.91923887 0.37527767 1.1258893 0.7211102 0.6500001 0.4596194 0.9192388 0.37527764 1.1258893 0.7211102 0.6500001 0.4596194 0.9192388 0.37527764 1.1258893 0.7211102 0.6500001 0.4596194 0.9192388 0.37527764 1.1258893 0.7211102 0.6500001 0.45961946 0.9192388 0.3752777 1.1258893 0.72111017 0.6500001 0.45961946 0.9192388 0.3752777 1.1258893 0.72111017 0.6500001 0.45961946 0.9192388 0.3752777 1.1258893 0.72111017 0.8000001 0.5656855 1.131371 0.4618802 1.3857099 0.8717798 0.8000001 0.5656855 1.131371 0.4618802 1.3857099 0.8717798 0.80000013 0.5656855 1.131371 0.4618802 1.3857098 0.87177986 0.80000013 0.5656855 1.131371 0.4618802 1.3857098 0.87177986 0.95000005 0.6717515 1.343503 0.5484828 1.6455305 0.97467947 + 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.6499999 0.9192387 0.45961934 1.1258329 0.3752589 0.7211102 0.64999986 0.9192387 0.4596193 1.125833 0.37525892 0.7211102 0.64999986 0.91923875 0.45961928 1.1258328 0.37525892 0.72111017 0.6499999 0.9192387 0.45961934 1.1258329 0.3752589 0.7211102 0.64999986 0.9192387 0.4596193 1.125833 0.37525892 0.7211102 0.64999986 0.91923875 0.45961928 1.1258328 0.37525892 0.72111017 0.6499999 0.9192387 0.45961934 1.1258329 0.3752589 0.7211102 0.64999986 0.9192387 0.4596193 1.125833 0.37525892 0.7211102 0.64999986 0.91923875 0.45961928 1.1258328 0.37525892 0.72111017 0.79999995 1.1313708 0.5656854 1.3856406 0.46185714 0.8717798 0.79999995 1.1313708 0.56568533 1.3856406 0.46185708 0.87177986 0.79999995 1.1313708 0.5656854 1.3856406 0.46185714 0.8717798 0.79999995 1.1313708 0.56568533 1.3856406 0.46185708 0.87177986 0.9499999 1.3435028 0.6717514 1.6454482 0.54845536 0.97467947 \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/coco_labels_list.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/coco_labels_list.txt new file mode 100644 index 0000000..c634db4 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/coco_labels_list.txt @@ -0,0 +1,91 @@ +??? +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +??? +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +??? +backpack +umbrella +??? +??? +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +??? +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +??? +dining table +??? +??? +toilet +??? +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +??? +book +clock +vase +scissors +teddy bear +hair drier +toothbrush \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/road.bmp b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/road.bmp new file mode 100644 index 0000000..1254b96 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/road.bmp differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rk180x.rknn new file mode 100644 index 0000000..712b791 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rv1109_rv1126.rknn new file mode 100644 index 0000000..a65660e Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/main.cc new file mode 100644 index 0000000..b4e9559 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/main.cc @@ -0,0 +1,277 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include + +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb/stb_image_write.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#undef cimg_display +#define cimg_display 0 +#include "CImg/CImg.h" +#include "rknn_api.h" +#include "ssd.h" + +using namespace std; +using namespace cimg_library; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr* attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d " + "scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char* load_model(const char* filename, int* model_size) +{ + FILE* fp = fopen(filename, "rb"); + if (fp == nullptr) { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char* model = (unsigned char*)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) { + fclose(fp); + } + return model; +} +static unsigned char* load_image(const char* image_path, rknn_tensor_attr* input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char* image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) { + unsigned char* image_resized = (unsigned char*)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char** argv) +{ + const int img_width = 300; + const int img_height = 300; + const int img_channels = 3; + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char* model; + + const char* model_path = argv[1]; + const char* img_path = argv[2]; + + if (argc != 3) { + printf("Usage:%s model image\n", argv[0]); + return -1; + } + if (strstr(img_path, ".jpg") != NULL || strstr(img_path, ".png") != NULL) { + printf("Error: read %s failed! only support .bmp format image\n", img_path); + return -1; + } + + // Load RKNN Model + printf("Loading model ...\n"); + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image, only support .bmp format + CImg img(img_path); + if (img.height() != img_height || img.width() != img_width) { + img = img.resize(img_width, img_height); + } + unsigned char* input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) { + return -1; + } + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = input_data; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[2]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + outputs[1].want_float = 1; + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + if (ret < 0) { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + detect_result_group_t detect_result_group; + postProcessSSD((float*)(outputs[0].buf), (float*)(outputs[1].buf), img_height, img_width, &detect_result_group); + // Release rknn_outputs + rknn_outputs_release(ctx, 2, outputs); + + // Draw Objects + const unsigned char blue[] = {0, 0, 255}; + for (int i = 0; i < detect_result_group.count; i++) { + detect_result_t* det_result = &(detect_result_group.results[i]); + printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top, + det_result->box.right, det_result->box.bottom, det_result->prop); + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + // draw box + img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U); + img.draw_text(x1, y1 - 12, det_result->name, blue); + } + img.save("./out.bmp"); + + // Release + if (ctx >= 0) { + rknn_destroy(ctx); + } + if (model) { + free(model); + } + if (input_data) { + stbi_image_free(input_data); + } + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.cc new file mode 100644 index 0000000..2dccac7 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.cc @@ -0,0 +1,323 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "ssd.h" + +#define BOX_PRIORS_TXT_PATH "./model/box_priors.txt" +#define LABEL_NALE_TXT_PATH "./model/coco_labels_list.txt" + +float MIN_SCORE = 0.4f; +float NMS_THRESHOLD = 0.45f; + +static char* labels[NUM_CLASS]; +static float box_priors[4][NUM_RESULTS]; + +int64_t getCurrentTimeUs() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +char* readLine(FILE* fp, char* buffer, int* len) +{ + int ch; + int i = 0; + size_t buff_len = 0; + + buffer = (char*)malloc(buff_len + 1); + if (!buffer) + return NULL; // Out of memory + + while ((ch = fgetc(fp)) != '\n' && ch != EOF) { + buff_len++; + void* tmp = realloc(buffer, buff_len + 1); + if (tmp == NULL) { + free(buffer); + return NULL; // Out of memory + } + buffer = (char*)tmp; + + buffer[i] = (char)ch; + i++; + } + buffer[i] = '\0'; + + *len = buff_len; + + // Detect end + if (ch == EOF && (i == 0 || ferror(fp))) { + free(buffer); + return NULL; + } + return buffer; +} + +int readLines(const char* fileName, char* lines[], int max_line) +{ + FILE* file = fopen(fileName, "r"); + char* s; + int i = 0; + int n = 0; + while ((s = readLine(file, s, &n)) != NULL) { + lines[i++] = s; + if (i >= max_line) + break; + } + return i; +} + +int loadLabelName(const char* locationFilename, char* label[]) +{ + printf("ssd - loadLabelName %s\n", locationFilename); + readLines(locationFilename, label, NUM_CLASS); + return 0; +} + +int loadBoxPriors(const char* locationFilename, float (*boxPriors)[NUM_RESULTS]) +{ + const char* d = " "; + char* lines[4]; + int count = readLines(locationFilename, lines, 4); + // printf("line count %d\n", count); + // for (int i = 0; i < count; i++) { + // printf("%s\n", lines[i]); + // } + for (int i = 0; i < 4; i++) { + char* line_str = lines[i]; + char* p; + p = strtok(line_str, d); + int priorIndex = 0; + while (p) { + float number = (float)(atof(p)); + boxPriors[i][priorIndex++] = number; + p = strtok(NULL, d); + } + if (priorIndex != NUM_RESULTS) { + printf("error\n"); + return -1; + } + } + return 0; +} + +float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, + float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1)); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1)); + float i = w * h; + float u = (xmax0 - xmin0) * (ymax0 - ymin0) + (xmax1 - xmin1) * (ymax1 - ymin1) - i; + return u <= 0.f ? 0.f : (i / u); +} + +float unexpit(float y) { return -1.0 * logf((1.0 / y) - 1.0); } + +float expit(float x) { return (float)(1.0 / (1.0 + expf(-x))); } + +void decodeCenterSizeBoxes(float* predictions, float (*boxPriors)[NUM_RESULTS]) +{ + for (int i = 0; i < NUM_RESULTS; ++i) { + float ycenter = predictions[i * 4 + 0] / Y_SCALE * boxPriors[2][i] + boxPriors[0][i]; + float xcenter = predictions[i * 4 + 1] / X_SCALE * boxPriors[3][i] + boxPriors[1][i]; + float h = (float)expf(predictions[i * 4 + 2] / H_SCALE) * boxPriors[2][i]; + float w = (float)expf(predictions[i * 4 + 3] / W_SCALE) * boxPriors[3][i]; + + float ymin = ycenter - h / 2.0f; + float xmin = xcenter - w / 2.0f; + float ymax = ycenter + h / 2.0f; + float xmax = xcenter + w / 2.0f; + + predictions[i * 4 + 0] = ymin; + predictions[i * 4 + 1] = xmin; + predictions[i * 4 + 2] = ymax; + predictions[i * 4 + 3] = xmax; + } +} + +int filterValidResult(float* outputClasses, int (*output)[NUM_RESULTS], int numClasses, float* props) +{ + int validCount = 0; + float min_score = unexpit(MIN_SCORE); + + // Scale them back to the input size. + for (int i = 0; i < NUM_RESULTS; ++i) { + float topClassScore = (float)(-1000.0); + int topClassScoreIndex = -1; + + // Skip the first catch-all class. + for (int j = 1; j < numClasses; ++j) { + // x and expit(x) has same monotonicity + // so compare x and comare expit(x) is same + // float score = expit(outputClasses[i*numClasses+j]); + float score = outputClasses[i * numClasses + j]; + + if (score > topClassScore) { + topClassScoreIndex = j; + topClassScore = score; + } + } + + if (topClassScore >= min_score) { + output[0][validCount] = i; + output[1][validCount] = topClassScoreIndex; + props[validCount] = expit(outputClasses[i * numClasses + topClassScoreIndex]); + ++validCount; + } + } + + return validCount; +} + +int nms(int validCount, float* outputLocations, int (*output)[NUM_RESULTS]) +{ + for (int i = 0; i < validCount; ++i) { + if (output[0][i] == -1) { + continue; + } + int n = output[0][i]; + for (int j = i + 1; j < validCount; ++j) { + int m = output[0][j]; + if (m == -1) { + continue; + } + float xmin0 = outputLocations[n * 4 + 1]; + float ymin0 = outputLocations[n * 4 + 0]; + float xmax0 = outputLocations[n * 4 + 3]; + float ymax0 = outputLocations[n * 4 + 2]; + + float xmin1 = outputLocations[m * 4 + 1]; + float ymin1 = outputLocations[m * 4 + 0]; + float xmax1 = outputLocations[m * 4 + 3]; + float ymax1 = outputLocations[m * 4 + 2]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou >= NMS_THRESHOLD) { + output[0][j] = -1; + } + } + } + return 0; +} + +void sort(int output[][1917], float* props, int sz) +{ + int i = 0; + int j = 0; + + if (sz < 2) { + return; + } + + for (i = 0; i < sz - 1; i++) { + int top = i; + for (j = i + 1; j < sz; j++) { + if (props[top] < props[j]) { + top = j; + } + } + + if (i != top) { + int tmp1 = output[0][i]; + int tmp2 = output[1][i]; + float prop = props[i]; + output[0][i] = output[0][top]; + output[1][i] = output[1][top]; + props[i] = props[top]; + output[0][top] = tmp1; + output[1][top] = tmp2; + props[top] = prop; + } + } +} + +int postProcessSSD(float* predictions, float* output_classes, int width, int heigh, detect_result_group_t* group) +{ + static int init = -1; + if (init == -1) { + int ret = 0; + printf("loadLabelName\n"); + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) { + return -1; + } + + printf("loadBoxPriors\n"); + ret = loadBoxPriors(BOX_PRIORS_TXT_PATH, box_priors); + if (ret < 0) { + return -1; + } + + init = 0; + } + + int output[2][NUM_RESULTS]; + float props[NUM_RESULTS]; + memset(output, 0, 2 * NUM_RESULTS); + memset(props, 0, sizeof(float) * NUM_RESULTS); + + decodeCenterSizeBoxes(predictions, box_priors); + + int validCount = filterValidResult(output_classes, output, NUM_CLASS, props); + + if (validCount > OBJ_NUMB_MAX_SIZE) { + printf("validCount too much !!\n"); + return -1; + } + + sort(output, props, validCount); + + /* detect nest box */ + nms(validCount, predictions, output); + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) { + if (output[0][i] == -1) { + continue; + } + int n = output[0][i]; + int topClassScoreIndex = output[1][i]; + + int x1 = (int)(predictions[n * 4 + 1] * width); + int y1 = (int)(predictions[n * 4 + 0] * heigh); + int x2 = (int)(predictions[n * 4 + 3] * width); + int y2 = (int)(predictions[n * 4 + 2] * heigh); + // There are a bug show toothbrush always + if (x1 == 0 && x2 == 0 && y1 == 0 && y2 == 0) + continue; + char* label = labels[topClassScoreIndex]; + + group->results[last_count].box.left = x1; + group->results[last_count].box.top = y1; + group->results[last_count].box.right = x2; + group->results[last_count].box.bottom = y2; + group->results[last_count].prop = props[i]; + memcpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE); + + // printf("ssd result %2d: (%4d, %4d, %4d, %4d), %4.2f, %s\n", i, x1, y1, x2, y2, props[i], label); + last_count++; + } + + group->count = last_count; + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.h b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.h new file mode 100644 index 0000000..ff910a0 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.h @@ -0,0 +1,58 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSD_H_ +#define _SSD_H_ + +#define OBJ_NAME_MAX_SIZE 16 +#define OBJ_NUMB_MAX_SIZE 64 + +typedef struct _BOX_RECT { + int left; + int right; + int top; + int bottom; +} BOX_RECT; + +typedef struct __detect_result_t { + char name[OBJ_NAME_MAX_SIZE]; + BOX_RECT box; + float prop; +} detect_result_t; + +typedef struct _detect_result_group_t { + int id; + int count; + detect_result_t results[OBJ_NUMB_MAX_SIZE]; +} detect_result_group_t; + +#define IMG_CHANNEL 3 +#define MODEL_INPUT_SIZE 300 +#define NUM_RESULTS 1917 +#define NUM_CLASS 91 + +#define Y_SCALE 10.0f +#define X_SCALE 10.0f +#define H_SCALE 5.0f +#define W_SCALE 5.0f + +int loadLabelName(const char* locationFilename, char* labels[]); + +int loadBoxPriors(const char* locationFilename, float (*boxPriors)[NUM_RESULTS]); + +int postProcessSSD(float * predictions, float *output_classes, int width, int heigh, detect_result_group_t *group); + +int64_t getCurrentTimeUs(); + +#endif diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/CMakeLists.txt new file mode 100644 index 0000000..d7ea9d6 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_yolov5_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s -O3") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rga +set(RGA_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/rga) +include_directories(${RGA_DIR}/include) + +# drm +set(DRM_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/drm) +include_directories(${DRM_DIR}/include) +include_directories(${DRM_DIR}/include/libdrm) + +include_directories(${CMAKE_SOURCE_DIR}/include) + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_yolov5_demo + src/drm_func.c + src/rga_func.c + src/postprocess.cc + src/main.cc + ) + +target_link_libraries(rknn_yolov5_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_yolov5_demo) +install(TARGETS rknn_yolov5_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/README.md new file mode 100644 index 0000000..e8086cc --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/README.md @@ -0,0 +1,64 @@ +# Yolo-v5 demo + +## 导出rknn模型 + +请参考 https://github.com/airockchip/rknn_model_zoo/tree/main/models/vision/object_detection/yolov5-pytorch + + + +## 注意事项: + +1. 使用rknn-toolkit版本大于等于1.7.0。 +2. 本Demo只支持8比特非对称量化的rknn模型推理。 +3. 切换成自己训练的模型时,请注意对齐anchor等后处理参数,否则会导致后处理解析出错。 +4. 官网和rk预训练模型都是检测80类的目标,如果自己训练的模型,自行更改include/postprocess.h中的OBJ_CLASS_NUM以及NMS_THRESH,BOX_THRESH后处理参数后再编译。 +5. 由于硬件限制,该demo的模型默认把 yolov5 模型的后处理部分,移至cpu实现。本demo附带的模型均使用relu为激活函数,相比silu激活函数精度略微下降,推理速度更快。 +6. 关于加载时间:model目录下均是预编译rknn模型,加载速度比非预编译rknn模型快。convert_rknn_demo目录下的转换脚本生成非预编译rknn模型,如需重新生成预编译rknn模型,请参考rknn-toolkit的User Guide. + + +## Aarch64 Linux Demo + +### 编译 + +根据指定平台修改`build.sh`中的交叉编译器所在目录的路径`GCC_COMPILER`,例如修改成 + +```sh +GCC_COMPILER=~/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf +``` + +然后执行: + +```sh +./build.sh +``` + +### 推送执行文件到板子 + +将 install/rknn_yolov5_demo 拷贝到板子的/userdata/目录. + +- 如果使用rockchip的EVB板子,可以使用adb将文件推到板子上: + +```sh +adb push install/rknn_yolov5_demo /userdata/ +``` + +如果使用其他板子,可以使用scp等方式将install/rknn_yolov5_demo拷贝到板子的/userdata/目录 + +## 运行 + +```sh +adb shell +cd /userdata/rknn_yolov5_demo/ +``` + +- rk180x + +```sh +./run_rk180x.sh +``` + +- rv1109/rv1126 + +```sh +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/build.sh new file mode 100644 index 0000000..334bf73 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_yolov5_demo/ +cp run_rv1109_rv1126.sh install/rknn_yolov5_demo/ \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/bus.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/bus.jpg new file mode 100644 index 0000000..d8ef30b Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/bus.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/dataset.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/dataset.txt new file mode 100644 index 0000000..a81b1f8 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/dataset.txt @@ -0,0 +1 @@ +bus.jpg diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx2rknn.py b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx2rknn.py new file mode 100644 index 0000000..9f4ec6e --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx2rknn.py @@ -0,0 +1,40 @@ +from rknn.api import RKNN + +ONNX_MODEL = './onnx_models/yolov5s_rm_transpose.onnx' +# platform="rk1808" +platform = "rv1109" +RKNN_MODEL = 'yolov5s_relu_{}_out_opt.rknn'.format(platform) + + +if __name__ == '__main__': + + add_perm = False # 如果设置成True,则将模型输入layout修改成NHWC + # Create RKNN object + rknn = RKNN(verbose=True) + + # pre-process config + print('--> config model') + rknn.config(batch_size=1, mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], reorder_channel='0 1 2', target_platform=[platform], + force_builtin_perm=add_perm, output_optimize=1) + print('done') + + # Load tensorflow model + print('--> Loading model') + ret = rknn.load_onnx(model=ONNX_MODEL) + if ret != 0: + print('Load resnet50v2 failed!') + exit(ret) + print('done') + + # Build model + print('--> Building model') + ret = rknn.build(do_quantization=True, dataset='./dataset.txt') + if ret != 0: + print('Build resnet50 failed!') + exit(ret) + print('done') + + # rknn.export_rknn_precompile_model(RKNN_MODEL) + rknn.export_rknn(RKNN_MODEL) + + rknn.release() diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/bus.jpg b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/bus.jpg new file mode 100644 index 0000000..d8ef30b Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/bus.jpg differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/dataset.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/dataset.txt new file mode 100644 index 0000000..a81b1f8 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/dataset.txt @@ -0,0 +1 @@ +bus.jpg diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/yolov5s_rm_transpose.onnx b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/yolov5s_rm_transpose.onnx new file mode 100644 index 0000000..0a340ed Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/yolov5s_rm_transpose.onnx differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/drm_func.h b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/drm_func.h new file mode 100644 index 0000000..0a9e3b3 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/drm_func.h @@ -0,0 +1,53 @@ +#ifndef __DRM_FUNC_H__ +#define __DRM_FUNC_H__ +#include +#include +#include +#include +#include // open function +#include // close function +#include +#include + + +#include +#include "libdrm/drm_fourcc.h" +#include "xf86drm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (* FUNC_DRM_IOCTL)(int fd, unsigned long request, void *arg); + +typedef struct _drm_context{ + void *drm_handle; + FUNC_DRM_IOCTL io_func; +} drm_context; + +/* memory type definitions. */ +enum drm_rockchip_gem_mem_type +{ + /* Physically Continuous memory and used as default. */ + ROCKCHIP_BO_CONTIG = 1 << 0, + /* cachable mapping. */ + ROCKCHIP_BO_CACHABLE = 1 << 1, + /* write-combine mapping. */ + ROCKCHIP_BO_WC = 1 << 2, + ROCKCHIP_BO_SECURE = 1 << 3, + ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE | + ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE +}; + +int drm_init(drm_context *drm_ctx); + +void* drm_buf_alloc(drm_context *drm_ctx,int drm_fd, int TexWidth, int TexHeight,int bpp,int *fd,unsigned int *handle,size_t *actual_size); + +int drm_buf_destroy(drm_context *drm_ctx,int drm_fd,int buf_fd, int handle,void *drm_buf,size_t size); + +void drm_deinit(drm_context *drm_ctx, int drm_fd); + +#ifdef __cplusplus +} +#endif +#endif /*__DRM_FUNC_H__*/ \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/postprocess.h b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/postprocess.h new file mode 100644 index 0000000..e474505 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/postprocess.h @@ -0,0 +1,40 @@ +#ifndef _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ +#define _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ + +#include + +#define OBJ_NAME_MAX_SIZE 16 +#define OBJ_NUMB_MAX_SIZE 64 +#define OBJ_CLASS_NUM 80 +#define NMS_THRESH 0.6 +#define BOX_THRESH 0.5 +#define PROP_BOX_SIZE (5+OBJ_CLASS_NUM) + +typedef struct _BOX_RECT +{ + int left; + int right; + int top; + int bottom; +} BOX_RECT; + +typedef struct __detect_result_t +{ + char name[OBJ_NAME_MAX_SIZE]; + BOX_RECT box; + float prop; +} detect_result_t; + +typedef struct _detect_result_group_t +{ + int id; + int count; + detect_result_t results[OBJ_NUMB_MAX_SIZE]; +} detect_result_group_t; + +int post_process(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector &qnt_zps, std::vector &qnt_scales, + detect_result_group_t *group); + +#endif //_RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/rga_func.h b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/rga_func.h new file mode 100644 index 0000000..beeb441 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/include/rga_func.h @@ -0,0 +1,33 @@ +#ifndef __RGA_FUNC_H__ +#define __RGA_FUNC_H__ + +#include +#include "RgaApi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(* FUNC_RGA_INIT)(); +typedef void(* FUNC_RGA_DEINIT)(); +typedef int(* FUNC_RGA_BLIT)(rga_info_t *, rga_info_t *, rga_info_t *); + +typedef struct _rga_context{ + void *rga_handle; + FUNC_RGA_INIT init_func; + FUNC_RGA_DEINIT deinit_func; + FUNC_RGA_BLIT blit_func; +} rga_context; + +int RGA_init(rga_context* rga_ctx); + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h); + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h); + +int RGA_deinit(rga_context* rga_ctx); + +#ifdef __cplusplus +} +#endif +#endif/*__RGA_FUNC_H__*/ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/bus.bmp b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/bus.bmp new file mode 100644 index 0000000..82df079 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/bus.bmp differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/coco_80_labels_list.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/coco_80_labels_list.txt new file mode 100644 index 0000000..941cb4e --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/coco_80_labels_list.txt @@ -0,0 +1,80 @@ +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +dining table +toilet +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/rk180x/yolov5s_relu_rk180x_out_opt.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/rk180x/yolov5s_relu_rk180x_out_opt.rknn new file mode 100644 index 0000000..b240c62 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/rk180x/yolov5s_relu_rk180x_out_opt.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_out_opt.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_out_opt.rknn new file mode 100644 index 0000000..0692c1c Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_out_opt.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/run_rk180x.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/run_rk180x.sh new file mode 100644 index 0000000..f8492f8 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/run_rk180x.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +chip_dir="rk180x" +./rknn_yolov5_demo model/${chip_dir}/yolov5s_relu_rk180x_out_opt.rknn model/bus.bmp + + + diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..54d81fe --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/run_rv1109_rv1126.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +./rknn_yolov5_demo model/${chip_dir}/yolov5s_relu_rv1109_rv1126_out_opt.rknn model/bus.bmp + + diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/drm_func.c b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/drm_func.c new file mode 100644 index 0000000..2992326 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/drm_func.c @@ -0,0 +1,163 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "drm_func.h" + +#include + +int drm_init(drm_context* drm_ctx) +{ + static const char* card = "/dev/dri/card0"; + int flag = O_RDWR; + int drm_fd = -1; + + drm_fd = open(card, flag); + if (drm_fd < 0) { + printf("failed to open %s\n", card); + return -1; + } + + drm_ctx->drm_handle = dlopen("/usr/lib/libdrm.so", RTLD_LAZY); + if (!drm_ctx->drm_handle) { + printf("failed to dlopen /usr/lib/libdrm.so\n"); + drm_deinit(drm_ctx, drm_fd); + return -1; + } + + drm_ctx->io_func = (FUNC_DRM_IOCTL)dlsym(drm_ctx->drm_handle, "drmIoctl"); + if (drm_ctx->io_func == NULL) { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + drm_deinit(drm_ctx, drm_fd); + printf("failed to dlsym drmIoctl\n"); + return -1; + } + return drm_fd; +} + +void drm_deinit(drm_context* drm_ctx, int drm_fd) +{ + if (drm_ctx->drm_handle) { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + } + if (drm_fd > 0) { + close(drm_fd); + } +} + +void* drm_buf_alloc(drm_context* drm_ctx, int drm_fd, int TexWidth, int TexHeight, int bpp, int* fd, + unsigned int* handle, size_t* actual_size) +{ + int ret; + if (drm_ctx == NULL) { + printf("drm context is unvalid\n"); + return NULL; + } + char* map = NULL; + + void* vir_addr = NULL; + struct drm_prime_handle fd_args; + struct drm_mode_map_dumb mmap_arg; + struct drm_mode_destroy_dumb destory_arg; + + struct drm_mode_create_dumb alloc_arg; + + memset(&alloc_arg, 0, sizeof(alloc_arg)); + alloc_arg.bpp = bpp; + alloc_arg.width = TexWidth; + alloc_arg.height = TexHeight; + // alloc_arg.flags = ROCKCHIP_BO_CONTIG; + + //获取handle和size + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); + if (ret) { + printf("failed to create dumb buffer: %s\n", strerror(errno)); + return NULL; + } + if (handle != NULL) { + *handle = alloc_arg.handle; + } + if (actual_size != NULL) { + *actual_size = alloc_arg.size; + } + // printf("create width=%u, height=%u, bpp=%u, size=%lu dumb + // buffer\n",alloc_arg.width,alloc_arg.height,alloc_arg.bpp,alloc_arg.size); printf("out handle= + // %d\n",alloc_arg.handle); + + //获取fd + memset(&fd_args, 0, sizeof(fd_args)); + fd_args.fd = -1; + fd_args.handle = alloc_arg.handle; + ; + fd_args.flags = 0; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &fd_args); + if (ret) { + printf("rk-debug handle_to_fd failed ret=%d,err=%s, handle=%x \n", ret, strerror(errno), fd_args.handle); + return NULL; + } + // printf("out fd = %d, drm fd: %d\n",fd_args.fd,drm_fd); + if (fd != NULL) { + *fd = fd_args.fd; + } + + //获取虚拟地址 + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = alloc_arg.handle; + + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); + if (ret) { + printf("failed to create map dumb: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + vir_addr = map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, mmap_arg.offset); + if (map == MAP_FAILED) { + printf("failed to mmap buffer: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + // printf("alloc map=%x \n",map); + return vir_addr; +destory_dumb: + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = alloc_arg.handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d\n", ret); + return vir_addr; +} + +int drm_buf_destroy(drm_context* drm_ctx, int drm_fd, int buf_fd, int handle, void* drm_buf, size_t size) +{ + int ret = -1; + if (drm_buf == NULL) { + printf("drm buffer is NULL\n"); + return -1; + } + + munmap(drm_buf, size); + + struct drm_mode_destroy_dumb destory_arg; + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d, error=%s\n", ret, strerror(errno)); + if (buf_fd > 0) { + close(buf_fd); + } + + return ret; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/main.cc new file mode 100644 index 0000000..db229bc --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/main.cc @@ -0,0 +1,417 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include + +#include + +#define _BASETSD_H + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#undef cimg_display +#define cimg_display 0 +#include "CImg/CImg.h" +#include "drm_func.h" +#include "postprocess.h" +#include "rga_func.h" +#include "rknn_api.h" + +#define PERF_WITH_POST 1 + +using namespace cimg_library; +/*------------------------------------------- + Functions +-------------------------------------------*/ + +inline const char* get_type_string(rknn_tensor_type type) +{ + switch (type) { + case RKNN_TENSOR_FLOAT32: + return "FP32"; + case RKNN_TENSOR_FLOAT16: + return "FP16"; + case RKNN_TENSOR_INT8: + return "INT8"; + case RKNN_TENSOR_UINT8: + return "UINT8"; + case RKNN_TENSOR_INT16: + return "INT16"; + default: + return "UNKNOW"; + } +} + +inline const char* get_qnt_type_string(rknn_tensor_qnt_type type) +{ + switch (type) { + case RKNN_TENSOR_QNT_NONE: + return "NONE"; + case RKNN_TENSOR_QNT_DFP: + return "DFP"; + case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + return "AFFINE"; + default: + return "UNKNOW"; + } +} + +inline const char* get_format_string(rknn_tensor_format fmt) +{ + switch (fmt) { + case RKNN_TENSOR_NCHW: + return "NCHW"; + case RKNN_TENSOR_NHWC: + return "NHWC"; + default: + return "UNKNOW"; + } +} + +static void dump_tensor_attr(rknn_tensor_attr* attr) +{ + printf(" index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, " + "zp=%d, scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, get_format_string(attr->fmt), get_type_string(attr->type), + get_qnt_type_string(attr->qnt_type), attr->zp, attr->scale); +} + +double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); } + +static unsigned char* load_data(FILE* fp, size_t ofst, size_t sz) +{ + unsigned char* data; + int ret; + + data = NULL; + + if (NULL == fp) { + return NULL; + } + + ret = fseek(fp, ofst, SEEK_SET); + if (ret != 0) { + printf("blob seek failure.\n"); + return NULL; + } + + data = (unsigned char*)malloc(sz); + if (data == NULL) { + printf("buffer malloc failure.\n"); + return NULL; + } + ret = fread(data, 1, sz, fp); + return data; +} + +static unsigned char* load_model(const char* filename, int* model_size) +{ + FILE* fp; + unsigned char* data; + + fp = fopen(filename, "rb"); + if (NULL == fp) { + printf("Open file %s failed.\n", filename); + return NULL; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + + data = load_data(fp, 0, size); + + fclose(fp); + + *model_size = size; + return data; +} + +static int saveFloat(const char* file_name, float* output, int element_size) +{ + FILE* fp; + fp = fopen(file_name, "w"); + for (int i = 0; i < element_size; i++) { + fprintf(fp, "%.6f\n", output[i]); + } + fclose(fp); + return 0; +} + +static unsigned char* load_image(const char* image_path, int* org_height, int* org_width, int* org_ch, + rknn_tensor_attr* input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char* image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) { + printf("load image failed!\n"); + return NULL; + } + *org_width = width; + *org_height = height; + *org_ch = channel; + + return image_data; +} + +/*------------------------------------------- + Main Functions +-------------------------------------------*/ +int main(int argc, char** argv) +{ + int status = 0; + char* model_name = NULL; + rknn_context ctx; + void* drm_buf = NULL; + int drm_fd = -1; + int buf_fd = -1; // converted from buffer handle + unsigned int handle; + size_t actual_size = 0; + int img_width = 0; + int img_height = 0; + int img_channel = 0; + rga_context rga_ctx; + drm_context drm_ctx; + const float nms_threshold = NMS_THRESH; + const float box_conf_threshold = BOX_THRESH; + struct timeval start_time, stop_time; + int ret; + memset(&rga_ctx, 0, sizeof(rga_context)); + memset(&drm_ctx, 0, sizeof(drm_context)); + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + printf("post process config: box_conf_threshold = %.2f, nms_threshold = %.2f\n", box_conf_threshold, nms_threshold); + + model_name = (char*)argv[1]; + char* image_name = argv[2]; + + if (strstr(image_name, ".jpg") != NULL || strstr(image_name, ".png") != NULL) { + printf("Error: read %s failed! only support .bmp format image\n", image_name); + return -1; + } + + /* Create the neural network */ + printf("Loading mode...\n"); + int model_data_size = 0; + unsigned char* model_data = load_model(model_name, &model_data_size); + ret = rknn_init(&ctx, model_data, model_data_size, 0); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, sizeof(rknn_sdk_version)); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("sdk version: %s driver version: %s\n", version.api_version, version.drv_version); + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + dump_tensor_attr(&(input_attrs[i])); + } + + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + dump_tensor_attr(&(output_attrs[i])); + if (output_attrs[i].qnt_type != RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC || output_attrs[i].type != RKNN_TENSOR_UINT8) { + fprintf(stderr, + "The Demo required for a Affine asymmetric u8 quantized rknn model, but output quant type is %s, output " + "data type is %s\n", + get_qnt_type_string(output_attrs[i].qnt_type), get_type_string(output_attrs[i].type)); + return -1; + } + } + + int channel = 3; + int width = 0; + int height = 0; + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) { + printf("model is NCHW input fmt\n"); + width = input_attrs[0].dims[0]; + height = input_attrs[0].dims[1]; + } else { + printf("model is NHWC input fmt\n"); + width = input_attrs[0].dims[1]; + height = input_attrs[0].dims[2]; + } + + printf("model input height=%d, width=%d, channel=%d\n", height, width, channel); + + // Load image + CImg img(image_name); + unsigned char* input_data = NULL; + input_data = load_image(image_name, &img_height, &img_width, &img_channel, &input_attrs[0]); + if (!input_data) { + return -1; + } + + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = width * height * channel; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].pass_through = 0; + + // DRM alloc buffer + drm_fd = drm_init(&drm_ctx); + drm_buf = drm_buf_alloc(&drm_ctx, drm_fd, img_width, img_height, channel * 8, &buf_fd, &handle, &actual_size); + memcpy(drm_buf, input_data, img_width * img_height * channel); + void* resize_buf = malloc(height * width * channel); + + // init rga context + RGA_init(&rga_ctx); + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + inputs[0].buf = resize_buf; + gettimeofday(&start_time, NULL); + rknn_inputs_set(ctx, io_num.n_input, inputs); + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) { + outputs[i].want_float = 0; + } + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + gettimeofday(&stop_time, NULL); + printf("once run use %f ms\n", (__get_us(stop_time) - __get_us(start_time)) / 1000); + + // post process + float scale_w = (float)width / img_width; + float scale_h = (float)height / img_height; + + detect_result_group_t detect_result_group; + std::vector out_scales; + std::vector out_zps; + for (int i = 0; i < io_num.n_output; ++i) { + out_scales.push_back(output_attrs[i].scale); + out_zps.push_back(output_attrs[i].zp); + } + post_process((uint8_t*)outputs[0].buf, (uint8_t*)outputs[1].buf, (uint8_t*)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); + + // Draw Objects + char text[256]; + const unsigned char blue[] = {0, 0, 255}; + const unsigned char white[] = {255, 255, 255}; + for (int i = 0; i < detect_result_group.count; i++) { + detect_result_t* det_result = &(detect_result_group.results[i]); + sprintf(text, "%s %.2f", det_result->name, det_result->prop); + printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top, + det_result->box.right, det_result->box.bottom, det_result->prop); + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + // draw box + img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U); + img.draw_text(x1, y1 - 12, text, white); + } + img.save("./out.bmp"); + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + + // loop test + int test_count = 10; + gettimeofday(&start_time, NULL); + for (int i = 0; i < test_count; ++i) { + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + rknn_inputs_set(ctx, io_num.n_input, inputs); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); +#if PERF_WITH_POST + post_process((uint8_t*)outputs[0].buf, (uint8_t*)outputs[1].buf, (uint8_t*)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); +#endif + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + } + gettimeofday(&stop_time, NULL); + printf("loop count = %d , average run %f ms\n", test_count, + (__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count); + + // release + ret = rknn_destroy(ctx); + drm_buf_destroy(&drm_ctx, drm_fd, buf_fd, handle, drm_buf, actual_size); + + drm_deinit(&drm_ctx, drm_fd); + RGA_deinit(&rga_ctx); + if (model_data) { + free(model_data); + } + + if (resize_buf) { + free(resize_buf); + } + stbi_image_free(input_data); + + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/postprocess.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/postprocess.cc new file mode 100644 index 0000000..793efd4 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/postprocess.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "postprocess.h" + + +#define LABEL_NALE_TXT_PATH "./model/coco_80_labels_list.txt" + +static char* labels[OBJ_CLASS_NUM]; + +const int anchor0[6] = {10, 13, 16, 30, 33, 23}; +const int anchor1[6] = {30, 61, 62, 45, 59, 119}; +const int anchor2[6] = {116, 90, 156, 198, 373, 326}; + +inline static int clamp(float val, int min, int max) { return val > min ? (val < max ? val : max) : min; } + +char* readLine(FILE* fp, char* buffer, int* len) +{ + int ch; + int i = 0; + size_t buff_len = 0; + + buffer = (char*)malloc(buff_len + 1); + if (!buffer) + return NULL; // Out of memory + + while ((ch = fgetc(fp)) != '\n' && ch != EOF) { + buff_len++; + void* tmp = realloc(buffer, buff_len + 1); + if (tmp == NULL) { + free(buffer); + return NULL; // Out of memory + } + buffer = (char*)tmp; + + buffer[i] = (char)ch; + i++; + } + buffer[i] = '\0'; + + *len = buff_len; + + // Detect end + if (ch == EOF && (i == 0 || ferror(fp))) { + free(buffer); + return NULL; + } + return buffer; +} + +int readLines(const char* fileName, char* lines[], int max_line) +{ + FILE* file = fopen(fileName, "r"); + char* s; + int i = 0; + int n = 0; + while ((s = readLine(file, s, &n)) != NULL) { + lines[i++] = s; + if (i >= max_line) + break; + } + return i; +} + +int loadLabelName(const char* locationFilename, char* label[]) +{ + printf("loadLabelName %s\n", locationFilename); + readLines(locationFilename, label, OBJ_CLASS_NUM); + return 0; +} + +static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, + float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1) + 1.0); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1) + 1.0); + float i = w * h; + float u = (xmax0 - xmin0 + 1.0) * (ymax0 - ymin0 + 1.0) + (xmax1 - xmin1 + 1.0) * (ymax1 - ymin1 + 1.0) - i; + return u <= 0.f ? 0.f : (i / u); +} + +static int nms(int validCount, std::vector& outputLocations, std::vector classIds, std::vector& order, + int filterId, float threshold) +{ + for (int i = 0; i < validCount; ++i) { + if (order[i] == -1 || classIds[i] != filterId) { + continue; + } + int n = order[i]; + for (int j = i + 1; j < validCount; ++j) { + int m = order[j]; + if (m == -1 || classIds[i] != filterId) { + continue; + } + float xmin0 = outputLocations[n * 4 + 0]; + float ymin0 = outputLocations[n * 4 + 1]; + float xmax0 = outputLocations[n * 4 + 0] + outputLocations[n * 4 + 2]; + float ymax0 = outputLocations[n * 4 + 1] + outputLocations[n * 4 + 3]; + + float xmin1 = outputLocations[m * 4 + 0]; + float ymin1 = outputLocations[m * 4 + 1]; + float xmax1 = outputLocations[m * 4 + 0] + outputLocations[m * 4 + 2]; + float ymax1 = outputLocations[m * 4 + 1] + outputLocations[m * 4 + 3]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou > threshold) { + order[j] = -1; + } + } + } + return 0; +} + +static int quick_sort_indice_inverse(std::vector& input, int left, int right, std::vector& indices) +{ + float key; + int key_index; + int low = left; + int high = right; + if (left < right) { + key_index = indices[left]; + key = input[left]; + while (low < high) { + while (low < high && input[high] <= key) { + high--; + } + input[low] = input[high]; + indices[low] = indices[high]; + while (low < high && input[low] >= key) { + low++; + } + input[high] = input[low]; + indices[high] = indices[low]; + } + input[low] = key; + indices[low] = key_index; + quick_sort_indice_inverse(input, left, low - 1, indices); + quick_sort_indice_inverse(input, low + 1, right, indices); + } + return low; +} + +static float sigmoid(float x) { return 1.0 / (1.0 + expf(-x)); } + +static float unsigmoid(float y) { return -1.0 * logf((1.0 / y) - 1.0); } + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + return f; +} + +static uint8_t qnt_f32_to_affine(float f32, uint32_t zp, float scale) +{ + float dst_val = (f32 / scale) + zp; + uint8_t res = (uint8_t)__clip(dst_val, 0, 255); + return res; +} + +static float deqnt_affine_to_f32(uint8_t qnt, uint32_t zp, float scale) { return ((float)qnt - (float)zp) * scale; } + +static int process(uint8_t* input, int* anchor, int grid_h, int grid_w, int height, int width, int stride, + std::vector& boxes, std::vector& objProbs, std::vector& classId, float threshold, + uint32_t zp, float scale) +{ + int validCount = 0; + int grid_len = grid_h * grid_w; + float thres = unsigmoid(threshold); + uint8_t thres_u8 = qnt_f32_to_affine(thres, zp, scale); + for (int a = 0; a < 3; a++) { + for (int i = 0; i < grid_h; i++) { + for (int j = 0; j < grid_w; j++) { + uint8_t box_confidence = input[(PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j]; + if (box_confidence >= thres_u8) { + int offset = (PROP_BOX_SIZE * a) * grid_len + i * grid_w + j; + uint8_t* in_ptr = input + offset; + float box_x = sigmoid(deqnt_affine_to_f32(*in_ptr, zp, scale)) * 2.0 - 0.5; + float box_y = sigmoid(deqnt_affine_to_f32(in_ptr[grid_len], zp, scale)) * 2.0 - 0.5; + float box_w = sigmoid(deqnt_affine_to_f32(in_ptr[2 * grid_len], zp, scale)) * 2.0; + float box_h = sigmoid(deqnt_affine_to_f32(in_ptr[3 * grid_len], zp, scale)) * 2.0; + box_x = (box_x + j) * (float)stride; + box_y = (box_y + i) * (float)stride; + box_w = box_w * box_w * (float)anchor[a * 2]; + box_h = box_h * box_h * (float)anchor[a * 2 + 1]; + box_x -= (box_w / 2.0); + box_y -= (box_h / 2.0); + boxes.push_back(box_x); + boxes.push_back(box_y); + boxes.push_back(box_w); + boxes.push_back(box_h); + + uint8_t maxClassProbs = in_ptr[5 * grid_len]; + int maxClassId = 0; + for (int k = 1; k < OBJ_CLASS_NUM; ++k) { + uint8_t prob = in_ptr[(5 + k) * grid_len]; + if (prob > maxClassProbs) { + maxClassId = k; + maxClassProbs = prob; + } + } + objProbs.push_back(sigmoid(deqnt_affine_to_f32(maxClassProbs, zp, scale))); + classId.push_back(maxClassId); + validCount++; + } + } + } + } + return validCount; +} + +int post_process(uint8_t* input0, uint8_t* input1, uint8_t* input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector& qnt_zps, std::vector& qnt_scales, detect_result_group_t* group) +{ + static int init = -1; + if (init == -1) { + int ret = 0; + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) { + return -1; + } + + init = 0; + } + memset(group, 0, sizeof(detect_result_group_t)); + + std::vector filterBoxes; + std::vector objProbs; + std::vector classId; + + // stride 8 + int stride0 = 8; + int grid_h0 = model_in_h / stride0; + int grid_w0 = model_in_w / stride0; + int validCount0 = 0; + validCount0 = process(input0, (int*)anchor0, grid_h0, grid_w0, model_in_h, model_in_w, stride0, filterBoxes, objProbs, + classId, conf_threshold, qnt_zps[0], qnt_scales[0]); + + // stride 16 + int stride1 = 16; + int grid_h1 = model_in_h / stride1; + int grid_w1 = model_in_w / stride1; + int validCount1 = 0; + validCount1 = process(input1, (int*)anchor1, grid_h1, grid_w1, model_in_h, model_in_w, stride1, filterBoxes, objProbs, + classId, conf_threshold, qnt_zps[1], qnt_scales[1]); + + // stride 32 + int stride2 = 32; + int grid_h2 = model_in_h / stride2; + int grid_w2 = model_in_w / stride2; + int validCount2 = 0; + validCount2 = process(input2, (int*)anchor2, grid_h2, grid_w2, model_in_h, model_in_w, stride2, filterBoxes, objProbs, + classId, conf_threshold, qnt_zps[2], qnt_scales[2]); + + int validCount = validCount0 + validCount1 + validCount2; + // no object detect + if (validCount <= 0) { + return 0; + } + + std::vector indexArray; + for (int i = 0; i < validCount; ++i) { + indexArray.push_back(i); + } + + quick_sort_indice_inverse(objProbs, 0, validCount - 1, indexArray); + + std::set class_set(std::begin(classId), std::end(classId)); + + for (auto c : class_set) { + nms(validCount, filterBoxes, classId, indexArray, c, nms_threshold); + } + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) { + if (indexArray[i] == -1 || last_count >= OBJ_NUMB_MAX_SIZE) { + continue; + } + int n = indexArray[i]; + + float x1 = filterBoxes[n * 4 + 0]; + float y1 = filterBoxes[n * 4 + 1]; + float x2 = x1 + filterBoxes[n * 4 + 2]; + float y2 = y1 + filterBoxes[n * 4 + 3]; + int id = classId[n]; + float obj_conf = objProbs[i]; + + group->results[last_count].box.left = (int)(clamp(x1, 0, model_in_w) / scale_w); + group->results[last_count].box.top = (int)(clamp(y1, 0, model_in_h) / scale_h); + group->results[last_count].box.right = (int)(clamp(x2, 0, model_in_w) / scale_w); + group->results[last_count].box.bottom = (int)(clamp(y2, 0, model_in_h) / scale_h); + group->results[last_count].prop = obj_conf; + char* label = labels[id]; + strncpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE); + + // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, + // group->results[last_count].box.top, + // group->results[last_count].box.right, group->results[last_count].box.bottom, label); + last_count++; + } + group->count = last_count; + + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/rga_func.c b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/rga_func.c new file mode 100644 index 0000000..c9e8a90 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_yolov5_demo/src/rga_func.c @@ -0,0 +1,108 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rga_func.h" + +int RGA_init(rga_context* rga_ctx) +{ + rga_ctx->rga_handle = dlopen("/usr/lib/librga.so", RTLD_LAZY); + if (!rga_ctx->rga_handle) { + printf("dlopen /usr/lib/librga.so failed\n"); + return -1; + } + rga_ctx->init_func = (FUNC_RGA_INIT)dlsym(rga_ctx->rga_handle, "c_RkRgaInit"); + rga_ctx->deinit_func = (FUNC_RGA_DEINIT)dlsym(rga_ctx->rga_handle, "c_RkRgaDeInit"); + rga_ctx->blit_func = (FUNC_RGA_BLIT)dlsym(rga_ctx->rga_handle, "c_RkRgaBlit"); + rga_ctx->init_func(); + return 0; +} + +void img_resize_fast(rga_context* rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h) +{ + // printf("rga use fd, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = src_fd; + src.mmuFlag = 1; + // src.virAddr = (void *)psrc; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 0; + +#if defined(__arm__) + dst.phyAddr = (void*)((uint32_t)dst_phys); +#else + dst.phyAddr = (void*)dst_phys; +#endif + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +void img_resize_slow(rga_context* rga_ctx, void* src_virt, int src_w, int src_h, void* dst_virt, int dst_w, int dst_h) +{ + // printf("rga use virtual, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = -1; + src.mmuFlag = 1; + src.virAddr = (void*)src_virt; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 1; + dst.virAddr = dst_virt; + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +int RGA_deinit(rga_context* rga_ctx) +{ + if (rga_ctx->rga_handle) { + dlclose(rga_ctx->rga_handle); + rga_ctx->rga_handle = NULL; + } +} \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/CMakeLists.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/CMakeLists.txt new file mode 100644 index 0000000..cdcfc38 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_zero_copy_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s -O3") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rga +set(RGA_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/rga) +include_directories(${RGA_DIR}/include) + +# drm +set(DRM_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/drm) +include_directories(${DRM_DIR}/include) +include_directories(${DRM_DIR}/include/libdrm) + +include_directories(${CMAKE_SOURCE_DIR}/include) + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_zero_copy_demo + src/drm_func.c + src/rga_func.c + src/postprocess.cc + src/main.cc + ) + +target_link_libraries(rknn_zero_copy_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_zero_copy_demo) +install(TARGETS rknn_zero_copy_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/README.md b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/README.md new file mode 100644 index 0000000..500cb9b --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/README.md @@ -0,0 +1,88 @@ +# Zero copy demo + +In specific case, the number of input data copies can be reduced to 0, that is, zero copy. For example, when the RKNN model is asymmetric quantization, the quantization data type is uint8, the mean value of the 3 channels is the same integer and the scaling factor is the same, the normalization and quantization can be omitted. + +This demo compares the performance difference between zero-copy and normal process inference. + +```txt +a. zero-copy inference uses the "rknn_inputs_map" interface + +b. normal inference uses the "rknn_inputs_set" interface. +``` + +In the "model" directory, models without NHWC suffix have input in NCHW format, and models with NHWC suffix have input in NHWC format. The model with NHWC suffix is ​​exported using rknn-toolkit by adding the following parameters + +```python +rknn.config(force_builtin_perm=True) +``` + +Note: if it shows "**get unvalid input physical address, please extend in/out memory space**", you may need to modify the size of **CMA** in the **kernel's dts**, such as: + +``` +diff --git a/arch/arm/boot/dts/rv1126.dtsi b/arch/arm/boot/dts/rv1126.dtsi +index f5f0a07..1d0487c 100644 +--- a/arch/arm/boot/dts/rv1126.dtsi ++++ b/arch/arm/boot/dts/rv1126.dtsi +@@ -362,11 +362,17 @@ + #size-cells = <1>; + ranges; + ++ nouse@0 { ++ reg = <0x00000000 0x4000>; ++ no-map; ++ }; ++ ++ + linux,cma { + compatible = "shared-dma-pool"; + inactive; + reusable; +- size = <0x800000>; ++ size = <0x8000000>; + linux,cma-default; + }; + +``` + +This patch is only for RV1109/RV1126,if you use RK1808,you need to select the appropriate dts file to modify. + +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +```sh +./build.sh +``` + +## Install + +Copy install/rknn_zero_copy_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +```sh +adb push install/rknn_zero_copy_demo /userdata/ +``` + +- If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +```sh +adb shell +cd /userdata/rknn_zero_copy_demo/ +``` + +- RK1808/RK1806 + +```sh +./run_rk180x.sh +``` + +- RV1109/RV1126 + +```sh +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/build.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/build.sh new file mode 100644 index 0000000..01da901 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_zero_copy_demo/ +cp run_rv1109_rv1126.sh install/rknn_zero_copy_demo/ \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/drm_func.h b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/drm_func.h new file mode 100644 index 0000000..0a9e3b3 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/drm_func.h @@ -0,0 +1,53 @@ +#ifndef __DRM_FUNC_H__ +#define __DRM_FUNC_H__ +#include +#include +#include +#include +#include // open function +#include // close function +#include +#include + + +#include +#include "libdrm/drm_fourcc.h" +#include "xf86drm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (* FUNC_DRM_IOCTL)(int fd, unsigned long request, void *arg); + +typedef struct _drm_context{ + void *drm_handle; + FUNC_DRM_IOCTL io_func; +} drm_context; + +/* memory type definitions. */ +enum drm_rockchip_gem_mem_type +{ + /* Physically Continuous memory and used as default. */ + ROCKCHIP_BO_CONTIG = 1 << 0, + /* cachable mapping. */ + ROCKCHIP_BO_CACHABLE = 1 << 1, + /* write-combine mapping. */ + ROCKCHIP_BO_WC = 1 << 2, + ROCKCHIP_BO_SECURE = 1 << 3, + ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE | + ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE +}; + +int drm_init(drm_context *drm_ctx); + +void* drm_buf_alloc(drm_context *drm_ctx,int drm_fd, int TexWidth, int TexHeight,int bpp,int *fd,unsigned int *handle,size_t *actual_size); + +int drm_buf_destroy(drm_context *drm_ctx,int drm_fd,int buf_fd, int handle,void *drm_buf,size_t size); + +void drm_deinit(drm_context *drm_ctx, int drm_fd); + +#ifdef __cplusplus +} +#endif +#endif /*__DRM_FUNC_H__*/ \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/postprocess.h b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/postprocess.h new file mode 100644 index 0000000..e474505 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/postprocess.h @@ -0,0 +1,40 @@ +#ifndef _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ +#define _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ + +#include + +#define OBJ_NAME_MAX_SIZE 16 +#define OBJ_NUMB_MAX_SIZE 64 +#define OBJ_CLASS_NUM 80 +#define NMS_THRESH 0.6 +#define BOX_THRESH 0.5 +#define PROP_BOX_SIZE (5+OBJ_CLASS_NUM) + +typedef struct _BOX_RECT +{ + int left; + int right; + int top; + int bottom; +} BOX_RECT; + +typedef struct __detect_result_t +{ + char name[OBJ_NAME_MAX_SIZE]; + BOX_RECT box; + float prop; +} detect_result_t; + +typedef struct _detect_result_group_t +{ + int id; + int count; + detect_result_t results[OBJ_NUMB_MAX_SIZE]; +} detect_result_group_t; + +int post_process(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector &qnt_zps, std::vector &qnt_scales, + detect_result_group_t *group); + +#endif //_RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/rga_func.h b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/rga_func.h new file mode 100644 index 0000000..beeb441 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/include/rga_func.h @@ -0,0 +1,33 @@ +#ifndef __RGA_FUNC_H__ +#define __RGA_FUNC_H__ + +#include +#include "RgaApi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(* FUNC_RGA_INIT)(); +typedef void(* FUNC_RGA_DEINIT)(); +typedef int(* FUNC_RGA_BLIT)(rga_info_t *, rga_info_t *, rga_info_t *); + +typedef struct _rga_context{ + void *rga_handle; + FUNC_RGA_INIT init_func; + FUNC_RGA_DEINIT deinit_func; + FUNC_RGA_BLIT blit_func; +} rga_context; + +int RGA_init(rga_context* rga_ctx); + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h); + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h); + +int RGA_deinit(rga_context* rga_ctx); + +#ifdef __cplusplus +} +#endif +#endif/*__RGA_FUNC_H__*/ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/bus.bmp b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/bus.bmp new file mode 100644 index 0000000..82df079 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/bus.bmp differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/coco_80_labels_list.txt b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/coco_80_labels_list.txt new file mode 100644 index 0000000..941cb4e --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/coco_80_labels_list.txt @@ -0,0 +1,80 @@ +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +dining table +toilet +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x.rknn new file mode 100644 index 0000000..f8170d3 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x_NHWC.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x_NHWC.rknn new file mode 100644 index 0000000..0b8c68c Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x_NHWC.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126.rknn new file mode 100644 index 0000000..eba83c6 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_NHWC.rknn b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_NHWC.rknn new file mode 100644 index 0000000..80f2a2e Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_NHWC.rknn differ diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rk180x.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rk180x.sh new file mode 100644 index 0000000..d74398a --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rk180x.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +chip_dir="rk180x" +echo '********************* run zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rk180x_NHWC.rknn model/bus.bmp 1 +echo '********************* run non-zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rk180x.rknn model/bus.bmp 0 + + + + diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..0701370 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rv1109_rv1126.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +echo '********************* run zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rv1109_rv1126_NHWC.rknn model/bus.bmp 1 + +echo '********************* run non-zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rv1109_rv1126.rknn model/bus.bmp 0 + + + diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/drm_func.c b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/drm_func.c new file mode 100644 index 0000000..5811922 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/drm_func.c @@ -0,0 +1,175 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "drm_func.h" +#include + +int drm_init(drm_context *drm_ctx) +{ + static const char *card = "/dev/dri/card0"; + int flag = O_RDWR; + int drm_fd = -1; + + drm_fd = open(card, flag); + if (drm_fd < 0) + { + printf("failed to open %s\n", card); + return -1; + } + + drm_ctx->drm_handle = dlopen("/usr/lib/libdrm.so", RTLD_LAZY); + if (!drm_ctx->drm_handle) + { + printf("failed to dlopen /usr/lib/libdrm.so\n"); + drm_deinit(drm_ctx, drm_fd); + return -1; + } + + drm_ctx->io_func = (FUNC_DRM_IOCTL)dlsym(drm_ctx->drm_handle, "drmIoctl"); + if (drm_ctx->io_func == NULL) + { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + drm_deinit(drm_ctx, drm_fd); + printf("failed to dlsym drmIoctl\n"); + return -1; + } + return drm_fd; +} + +void drm_deinit(drm_context *drm_ctx, int drm_fd) +{ + if (drm_ctx->drm_handle) + { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + } + if (drm_fd > 0) + { + close(drm_fd); + } +} + +void *drm_buf_alloc(drm_context *drm_ctx, int drm_fd, int TexWidth, int TexHeight, int bpp, int *fd, unsigned int *handle, size_t *actual_size) +{ + int ret; + if (drm_ctx == NULL) + { + printf("drm context is unvalid\n"); + return NULL; + } + char *map = NULL; + + void *vir_addr = NULL; + struct drm_prime_handle fd_args; + struct drm_mode_map_dumb mmap_arg; + struct drm_mode_destroy_dumb destory_arg; + + struct drm_mode_create_dumb alloc_arg; + + memset(&alloc_arg, 0, sizeof(alloc_arg)); + alloc_arg.bpp = bpp; + alloc_arg.width = TexWidth; + alloc_arg.height = TexHeight; + // alloc_arg.flags = ROCKCHIP_BO_CONTIG; + + //获取handle和size + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); + if (ret) + { + printf("failed to create dumb buffer: %s\n", strerror(errno)); + return NULL; + } + if (handle != NULL) + { + *handle = alloc_arg.handle; + } + if (actual_size != NULL) + { + *actual_size = alloc_arg.size; + } + // printf("create width=%u, height=%u, bpp=%u, size=%lu dumb buffer\n",alloc_arg.width,alloc_arg.height,alloc_arg.bpp,alloc_arg.size); + // printf("out handle= %d\n",alloc_arg.handle); + + //获取fd + memset(&fd_args, 0, sizeof(fd_args)); + fd_args.fd = -1; + fd_args.handle = alloc_arg.handle; + ; + fd_args.flags = 0; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &fd_args); + if (ret) + { + printf("rk-debug handle_to_fd failed ret=%d,err=%s, handle=%x \n", ret, strerror(errno), fd_args.handle); + return NULL; + } + // printf("out fd = %d, drm fd: %d\n",fd_args.fd,drm_fd); + if (fd != NULL) + { + *fd = fd_args.fd; + } + + //获取虚拟地址 + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = alloc_arg.handle; + + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); + if (ret) + { + printf("failed to create map dumb: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + vir_addr = map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, mmap_arg.offset); + if (map == MAP_FAILED) + { + printf("failed to mmap buffer: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + // printf("alloc map=%x \n",map); + return vir_addr; +destory_dumb: + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = alloc_arg.handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d\n", ret); + return vir_addr; +} + +int drm_buf_destroy(drm_context *drm_ctx, int drm_fd, int buf_fd, int handle, void *drm_buf, size_t size) +{ + int ret = -1; + if (drm_buf == NULL) + { + printf("drm buffer is NULL\n"); + return -1; + } + + munmap(drm_buf, size); + + struct drm_mode_destroy_dumb destory_arg; + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d, error=%s\n", ret, strerror(errno)); + if (buf_fd > 0) + { + close(buf_fd); + } + + return ret; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/main.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/main.cc new file mode 100644 index 0000000..60ce6c8 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/main.cc @@ -0,0 +1,500 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#define _BASETSD_H + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#undef cimg_display +#define cimg_display 0 +#include "CImg/CImg.h" + +#include "drm_func.h" +#include "rga_func.h" +#include "rknn_api.h" +#include "postprocess.h" + +#define PERF_WITH_POST 1 + +using namespace cimg_library; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +inline const char *get_type_string(rknn_tensor_type type) +{ + switch (type) + { + case RKNN_TENSOR_FLOAT32: + return "FP32"; + case RKNN_TENSOR_FLOAT16: + return "FP16"; + case RKNN_TENSOR_INT8: + return "INT8"; + case RKNN_TENSOR_UINT8: + return "UINT8"; + case RKNN_TENSOR_INT16: + return "INT16"; + default: + return "UNKNOW"; + } +} + +inline const char *get_qnt_type_string(rknn_tensor_qnt_type type) +{ + switch (type) + { + case RKNN_TENSOR_QNT_NONE: + return "NONE"; + case RKNN_TENSOR_QNT_DFP: + return "DFP"; + case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + return "AFFINE"; + default: + return "UNKNOW"; + } +} + +inline const char *get_format_string(rknn_tensor_format fmt) +{ + switch (fmt) + { + case RKNN_TENSOR_NCHW: + return "NCHW"; + case RKNN_TENSOR_NHWC: + return "NHWC"; + default: + return "UNKNOW"; + } +} + +static void dump_tensor_attr(rknn_tensor_attr *attr) +{ + printf(" index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, " + "zp=%d, scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, get_format_string(attr->fmt), get_type_string(attr->type), + get_qnt_type_string(attr->qnt_type), attr->zp, attr->scale); +} + +double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); } + +static unsigned char *load_data(FILE *fp, size_t ofst, size_t sz) +{ + unsigned char *data; + int ret; + + data = NULL; + + if (NULL == fp) + { + return NULL; + } + + ret = fseek(fp, ofst, SEEK_SET); + if (ret != 0) + { + printf("blob seek failure.\n"); + return NULL; + } + + data = (unsigned char *)malloc(sz); + if (data == NULL) + { + printf("buffer malloc failure.\n"); + return NULL; + } + ret = fread(data, 1, sz, fp); + return data; +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + + FILE *fp; + unsigned char *data; + + fp = fopen(filename, "rb"); + if (NULL == fp) + { + printf("Open file %s failed.\n", filename); + return NULL; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + + data = load_data(fp, 0, size); + + fclose(fp); + + *model_size = size; + return data; +} + +static int saveFloat(const char *file_name, float *output, int element_size) +{ + FILE *fp; + fp = fopen(file_name, "w"); + for (int i = 0; i < element_size; i++) + { + fprintf(fp, "%.6f\n", output[i]); + } + fclose(fp); + return 0; +} + +static unsigned char *load_image(const char *image_path, int *org_height, int *org_width, int *org_ch, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + *org_width = width; + *org_height = height; + *org_ch = channel; + + return image_data; +} + +/*------------------------------------------- + Main Functions +-------------------------------------------*/ +int main(int argc, char **argv) +{ + int status = 0; + char *model_name = NULL; + rknn_context ctx; + int is_map = 0; + rknn_tensor_mem in_mem[1]; + void *drm_buf = NULL; + int drm_fd = -1; + int buf_fd = -1; // converted from buffer handle + unsigned int handle; + size_t actual_size = 0; + int img_width = 0; + int img_height = 0; + int img_channel = 0; + rga_context rga_ctx; + drm_context drm_ctx; + const float nms_threshold = NMS_THRESH; + const float box_conf_threshold = BOX_THRESH; + struct timeval start_time, stop_time; + int ret; + memset(&rga_ctx, 0, sizeof(rga_context)); + memset(&drm_ctx, 0, sizeof(drm_context)); + + if (argc != 4) + { + printf("Note: rknn model need meet zero-copy condition: 3-channel with same integer means and same scales\n"); + printf("Usage: %s \n", argv[0]); + printf("flag:\n"); + printf("\t 0: run builtin_permute=False rknn model, perform rknn_inputs_set\n"); + printf("\t 1: run builtin_permute=True rknn model, perform rknn_inputs_map\n"); + return -1; + } + + printf("post process config: box_conf_threshold = %.2f, nms_threshold = %.2f\n", + box_conf_threshold, nms_threshold); + model_name = (char *)argv[1]; + char *image_name = argv[2]; + is_map = atoi(argv[3]); + + if (strstr(image_name, ".jpg") != NULL || strstr(image_name, ".png") != NULL) + { + printf("Error: read %s failed! only support .bmp format image\n", image_name); + return -1; + } + + /* Create the neural network */ + printf("Loading mode...\n"); + int model_data_size = 0; + unsigned char *model_data = load_model(model_name, &model_data_size); + ret = rknn_init(&ctx, model_data, model_data_size, 0); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + sizeof(rknn_sdk_version)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("sdk version: %s driver version: %s\n", version.api_version, + version.drv_version); + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, + io_num.n_output); + + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + dump_tensor_attr(&(input_attrs[i])); + } + + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + sizeof(rknn_tensor_attr)); + dump_tensor_attr(&(output_attrs[i])); + } + + int channel = 3; + int width = 0; + int height = 0; + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) + { + printf("model is NCHW input fmt\n"); + width = input_attrs[0].dims[0]; + height = input_attrs[0].dims[1]; + } + else + { + printf("model is NHWC input fmt\n"); + width = input_attrs[0].dims[1]; + height = input_attrs[0].dims[2]; + } + + printf("model input height=%d, width=%d, channel=%d\n", height, width, + channel); + + // Load image + CImg img(image_name); + unsigned char *input_data = NULL; + input_data = load_image(image_name, &img_height, &img_width, &img_channel, &input_attrs[0]); + if (!input_data) + { + return -1; + } + + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = width * height * channel; + inputs[0].fmt = RKNN_TENSOR_NHWC; + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) + { + outputs[i].want_float = 0; + } + + // DRM alloc buffer + drm_fd = drm_init(&drm_ctx); + drm_buf = drm_buf_alloc(&drm_ctx, drm_fd, img_width, img_height, channel * 8, + &buf_fd, &handle, &actual_size); + memcpy(drm_buf, input_data, img_width * img_height * channel); + void *resize_buf = malloc(height * width * channel); + + // init rga context + RGA_init(&rga_ctx); + if (is_map == 0) + { + inputs[0].pass_through = 0; + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + inputs[0].buf = resize_buf; + gettimeofday(&start_time, NULL); + rknn_inputs_set(ctx, io_num.n_input, inputs); + } + else + { + gettimeofday(&start_time, NULL); + ret = rknn_inputs_map(ctx, io_num.n_input, in_mem); + printf("input virt_addr = %p, phys_addr = 0x%llx, fd = %d, size = %d\n", + in_mem[0].logical_addr, in_mem[0].physical_addr, in_mem[0].fd, + in_mem[0].size); + if (in_mem[0].physical_addr == 0xffffffffffffffff) + { + printf("get unvalid input physical address, please extend in/out memory space\n"); + exit(-1); + } + else + { + // rga process by physical addr + img_resize_fast(&rga_ctx, buf_fd, img_width, img_height, in_mem[0].physical_addr, width, height); + } + rknn_inputs_sync(ctx, io_num.n_input, in_mem); + } + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + gettimeofday(&stop_time, NULL); + printf("once run use %f ms\n", + (__get_us(stop_time) - __get_us(start_time)) / 1000); + + //post process + float scale_w = (float)width / img_width; + float scale_h = (float)height / img_height; + + detect_result_group_t detect_result_group; + std::vector out_scales; + std::vector out_zps; + for (int i = 0; i < io_num.n_output; ++i) + { + out_scales.push_back(output_attrs[i].scale); + out_zps.push_back(output_attrs[i].zp); + } + post_process((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); + + // Draw Objects + char text[256]; + const unsigned char blue[] = {0, 0, 255}; + const unsigned char white[] = {255, 255, 255}; + for (int i = 0; i < detect_result_group.count; i++) + { + detect_result_t *det_result = &(detect_result_group.results[i]); + sprintf(text, "%s %.2f", det_result->name, det_result->prop); + printf("%s @ (%d %d %d %d) %f\n", + det_result->name, + det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom, + det_result->prop); + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + //draw box + img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U); + img.draw_text(x1, y1 - 12, text, white); + } + img.save("./out.bmp"); + + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + + // loop test + int test_count = 10; + gettimeofday(&start_time, NULL); + if (is_map == 0) + { + for (int i = 0; i < test_count; ++i) + { + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + rknn_inputs_set(ctx, io_num.n_input, inputs); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); +#if PERF_WITH_POST + post_process((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); +#endif + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + } + } + else + { + for (int i = 0; i < test_count; ++i) + { + img_resize_fast(&rga_ctx, buf_fd, img_width, img_height, in_mem[0].physical_addr, width, height); + rknn_inputs_sync(ctx, io_num.n_input, in_mem); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); +#if PERF_WITH_POST + post_process((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); +#endif + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + } + } + gettimeofday(&stop_time, NULL); + printf("run loop count = %d , average time: %f ms\n", test_count, + (__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count); + + // release + if (is_map) + { + ret = rknn_inputs_unmap(ctx, io_num.n_input, in_mem); + } + ret = rknn_destroy(ctx); + drm_buf_destroy(&drm_ctx, drm_fd, buf_fd, handle, drm_buf, actual_size); + + drm_deinit(&drm_ctx, drm_fd); + RGA_deinit(&rga_ctx); + if (model_data) + { + free(model_data); + } + + if (resize_buf) + { + free(resize_buf); + } + + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/postprocess.cc b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/postprocess.cc new file mode 100644 index 0000000..adf6ea8 --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/postprocess.cc @@ -0,0 +1,366 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include "postprocess.h" +#include +#define LABEL_NALE_TXT_PATH "./model/coco_80_labels_list.txt" + +static char *labels[OBJ_CLASS_NUM]; + +const int anchor0[6] = {10, 13, 16, 30, 33, 23}; +const int anchor1[6] = {30, 61, 62, 45, 59, 119}; +const int anchor2[6] = {116, 90, 156, 198, 373, 326}; + +inline static int clamp(float val, int min, int max) +{ + return val > min ? (val < max ? val : max) : min; +} + +char *readLine(FILE *fp, char *buffer, int *len) +{ + int ch; + int i = 0; + size_t buff_len = 0; + + buffer = (char *)malloc(buff_len + 1); + if (!buffer) + return NULL; // Out of memory + + while ((ch = fgetc(fp)) != '\n' && ch != EOF) + { + buff_len++; + void *tmp = realloc(buffer, buff_len + 1); + if (tmp == NULL) + { + free(buffer); + return NULL; // Out of memory + } + buffer = (char *)tmp; + + buffer[i] = (char)ch; + i++; + } + buffer[i] = '\0'; + + *len = buff_len; + + // Detect end + if (ch == EOF && (i == 0 || ferror(fp))) + { + free(buffer); + return NULL; + } + return buffer; +} + +int readLines(const char *fileName, char *lines[], int max_line) +{ + FILE *file = fopen(fileName, "r"); + char *s; + int i = 0; + int n = 0; + while ((s = readLine(file, s, &n)) != NULL) + { + lines[i++] = s; + if (i >= max_line) + break; + } + return i; +} + +int loadLabelName(const char *locationFilename, char *label[]) +{ + printf("loadLabelName %s\n", locationFilename); + readLines(locationFilename, label, OBJ_CLASS_NUM); + return 0; +} + +static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1) + 1.0); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1) + 1.0); + float i = w * h; + float u = (xmax0 - xmin0 + 1.0) * (ymax0 - ymin0 + 1.0) + (xmax1 - xmin1 + 1.0) * (ymax1 - ymin1 + 1.0) - i; + return u <= 0.f ? 0.f : (i / u); +} + +static int nms(int validCount, std::vector &outputLocations, std::vector classIds, std::vector &order, int filterId, float threshold) +{ + for (int i = 0; i < validCount; ++i) + { + if (order[i] == -1 || classIds[i] != filterId) + { + continue; + } + int n = order[i]; + for (int j = i + 1; j < validCount; ++j) + { + int m = order[j]; + if (m == -1 || classIds[i] != filterId) + { + continue; + } + float xmin0 = outputLocations[n * 4 + 0]; + float ymin0 = outputLocations[n * 4 + 1]; + float xmax0 = outputLocations[n * 4 + 0] + outputLocations[n * 4 + 2]; + float ymax0 = outputLocations[n * 4 + 1] + outputLocations[n * 4 + 3]; + + float xmin1 = outputLocations[m * 4 + 0]; + float ymin1 = outputLocations[m * 4 + 1]; + float xmax1 = outputLocations[m * 4 + 0] + outputLocations[m * 4 + 2]; + float ymax1 = outputLocations[m * 4 + 1] + outputLocations[m * 4 + 3]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou > threshold) + { + order[j] = -1; + } + } + } + return 0; +} + +static int quick_sort_indice_inverse( + std::vector &input, + int left, + int right, + std::vector &indices) +{ + float key; + int key_index; + int low = left; + int high = right; + if (left < right) + { + key_index = indices[left]; + key = input[left]; + while (low < high) + { + while (low < high && input[high] <= key) + { + high--; + } + input[low] = input[high]; + indices[low] = indices[high]; + while (low < high && input[low] >= key) + { + low++; + } + input[high] = input[low]; + indices[high] = indices[low]; + } + input[low] = key; + indices[low] = key_index; + quick_sort_indice_inverse(input, left, low - 1, indices); + quick_sort_indice_inverse(input, low + 1, right, indices); + } + return low; +} + +static float sigmoid(float x) +{ + return 1.0 / (1.0 + expf(-x)); +} + +static float unsigmoid(float y) +{ + return -1.0 * logf((1.0 / y) - 1.0); +} + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + return f; +} + +static uint8_t qnt_f32_to_affine(float f32, uint32_t zp, float scale) +{ + float dst_val = (f32 / scale) + zp; + uint8_t res = (uint8_t)__clip(dst_val, 0, 255); + return res; +} + +static float deqnt_affine_to_f32(uint8_t qnt, uint32_t zp, float scale) +{ + return ((float)qnt - (float)zp) * scale; +} + +static int process(uint8_t *input, int *anchor, int grid_h, int grid_w, int height, int width, int stride, + std::vector &boxes, std::vector &objProbs, std::vector &classId, + float threshold, uint32_t zp, float scale) +{ + + int validCount = 0; + int grid_len = grid_h * grid_w; + float thres = unsigmoid(threshold); + uint8_t thres_u8 = qnt_f32_to_affine(thres, zp, scale); + for (int a = 0; a < 3; a++) + { + for (int i = 0; i < grid_h; i++) + { + for (int j = 0; j < grid_w; j++) + { + uint8_t box_confidence = input[(PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j]; + if (box_confidence >= thres_u8) + { + int offset = (PROP_BOX_SIZE * a) * grid_len + i * grid_w + j; + uint8_t *in_ptr = input + offset; + float box_x = sigmoid(deqnt_affine_to_f32(*in_ptr, zp, scale)) * 2.0 - 0.5; + float box_y = sigmoid(deqnt_affine_to_f32(in_ptr[grid_len], zp, scale)) * 2.0 - 0.5; + float box_w = sigmoid(deqnt_affine_to_f32(in_ptr[2 * grid_len], zp, scale)) * 2.0; + float box_h = sigmoid(deqnt_affine_to_f32(in_ptr[3 * grid_len], zp, scale)) * 2.0; + box_x = (box_x + j) * (float)stride; + box_y = (box_y + i) * (float)stride; + box_w = box_w * box_w * (float)anchor[a * 2]; + box_h = box_h * box_h * (float)anchor[a * 2 + 1]; + box_x -= (box_w / 2.0); + box_y -= (box_h / 2.0); + boxes.push_back(box_x); + boxes.push_back(box_y); + boxes.push_back(box_w); + boxes.push_back(box_h); + + uint8_t maxClassProbs = in_ptr[5 * grid_len]; + int maxClassId = 0; + for (int k = 1; k < OBJ_CLASS_NUM; ++k) + { + uint8_t prob = in_ptr[(5 + k) * grid_len]; + if (prob > maxClassProbs) + { + maxClassId = k; + maxClassProbs = prob; + } + } + objProbs.push_back(sigmoid(deqnt_affine_to_f32(maxClassProbs, zp, scale))); + classId.push_back(maxClassId); + validCount++; + } + } + } + } + return validCount; +} + +int post_process(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector &qnt_zps, std::vector &qnt_scales, + detect_result_group_t *group) +{ + static int init = -1; + if (init == -1) + { + int ret = 0; + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) + { + return -1; + } + + init = 0; + } + memset(group, 0, sizeof(detect_result_group_t)); + + std::vector filterBoxes; + std::vector objProbs; + std::vector classId; + + // stride 8 + int stride0 = 8; + int grid_h0 = model_in_h / stride0; + int grid_w0 = model_in_w / stride0; + int validCount0 = 0; + validCount0 = process(input0, (int *)anchor0, grid_h0, grid_w0, model_in_h, model_in_w, + stride0, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[0], qnt_scales[0]); + + // stride 16 + int stride1 = 16; + int grid_h1 = model_in_h / stride1; + int grid_w1 = model_in_w / stride1; + int validCount1 = 0; + validCount1 = process(input1, (int *)anchor1, grid_h1, grid_w1, model_in_h, model_in_w, + stride1, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[1], qnt_scales[1]); + + // stride 32 + int stride2 = 32; + int grid_h2 = model_in_h / stride2; + int grid_w2 = model_in_w / stride2; + int validCount2 = 0; + validCount2 = process(input2, (int *)anchor2, grid_h2, grid_w2, model_in_h, model_in_w, + stride2, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[2], qnt_scales[2]); + + int validCount = validCount0 + validCount1 + validCount2; + // no object detect + if (validCount <= 0) + { + return 0; + } + + std::vector indexArray; + for (int i = 0; i < validCount; ++i) + { + indexArray.push_back(i); + } + + quick_sort_indice_inverse(objProbs, 0, validCount - 1, indexArray); + + std::set class_set(std::begin(classId), std::end(classId)); + + for (auto c : class_set) + { + nms(validCount, filterBoxes, classId, indexArray, c, nms_threshold); + } + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) + { + + if (indexArray[i] == -1 || last_count >= OBJ_NUMB_MAX_SIZE) + { + continue; + } + int n = indexArray[i]; + + float x1 = filterBoxes[n * 4 + 0]; + float y1 = filterBoxes[n * 4 + 1]; + float x2 = x1 + filterBoxes[n * 4 + 2]; + float y2 = y1 + filterBoxes[n * 4 + 3]; + int id = classId[n]; + float obj_conf = objProbs[i]; + + group->results[last_count].box.left = (int)(clamp(x1, 0, model_in_w) / scale_w); + group->results[last_count].box.top = (int)(clamp(y1, 0, model_in_h) / scale_h); + group->results[last_count].box.right = (int)(clamp(x2, 0, model_in_w) / scale_w); + group->results[last_count].box.bottom = (int)(clamp(y2, 0, model_in_h) / scale_h); + group->results[last_count].prop = obj_conf; + char *label = labels[id]; + strncpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE); + + // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, group->results[last_count].box.top, + // group->results[last_count].box.right, group->results[last_count].box.bottom, label); + last_count++; + } + group->count = last_count; + + return 0; +} diff --git a/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/rga_func.c b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/rga_func.c new file mode 100644 index 0000000..69caada --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/examples/rknn_zero_copy_demo/src/rga_func.c @@ -0,0 +1,114 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rga_func.h" + +int RGA_init(rga_context *rga_ctx) +{ + rga_ctx->rga_handle = dlopen("/usr/lib/librga.so", RTLD_LAZY); + if (!rga_ctx->rga_handle) + { + printf("dlopen /usr/lib/librga.so failed\n"); + return -1; + } + rga_ctx->init_func = (FUNC_RGA_INIT)dlsym(rga_ctx->rga_handle, "c_RkRgaInit"); + rga_ctx->deinit_func = (FUNC_RGA_DEINIT)dlsym(rga_ctx->rga_handle, "c_RkRgaDeInit"); + rga_ctx->blit_func = (FUNC_RGA_BLIT)dlsym(rga_ctx->rga_handle, "c_RkRgaBlit"); + rga_ctx->init_func(); + return 0; +} + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h) +{ + // printf("rga use fd, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) + { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = src_fd; + src.mmuFlag = 1; + // src.virAddr = (void *)psrc; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 0; + +#if defined(__arm__) + dst.phyAddr = (void *)((uint32_t)dst_phys); +#else + dst.phyAddr = (void *)dst_phys; +#endif + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) + { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h) +{ + // printf("rga use virtual, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) + { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = -1; + src.mmuFlag = 1; + src.virAddr = (void *)src_virt; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 1; + dst.virAddr = dst_virt; + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) + { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +int RGA_deinit(rga_context *rga_ctx) +{ + if(rga_ctx->rga_handle) + { + dlclose(rga_ctx->rga_handle); + rga_ctx->rga_handle = NULL; + } +} \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_api/librknn_api/include/rknn_api.h b/libs/rklibs/rknn/rknn_api/librknn_api/include/rknn_api.h new file mode 100644 index 0000000..a114c1f --- /dev/null +++ b/libs/rklibs/rknn/rknn_api/librknn_api/include/rknn_api.h @@ -0,0 +1,504 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + + +#ifndef _RKNN_RUNTIME_H +#define _RKNN_RUNTIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + Definition of extended flag for rknn_init. +*/ +/* set high priority context. */ +#define RKNN_FLAG_PRIOR_HIGH 0x00000000 + +/* set medium priority context */ +#define RKNN_FLAG_PRIOR_MEDIUM 0x00000001 + +/* set low priority context. */ +#define RKNN_FLAG_PRIOR_LOW 0x00000002 + +/* asynchronous mode. + when enable, rknn_outputs_get will not block for too long because it directly retrieves the result of + the previous frame which can increase the frame rate on single-threaded mode, but at the cost of + rknn_outputs_get not retrieves the result of the current frame. + in multi-threaded mode you do not need to turn this mode on. */ +#define RKNN_FLAG_ASYNC_MASK 0x00000004 + +/* collect performance mode. + when enable, you can get detailed performance reports via rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, ...), + but it will reduce the frame rate. */ +#define RKNN_FLAG_COLLECT_PERF_MASK 0x00000008 + +/* + save pre-compile model. +*/ +#define RKNN_FLAG_PRECOMPILE_MASK 0x00000020 + +/* + Error code returned by the RKNN API. +*/ +#define RKNN_SUCC 0 /* execute succeed. */ +#define RKNN_ERR_FAIL -1 /* execute failed. */ +#define RKNN_ERR_TIMEOUT -2 /* execute timeout. */ +#define RKNN_ERR_DEVICE_UNAVAILABLE -3 /* device is unavailable. */ +#define RKNN_ERR_MALLOC_FAIL -4 /* memory malloc fail. */ +#define RKNN_ERR_PARAM_INVALID -5 /* parameter is invalid. */ +#define RKNN_ERR_MODEL_INVALID -6 /* model is invalid. */ +#define RKNN_ERR_CTX_INVALID -7 /* context is invalid. */ +#define RKNN_ERR_INPUT_INVALID -8 /* input is invalid. */ +#define RKNN_ERR_OUTPUT_INVALID -9 /* output is invalid. */ +#define RKNN_ERR_DEVICE_UNMATCH -10 /* the device is unmatch, please update rknn sdk + and npu driver/firmware. */ +#define RKNN_ERR_INCOMPATILE_PRE_COMPILE_MODEL -11 /* This RKNN model use pre_compile mode, but not compatible with current driver. */ +//add by chifred: for reporting optimization version bug info +#define RKNN_ERR_INCOMPATILE_OPTIMIZATION_LEVEL_VERSION -12 /* This RKNN model set optimization level, but not compatible with current driver. */ +#define RKNN_ERR_TARGET_PLATFORM_UNMATCH -13 /* This RKNN model set target platform, but not compatible with current platform. */ +//chifred add end +#define RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER -14 /* This RKNN model is not a pre-compiled model, but the npu driver is mini driver. */ + +/* + Definition for tensor +*/ +#define RKNN_MAX_DIMS 16 /* maximum dimension of tensor. */ +#define RKNN_MAX_NAME_LEN 256 /* maximum name lenth of tensor. */ + + + +#ifdef __arm__ +typedef uint32_t rknn_context; +#else +typedef uint64_t rknn_context; +#endif + +/* + The query command for rknn_query +*/ +typedef enum _rknn_query_cmd { + RKNN_QUERY_IN_OUT_NUM = 0, /* query the number of input & output tensor. */ + RKNN_QUERY_INPUT_ATTR, /* query the attribute of input tensor. */ + RKNN_QUERY_OUTPUT_ATTR, /* query the attribute of output tensor. */ + RKNN_QUERY_PERF_DETAIL, /* query the detail performance, need set + RKNN_FLAG_COLLECT_PERF_MASK when call rknn_init. */ + RKNN_QUERY_PERF_RUN, /* query the time of run. */ + RKNN_QUERY_SDK_VERSION, /* query the sdk & driver version */ + RKNN_QUERY_PRE_COMPILE, /* query the pre compile model */ + + RKNN_QUERY_CMD_MAX +} rknn_query_cmd; + +/* + the tensor data type. +*/ +typedef enum _rknn_tensor_type { + RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */ + RKNN_TENSOR_FLOAT16, /* data type is float16. */ + RKNN_TENSOR_INT8, /* data type is int8. */ + RKNN_TENSOR_UINT8, /* data type is uint8. */ + RKNN_TENSOR_INT16, /* data type is int16. */ + + RKNN_TENSOR_TYPE_MAX +} rknn_tensor_type; + +/* + the quantitative type. +*/ +typedef enum _rknn_tensor_qnt_type { + RKNN_TENSOR_QNT_NONE = 0, /* none. */ + RKNN_TENSOR_QNT_DFP, /* dynamic fixed point. */ + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC, /* asymmetric affine. */ + + RKNN_TENSOR_QNT_MAX +} rknn_tensor_qnt_type; + +/* + the tensor data format. +*/ +typedef enum _rknn_tensor_format { + RKNN_TENSOR_NCHW = 0, /* data format is NCHW. */ + RKNN_TENSOR_NHWC, /* data format is NHWC. */ + + RKNN_TENSOR_FORMAT_MAX +} rknn_tensor_format; + +/* + the information for RKNN_QUERY_IN_OUT_NUM. +*/ +typedef struct _rknn_input_output_num { + uint32_t n_input; /* the number of input. */ + uint32_t n_output; /* the number of output. */ +} rknn_input_output_num; + +/* + the information for RKNN_QUERY_INPUT_ATTR / RKNN_QUERY_OUTPUT_ATTR. +*/ +typedef struct _rknn_tensor_attr { + uint32_t index; /* input parameter, the index of input/output tensor, + need set before call rknn_query. */ + + uint32_t n_dims; /* the number of dimensions. */ + uint32_t dims[RKNN_MAX_DIMS]; /* the dimensions array. */ + char name[RKNN_MAX_NAME_LEN]; /* the name of tensor. */ + + uint32_t n_elems; /* the number of elements. */ + uint32_t size; /* the bytes size of tensor. */ + + rknn_tensor_format fmt; /* the data format of tensor. */ + rknn_tensor_type type; /* the data type of tensor. */ + rknn_tensor_qnt_type qnt_type; /* the quantitative type of tensor. */ + int8_t fl; /* fractional length for RKNN_TENSOR_QNT_DFP. */ + uint32_t zp; /* zero point for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ + float scale; /* scale for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ +} rknn_tensor_attr; + +/* + the information for RKNN_QUERY_PERF_DETAIL. +*/ +typedef struct _rknn_perf_detail { + char* perf_data; /* the string pointer of perf detail. don't need free it by user. */ + uint64_t data_len; /* the string length. */ +} rknn_perf_detail; + +/* + the information for RKNN_QUERY_PERF_RUN. +*/ +typedef struct _rknn_perf_run { + int64_t run_duration; /* real inference time (us) */ +} rknn_perf_run; + +/* + the information for RKNN_QUERY_SDK_VERSION. +*/ +typedef struct _rknn_sdk_version { + char api_version[256]; /* the version of rknn api. */ + char drv_version[256]; /* the version of rknn driver. */ +} rknn_sdk_version; + +/* + The flags of rknn_tensor_mem. +*/ +typedef enum _rknn_tensor_mem_flags { + RKNN_TENSOR_MEMORY_FLAGS_UNKNOWN = 0, + RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE = 1, /*Used to mark in rknn_destroy_mem() whether it is necessary to release the "mem" pointer itself. + If the flag RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE is set, rknn_destroy_mem() will call free(mem).*/ + +} rknn_tensor_mem_flags; + + +/* + the memory information of tensor. +*/ +typedef struct _rknn_tensor_memory { + void* logical_addr; /* the virtual address of tensor buffer. */ + uint64_t physical_addr; /* the physical address of tensor buffer. */ + int32_t fd; /* the fd of tensor buffer. */ + uint32_t size; /* the size of tensor buffer. */ + uint32_t handle; /* the handle tensor buffer. */ + void * priv_data; /* the data which is reserved. */ + uint64_t reserved_flag; /* the flag which is reserved. */ +} rknn_tensor_mem; + +/* + the input information for rknn_input_set. +*/ +typedef struct _rknn_input { + uint32_t index; /* the input index. */ + void* buf; /* the input buf for index. */ + uint32_t size; /* the size of input buf. */ + uint8_t pass_through; /* pass through mode. + if TRUE, the buf data is passed directly to the input node of the rknn model + without any conversion. the following variables do not need to be set. + if FALSE, the buf data is converted into an input consistent with the model + according to the following type and fmt. so the following variables + need to be set.*/ + rknn_tensor_type type; /* the data type of input buf. */ + rknn_tensor_format fmt; /* the data format of input buf. + currently the internal input format of NPU is NCHW by default. + so entering NCHW data can avoid the format conversion in the driver. */ +} rknn_input; + +/* + the output information for rknn_outputs_get. +*/ +typedef struct _rknn_output { + uint8_t want_float; /* want transfer output data to float */ + uint8_t is_prealloc; /* whether buf is pre-allocated. + if true, the following variables need to be set. + if false, The following variables do not need to be set. */ + uint32_t index; /* the output index. */ + void* buf; /* the output buf for index. + when is_prealloc = FALSE and rknn_outputs_release called, + this buf pointer will be free and don't use it anymore. */ + uint32_t size; /* the size of output buf. */ +} rknn_output; + +/* + the extend information for rknn_run. +*/ +typedef struct _rknn_run_extend { + uint64_t frame_id; /* output parameter, indicate current frame id of run. */ +} rknn_run_extend; + +/* + the extend information for rknn_outputs_get. +*/ +typedef struct _rknn_output_extend { + uint64_t frame_id; /* output parameter, indicate the frame id of outputs, corresponds to + struct rknn_run_extend.frame_id.*/ +} rknn_output_extend; + +/* + the information for RKNN_QUERY_RKNN_PRECOMPILE. +*/ +typedef struct _rknn_precompile { + void* model_data; /* the pointer of precompile model. don't need free it by user. */ + uint32_t data_len; /* the model length. */ +} rknn_precompile; + + +/* rknn_init + + initial the context and load the rknn model. + + input: + rknn_context* context the pointer of context handle. + void* model pointer to the rknn model. + uint32_t size the size of rknn model. + uint32_t flag extend flag, see the define of RKNN_FLAG_XXX_XXX. + return: + int error code. +*/ +int rknn_init(rknn_context* context, void* model, uint32_t size, uint32_t flag); + + +/* rknn_destroy + + unload the rknn model and destroy the context. + + input: + rknn_context context the handle of context. + return: + int error code. +*/ +int rknn_destroy(rknn_context context); + + +/* rknn_query + + query the information about model or others. see rknn_query_cmd. + + input: + rknn_context context the handle of context. + rknn_query_cmd cmd the command of query. + void* info the buffer point of information. + uint32_t size the size of information. + return: + int error code. +*/ +int rknn_query(rknn_context context, rknn_query_cmd cmd, void* info, uint32_t size); + + +/* rknn_inputs_set + + set inputs information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_input inputs[] the arrays of inputs information, see rknn_input. + return: + int error code +*/ +int rknn_inputs_set(rknn_context context, uint32_t n_inputs, rknn_input inputs[]); + + +/* rknn_inputs_map + + map inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_map(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_sync + + synchronize inputs tensor buffer by input index of rknn model. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_sync(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_unmap + + unmap inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_unmap(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_run + + run the model to execute inference. + + input: + rknn_context context the handle of context. + rknn_run_extend* extend the extend information of run. + return: + int error code. +*/ +int rknn_run(rknn_context context, rknn_run_extend* extend); + + +/* rknn_outputs_get + + wait the inference to finish and get the outputs. + this function will block until inference finish. + the results will set to outputs[]. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_output outputs[] the arrays of output, see rknn_output. + rknn_output_extend* the extend information of output. + return: + int error code. +*/ +int rknn_outputs_get(rknn_context context, uint32_t n_outputs, rknn_output outputs[], rknn_output_extend* extend); + + +/* rknn_outputs_release + + release the outputs that get by rknn_outputs_get. + after called, the rknn_output[x].buf get from rknn_outputs_get will + also be free when rknn_output[x].is_prealloc = FALSE. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_output outputs[] the arrays of output. + return: + int error code +*/ +int rknn_outputs_release(rknn_context context, uint32_t n_ouputs, rknn_output outputs[]); + + +/* rknn_outputs_map + + map the model output tensors memory information. + The difference between this function and "rknn_outputs_get" is + that it directly maps the model output tensor memory location to the user. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_map(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_sync + + synchronize the output tensors buffer to ensure cache cohenrency, wait the inference to finish. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_sync(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_unmap + + unmap the outputs memory information that get by rknn_outputs_map. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_outputs_unmap(rknn_context context, uint32_t n_ouputs, rknn_tensor_mem mem[]); + +/* rknn_create_mem (memory allocated inside) + + Create tensor memory. This API require libdrm support! + + input: + rknn_context ctx the handle of context. + uint64_t size the size of tensor buffer. + return: + rknn_tensor_mem the pointer of tensor memory information. +*/ +rknn_tensor_mem* rknn_create_mem(rknn_context ctx, uint64_t size); + +/* rknn_destroy_mem (support allocate inside and outside) + + destroy tensor memory. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the pointer of tensor memory information. + return: + int error code +*/ +int rknn_destroy_mem(rknn_context ctx, rknn_tensor_mem *mem); + + + +/* rknn_set_io_mem + + set the input and output tensors buffer. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the array of tensor memory information. + rknn_tensor_attr *attr the attribute of input or output tensor buffer. + return: + int error code. +*/ +int rknn_set_io_mem(rknn_context ctx, rknn_tensor_mem *mem, rknn_tensor_attr *attr); + +#ifdef __cplusplus +} //extern "C" +#endif + +#endif //_RKNN_RUNTIME_H diff --git a/libs/rklibs/rknn/rknn_api/librknn_api/lib/librknn_api.so b/libs/rklibs/rknn/rknn_api/librknn_api/lib/librknn_api.so new file mode 100644 index 0000000..ae3b0d1 Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/librknn_api/lib/librknn_api.so differ diff --git a/libs/rklibs/rknn/rknn_api/librknn_api/lib64/librknn_api.so b/libs/rklibs/rknn/rknn_api/librknn_api/lib64/librknn_api.so new file mode 100644 index 0000000..950bc6e Binary files /dev/null and b/libs/rklibs/rknn/rknn_api/librknn_api/lib64/librknn_api.so differ diff --git a/libs/rklibs/rknn/rknn_utils/CMakeLists.txt b/libs/rklibs/rknn/rknn_utils/CMakeLists.txt new file mode 100644 index 0000000..ab0d1f6 --- /dev/null +++ b/libs/rklibs/rknn/rknn_utils/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_matmul_sample_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rknn_utils +include_directories(${CMAKE_SOURCE_DIR}/librknn_utils/include) +set(RKNN_UTILS_LIB ${CMAKE_CURRENT_SOURCE_DIR}/librknn_utils/${LIB_ARCH}/librknn_utils.so) + +add_executable(rknn_matmul_sample + example/matmul_sample.c +) + +target_link_libraries(rknn_matmul_sample + "-Wl,--allow-shlib-undefined" ${RKNN_UTILS_LIB} +) + +# install target +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_matmul_sample) +install(TARGETS rknn_matmul_sample DESTINATION bin) +install(PROGRAMS ${RKNN_UTILS_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknn/rknn_utils/README.md b/libs/rklibs/rknn/rknn_utils/README.md new file mode 100644 index 0000000..bdac65e --- /dev/null +++ b/libs/rklibs/rknn/rknn_utils/README.md @@ -0,0 +1,28 @@ +## build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## install + +connect device and push build output into `/userdata` + +``` +adb push install/rknn_matmul_sample/lib/librknn_utils.so /usr/lib +adb push install/rknn_matmul_sample /userdata/ +``` + +## run + +``` +adb shell +cd /userdata/rknn_matmul_sample/ +``` + +- /rk180x/rv1109/rv1126 +``` +./bin/rknn_matmul_sample 256 4096 +``` diff --git a/libs/rklibs/rknn/rknn_utils/build.sh b/libs/rklibs/rknn/rknn_utils/build.sh new file mode 100644 index 0000000..75a9a87 --- /dev/null +++ b/libs/rklibs/rknn/rknn_utils/build.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +# GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_utils/example/matmul_sample.c b/libs/rklibs/rknn/rknn_utils/example/matmul_sample.c new file mode 100644 index 0000000..7fec91a --- /dev/null +++ b/libs/rklibs/rknn/rknn_utils/example/matmul_sample.c @@ -0,0 +1,182 @@ +/** + * Example: Calculate (x-y)^2 using Matmul API + */ + + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#include +#include "rknn_matmul_api.h" + +/*------------------------------------------- + Macros and Variables +-------------------------------------------*/ + +double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); } + +static int gen_random_data(int8_t *x,int len,int min_value,int max_value) { + srand((unsigned int)time(NULL)); + int range = max_value-min_value+1; + for (int i = 0;i \n",argv[0]); + return -1; + } + int DIM = atoi(argv[1]); + int BATCH_SIZE = atoi(argv[2]); + rknn_tensor_type dtype = RKNN_TENSOR_INT8; + int8_t *x = (int8_t *)malloc(DIM*sizeof(int8_t)); + int8_t *y = (int8_t *)malloc(DIM*BATCH_SIZE*sizeof(int8_t)); + float out_fp32_buf[BATCH_SIZE]; + float res_cpu[BATCH_SIZE]; + int x_size = DIM; + int y_size = DIM*BATCH_SIZE; + int test_count = 100; + float err; + struct timeval start_time, stop_time; + + int *square_y = (int *)malloc(BATCH_SIZE*sizeof(int)); + + //generate random test data + gen_random_data(x,x_size,-128,127); + gen_random_data(y,y_size,-128,127); + pre_process_y(y,DIM,BATCH_SIZE,square_y); + + rknn_matmul_handle_t handle= rknn_matmul_load(x,y, 1, DIM, BATCH_SIZE, dtype); + if (handle == NULL) + { + fprintf(stderr, "rknn_matmul_load failed.\n"); + return -1; + } + + //loop for matmul + gettimeofday(&start_time, NULL); + for(int i = 0;i + +/* + the tensor data type. +*/ + +typedef enum _rknn_tensor_type { + RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */ + RKNN_TENSOR_FLOAT16, /* data type is float16. */ + RKNN_TENSOR_INT8, /* data type is int8. */ + RKNN_TENSOR_UINT8, /* data type is uint8. */ + RKNN_TENSOR_INT16, /* data type is int16. */ + + RKNN_TENSOR_TYPE_MAX +} rknn_tensor_type; + +#ifdef __arm__ +typedef uint32_t rknn_context; +#else +typedef uint64_t rknn_context; +#endif + + +/* + Definition of handle for rknn_matmul_run. +*/ +typedef struct _rknn_matmul_t{ + void* A; + void* B; + int32_t M; /* the low rank dimension of A */ + int32_t K; /* the high rank dimension of A and B*/ + int32_t N; /* the low rank dimension of B */ + rknn_tensor_type in_dtype; /* the input buffer type */ + rknn_context rknn_ctx; /* rknn context handle */ +} rknn_matmul_t; + +typedef rknn_matmul_t* rknn_matmul_handle_t; + +/* rknn_matmul_load + + load input buffer and initial the context. + + [input]: + void* a the pointer of A Matrix buffer, number of element is K*M, alloced by user. + void* b the pointer to B Matrix buffer, number of element is K*N, alloced by user. + int32_t M the low rank dimension of A + int32_t K the high rank dimension of A + int32_t N the low rank dimension of B + rknn_tensor_type dtype; the input buffer type, now only support RKNN_TENSOR_UINT8 and RKNN_TENSOR_INT8. + [output]: + rknn_matmul_handle_t the handle of mamul. it will return NULL if failed. +*/ +rknn_matmul_handle_t rknn_matmul_load(void *a, void *b, int M, int K, int N, rknn_tensor_type dtype); + +/* rknn_matmul_run + + run Matmul with fixed shape. + + [input]: + rknn_matmul_handle_t matmul_handle the handle of mamul, get by rknn_matmul_load. + float *c the pointer of C Matrix buffer, number of element is 256x4096, alloced by user. + [output]: + int error code. +*/ +int rknn_matmul_run(rknn_matmul_handle_t matmul_handle, float *c); + +/* rknn_matmul_unload + + destroy the context. + + [input]: + rknn_matmul_handle_t matmul_handle the handle of mamul, get by rknn_matmul_load. + [output]: + int error code. +*/ +int rknn_matmul_unload(rknn_matmul_handle_t matmul_handle); + +#ifdef __cplusplus +} //extern "C" +#endif + +#endif //_RKNN_MATMUL_H \ No newline at end of file diff --git a/libs/rklibs/rknn/rknn_utils/librknn_utils/lib/librknn_utils.so b/libs/rklibs/rknn/rknn_utils/librknn_utils/lib/librknn_utils.so new file mode 100644 index 0000000..a35f60e Binary files /dev/null and b/libs/rklibs/rknn/rknn_utils/librknn_utils/lib/librknn_utils.so differ diff --git a/libs/rklibs/rknn/rknn_utils/librknn_utils/lib64/librknn_utils.so b/libs/rklibs/rknn/rknn_utils/librknn_utils/lib64/librknn_utils.so new file mode 100644 index 0000000..2ca9bf6 Binary files /dev/null and b/libs/rklibs/rknn/rknn_utils/librknn_utils/lib64/librknn_utils.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/memory_profile b/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/memory_profile new file mode 100644 index 0000000..c8d125b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/memory_profile differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/libann_plugin.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/libann_plugin.so new file mode 100644 index 0000000..269d25a Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/libann_plugin.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/librknn_plugin.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/librknn_plugin.so new file mode 100644 index 0000000..27977c6 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-aarch64/usr/lib/npu/rknn/plugins/librknn_plugin.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libArchModelSw.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libGAL.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libGAL.so new file mode 100644 index 0000000..fe54bb2 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libGAL.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libNNArchPerf.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..d3e8586 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libOpenVXU.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..ccae677 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libVSC_Lite.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libVSC_Lite.so new file mode 100644 index 0000000..041deca Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/libVSC_Lite.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/librknn_runtime.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..b2c409b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-mini/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libArchModelSw.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libGAL.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libGAL.so new file mode 100644 index 0000000..669fadd Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libGAL.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libNNArchPerf.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..44c4399 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libOpenVXU.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..40c62f4 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libVSC_Lite.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libVSC_Lite.so new file mode 100644 index 0000000..041deca Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/libVSC_Lite.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/librknn_runtime.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..f2c5c5f Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma-mini/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/bin/rknn_server b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/bin/rknn_server new file mode 100644 index 0000000..1ff3313 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/bin/rknn_server differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libArchModelSw.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libCLC.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libCLC.so new file mode 100644 index 0000000..642b34b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libCLC.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libGAL.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libGAL.so new file mode 100644 index 0000000..7f5902c Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libGAL.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNArchPerf.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNGPUBinary.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNGPUBinary.so new file mode 100644 index 0000000..c43c374 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNGPUBinary.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNVXCBinary.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNVXCBinary.so new file mode 100644 index 0000000..2e20033 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libNNVXCBinary.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenCL.so.1.2 b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenCL.so.1.2 new file mode 100644 index 0000000..e6c22e1 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenCL.so.1.2 differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..cb0ef11 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenVXU.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..40c62f4 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOvx12VXCBinary.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOvx12VXCBinary.so new file mode 100644 index 0000000..ffb6711 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libOvx12VXCBinary.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libVSC.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libVSC.so new file mode 100644 index 0000000..639013d Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libVSC.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libneuralnetworks.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libneuralnetworks.so new file mode 100644 index 0000000..4afe4f3 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/libneuralnetworks.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/librknn_runtime.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..3576099 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/memory_profile b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/memory_profile new file mode 100644 index 0000000..542fae6 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/memory_profile differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/libann_plugin.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/libann_plugin.so new file mode 100644 index 0000000..da693e0 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/libann_plugin.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/librknn_plugin.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/librknn_plugin.so new file mode 100644 index 0000000..8d675de Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf-puma/usr/lib/npu/rknn/plugins/librknn_plugin.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/libVSC.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/libVSC.so new file mode 100644 index 0000000..639013d Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/libVSC.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/librknn_runtime.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..a70f988 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/memory_profile b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/memory_profile new file mode 100644 index 0000000..04dce87 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/memory_profile differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/plugins/libann_plugin.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/plugins/libann_plugin.so new file mode 100644 index 0000000..a3f8ba4 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/plugins/libann_plugin.so differ diff --git a/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/plugins/librknn_plugin.so b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/plugins/librknn_plugin.so new file mode 100644 index 0000000..b1f81ec Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/drivers/linux-armhf/usr/lib/npu/rknn/plugins/librknn_plugin.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/dog_224x224.jpg b/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/dog_224x224.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/mobilenet_v1.rknn b/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/mobilenet_v1.rknn new file mode 100644 index 0000000..1a1ff1b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/mobilenet_v1.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/test.py b/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/test.py new file mode 100644 index 0000000..2484b68 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/python/example/mobilenet_v1/test.py @@ -0,0 +1,49 @@ +import numpy as np +import cv2 +from rknn.api import RKNN + +def show_outputs(outputs): + output = outputs[0][0] + output_sorted = sorted(output, reverse=True) + top5_str = 'mobilenet_v1\n-----TOP 5-----\n' + for i in range(5): + value = output_sorted[i] + index = np.where(output == value) + for j in range(len(index)): + if (i + j) >= 5: + break + if value > 0: + topi = '{}: {}\n'.format(index[j], value) + else: + topi = '-1: 0.0\n' + top5_str += topi + print(top5_str) + +if __name__ == '__main__': + + # Create RKNN object + rknn = RKNN() + + # Direct load rknn model + print('--> Loading RKNN model') + ret = rknn.load_rknn('./mobilenet_v1.rknn') + if ret != 0: + print('Load mobilenet_v1.rknn failed!') + exit(ret) + print('done') + + # Set inputs + img = cv2.imread('./dog_224x224.jpg') + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + + # Init Runtime + rknn.init_runtime() + + # Inference + print('--> Running model') + outputs = rknn.inference(inputs=[img]) + show_outputs(outputs) + print('done') + + rknn.release() + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/__init__.py b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/__init__.py new file mode 100644 index 0000000..5b98fb2 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/__init__.py @@ -0,0 +1 @@ +from rknn.api.rknn import RKNN diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn.py b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn.py new file mode 100644 index 0000000..42b7e69 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn.py @@ -0,0 +1,130 @@ +# -*- coding:utf-8 -*- +import os +import sys +import traceback +import re + +from argparse import Namespace +from .rknn_base import RKNNBase +from .rknn_runtime import RKNNRuntime + +class RKNN: + """ + Rockchip NN SDK + """ + def __init__(self): + cur_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + self.rknn_base = RKNNBase(cur_path) + + def load_rknn(self, path): + """ + Load RKNN model + :param path: RKNN model file path + :return: success: 0, failure: -1 + """ + if not os.path.exists(path): + print('RKNN model file {} not exists!'.format(path)) + return -1 + try: + ret = self.rknn_base.import_rknn(path) + except: + print('Catch exception when loading RKNN model [{}]!'.format(path)) + RKNN._print_traceback(traceback.format_exc()) + return -1 + else: + if ret != 0: + print('Load RKNN model [{}] failed!'.format(path)) + return ret + + def init_runtime(self, perf_debug=False): + """ + Init run time environment. Needed by called before inference or eval performance. + :param perf_debug: enable dump layer performance. + :return: success: 0, failure: -1 + """ + try: + self.rknn_base.init_runtime(target=None, device_id=None, perf_debug=perf_debug) + except: + print('Catch exception when init runtime!') + RKNN._print_traceback(traceback.format_exc()) + return -1 + + return 0 + + def inference(self, inputs, data_type='uint8', data_format='nhwc', outputs=None): + """ + Run model inference + :param inputs: Input data List (ndarray list) + :param data_type: Input data type + :param data_format: Input data format + :param outputs: Output data list, for determine shape and dtype (empty ndarray) + :return: Output data (ndarray list) + """ + try: + results = self.rknn_base.inference(inputs=inputs, data_type=data_type, + data_format=data_format, outputs=outputs) + except: + print('Catch exception when inference!') + RKNN._print_traceback(traceback.format_exc()) + results = None + finally: + useless_file = os.path.join(os.getcwd(), 'viv_vir_cl_intrinsic_ImgLS.lib') + if os.path.exists(useless_file): + os.remove(useless_file) + return results + + + def eval_perf(self, inputs, data_type='uint8', data_format='nhwc', is_print=True): + """ + Evaluate model performance + :param inputs: Input data list (ndarray list) + :param data_type: Input data type + :param data_format: Input data format + :param is_print: Format print perf result + :return: Performance Result (dict) + """ + try: + perfs = self.rknn_base.eval_performance(inputs, data_type, data_format) + if perfs is None: + print('Error: Performance information is empty, some mistakes may happen.') + return perfs + if is_print: + print(self.rknn_base.format_perf_detail(detail=perfs)) + except: + print('Catch exception when evaluating model performance!') + RKNN._print_traceback(traceback.format_exc()) + return None + finally: + useless_file = os.path.join(os.getcwd(), 'viv_vir_cl_intrinsic_ImgLS.lib') + if os.path.exists(useless_file): + os.remove(useless_file) + + return perfs + + def get_sdk_version(self): + """ + Get SDK version + :return: sdk_version + """ + try: + sdk_version = self.rknn_base.get_sdk_version() + except Exception: + print('Catch exception when get sdk version') + RKNN._print_traceback(traceback.format_exc()) + return None + + return sdk_version + + def release(self): + """ + Release RKNN resource + :return: None + """ + self.rknn_base.release() + + @staticmethod + def _print_traceback(traceback_info): + reg = re.compile(re.escape(RKNNBase.redirect_src), re.IGNORECASE) + traceback_info = reg.sub(RKNNBase.redirect_dst, traceback_info) + print(traceback_info) + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_base.cpython-36m-aarch64-linux-gnu.so b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_base.cpython-36m-aarch64-linux-gnu.so new file mode 100644 index 0000000..f94360c Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_base.cpython-36m-aarch64-linux-gnu.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_model.cpython-36m-aarch64-linux-gnu.so b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_model.cpython-36m-aarch64-linux-gnu.so new file mode 100644 index 0000000..533a9bb Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_model.cpython-36m-aarch64-linux-gnu.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_runtime.cpython-36m-aarch64-linux-gnu.so b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_runtime.cpython-36m-aarch64-linux-gnu.so new file mode 100644 index 0000000..f98b431 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/python/rknn/api/rknn_runtime.cpython-36m-aarch64-linux-gnu.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/CImg/CImg.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/CImg/CImg.h new file mode 100644 index 0000000..2bfe567 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/CImg/CImg.h @@ -0,0 +1,65138 @@ +/* + # + # File : CImg.h + # ( C++ header file ) + # + # Description : C++ Template Image Processing Toolkit. + # This file is the main component of the CImg Library project. + # ( http://cimg.eu ) + # + # Project manager : David Tschumperlé + # ( http://tschumperle.users.greyc.fr/ ) + # + # A complete list of contributors is available in file 'README.txt' + # distributed within the CImg package. + # + # Licenses : This file is 'dual-licensed', you have to choose one + # of the two licenses below to apply. + # + # CeCILL-C + # The CeCILL-C license is close to the GNU LGPL. + # ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html ) + # + # or CeCILL v2.1 + # The CeCILL license is compatible with the GNU GPL. + # ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html ) + # + # This software is governed either by the CeCILL or the CeCILL-C license + # under French law and abiding by the rules of distribution of free software. + # You can use, modify and or redistribute the software under the terms of + # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA + # at the following URL: "http://cecill.info". + # + # As a counterpart to the access to the source code and rights to copy, + # modify and redistribute granted by the license, users are provided only + # with a limited warranty and the software's author, the holder of the + # economic rights, and the successive licensors have only limited + # liability. + # + # In this respect, the user's attention is drawn to the risks associated + # with loading, using, modifying and/or developing or reproducing the + # software by the user in light of its specific status of free software, + # that may mean that it is complicated to manipulate, and that also + # therefore means that it is reserved for developers and experienced + # professionals having in-depth computer knowledge. Users are therefore + # encouraged to load and test the software's suitability as regards their + # requirements in conditions enabling the security of their systems and/or + # data to be ensured and, more generally, to use and operate it in the + # same conditions as regards security. + # + # The fact that you are presently reading this means that you have had + # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms. + # +*/ + +// Set version number of the library. +#ifndef cimg_version +#define cimg_version 298 + +/*----------------------------------------------------------- + # + # Test and possibly auto-set CImg configuration variables + # and include required headers. + # + # If you find that the default configuration variables are + # not adapted to your system, you can override their values + # before including the header file "CImg.h" + # (use the #define directive). + # + ------------------------------------------------------------*/ + +// Include standard C++ headers. +// This is the minimal set of required headers to make CImg-based codes compile. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define cimg_str(x) #x +#define cimg_str2(x) cimg_str(x) + +// Detect/configure OS variables. +// +// Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies). +// '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...). +// '2' for Microsoft Windows. +// (auto-detection is performed if 'cimg_OS' is not set by the user). +#ifndef cimg_OS +#if defined(unix) || defined(__unix) || defined(__unix__) \ + || defined(linux) || defined(__linux) || defined(__linux__) \ + || defined(sun) || defined(__sun) \ + || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \ + || defined(__FreeBSD__) || defined (__DragonFly__) \ + || defined(sgi) || defined(__sgi) \ + || defined(__OSX__) || defined(__MACOSX__) || defined(__APPLE__) \ + || defined(__CYGWIN__) +#define cimg_OS 1 +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ + || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) +#define cimg_OS 2 +#else +#define cimg_OS 0 +#endif +#elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2) +#error CImg Library: Invalid configuration variable 'cimg_OS'. +#error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows'). +#endif +#ifndef cimg_date +#define cimg_date __DATE__ +#endif +#ifndef cimg_time +#define cimg_time __TIME__ +#endif + +// Disable silly warnings on some Microsoft VC++ compilers. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4127) +#pragma warning(disable:4244) +#pragma warning(disable:4311) +#pragma warning(disable:4312) +#pragma warning(disable:4319) +#pragma warning(disable:4512) +#pragma warning(disable:4571) +#pragma warning(disable:4640) +#pragma warning(disable:4706) +#pragma warning(disable:4710) +#pragma warning(disable:4800) +#pragma warning(disable:4804) +#pragma warning(disable:4820) +#pragma warning(disable:4996) + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#endif +#endif + +// Define correct string functions for each compiler and OS. +#if cimg_OS==2 && defined(_MSC_VER) +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#include +#if defined(__MACOSX__) || defined(__APPLE__) +#define cimg_sscanf cimg::_sscanf +#define cimg_sprintf cimg::_sprintf +#define cimg_snprintf cimg::_snprintf +#define cimg_vsnprintf cimg::_vsnprintf +#else +#define cimg_sscanf std::sscanf +#define cimg_sprintf std::sprintf +#define cimg_snprintf snprintf +#define cimg_vsnprintf vsnprintf +#endif +#endif + +// Include OS-specific headers. +#if cimg_OS==1 +#include +#include +#include +#include +#include +#include +#elif cimg_OS==2 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#ifndef _WIN32_IE +#define _WIN32_IE 0x0400 +#endif +#include +#include +#include +enum {FALSE_WIN = 0}; +#endif + +// Look for C++11 features. +#ifndef cimg_use_cpp11 +#if __cplusplus>201100 +#define cimg_use_cpp11 1 +#else +#define cimg_use_cpp11 0 +#endif +#endif +#if cimg_use_cpp11==1 +#include +#include +#endif + +// Convenient macro to define pragma +#ifdef _MSC_VER +#define cimg_pragma(x) __pragma(x) +#else +#define cimg_pragma(x) _Pragma(#x) +#endif + +// Define own types 'cimg_long/ulong' and 'cimg_int64/uint64' to ensure portability. +// ( constrained to 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' and 'sizeof(cimg_int64/cimg_uint64)=8' ). +#if cimg_OS==2 + +#define cimg_uint64 unsigned __int64 +#define cimg_int64 __int64 +#define cimg_ulong UINT_PTR +#define cimg_long INT_PTR +#ifdef _MSC_VER +#define cimg_fuint64 "%I64u" +#define cimg_fint64 "%I64d" +#else +#define cimg_fuint64 "%llu" +#define cimg_fint64 "%lld" +#endif + +#else + +#if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX)) +#define cimg_uint64 unsigned long long +#define cimg_int64 long long +#define cimg_fuint64 "%llu" +#define cimg_fint64 "%lld" +#else +#define cimg_uint64 unsigned long +#define cimg_int64 long +#define cimg_fuint64 "%lu" +#define cimg_fint64 "%ld" +#endif + +#if defined(__arm__) || defined(_M_ARM) +#define cimg_ulong unsigned long long +#define cimg_long long long +#else +#define cimg_ulong unsigned long +#define cimg_long long +#endif + +#endif + +// Configure filename separator. +// +// Filename separator is set by default to '/', except for Windows where it is '\'. +#ifndef cimg_file_separator +#if cimg_OS==2 +#define cimg_file_separator '\\' +#else +#define cimg_file_separator '/' +#endif +#endif + +// Configure verbosity of output messages. +// +// Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode). +// '1' to output library messages on the console. +// '2' to output library messages on a basic dialog window (default behavior). +// '3' to do as '1' + add extra warnings (may slow down the code!). +// '4' to do as '2' + add extra warnings (may slow down the code!). +// +// Define 'cimg_strict_warnings' to replace warning messages by exception throwns. +// +// Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals. +#ifndef cimg_verbosity +#if cimg_OS==2 +#define cimg_verbosity 2 +#else +#define cimg_verbosity 1 +#endif +#elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4) +#error CImg Library: Configuration variable 'cimg_verbosity' is badly defined. +#error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }). +#endif + +// Configure OpenMP support. +// (http://www.openmp.org) +// +// Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+). +// +// OpenMP directives are used in many CImg functions to get +// advantages of multi-core CPUs. +#if !defined(cimg_use_openmp) +#ifdef _OPENMP +#define cimg_use_openmp 1 +#else +#define cimg_use_openmp 0 +#endif +#endif +#if cimg_use_openmp!=0 +#include +#define cimg_pragma_openmp(p) cimg_pragma(omp p) +#else +#define cimg_pragma_openmp(p) +#endif + +// Configure the 'abort' signal handler (does nothing by default). +// A typical signal handler can be defined in your own source like this: +// #define cimg_abort_test if (is_abort) throw CImgAbortException("") +// +// where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method. +// 'cimg_abort_test2' does the same but is called more often (in inner loops). +#if defined(cimg_abort_test) && cimg_use_openmp!=0 + +// Define abort macros to be used with OpenMP. +#ifndef _cimg_abort_init_openmp +#define _cimg_abort_init_openmp bool _cimg_abort_go_openmp = true; cimg::unused(_cimg_abort_go_openmp) +#endif +#ifndef _cimg_abort_try_openmp +#define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try +#endif +#ifndef _cimg_abort_catch_openmp +#define _cimg_abort_catch_openmp catch (CImgAbortException&) { cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#endif +#ifndef _cimg_abort_catch_fill_openmp +#define _cimg_abort_catch_fill_openmp \ + catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg::string(e._message).move_to(is_error); \ + cimg_pragma(omp atomic) _cimg_abort_go_openmp&=false; } +#endif +#ifdef cimg_abort_test2 +#ifndef _cimg_abort_try_openmp2 +#define _cimg_abort_try_openmp2 _cimg_abort_try_openmp +#endif +#ifndef _cimg_abort_catch_openmp2 +#define _cimg_abort_catch_openmp2 _cimg_abort_catch_openmp +#endif +#endif +#endif + +#ifndef _cimg_abort_init_openmp +#define _cimg_abort_init_openmp +#endif +#ifndef _cimg_abort_try_openmp +#define _cimg_abort_try_openmp +#endif +#ifndef _cimg_abort_catch_openmp +#define _cimg_abort_catch_openmp +#endif +#ifndef _cimg_abort_try_openmp2 +#define _cimg_abort_try_openmp2 +#endif +#ifndef _cimg_abort_catch_openmp2 +#define _cimg_abort_catch_openmp2 +#endif +#ifndef _cimg_abort_catch_fill_openmp +#define _cimg_abort_catch_fill_openmp +#endif +#ifndef cimg_abort_init +#define cimg_abort_init +#endif +#ifndef cimg_abort_test +#define cimg_abort_test +#endif +#ifndef cimg_abort_test2 +#define cimg_abort_test2 +#endif + +// Configure display framework. +// +// Define 'cimg_display' to: '0' to disable display capabilities. +// '1' to use the X-Window framework (X11). +// '2' to use the Microsoft GDI32 framework. +#ifndef cimg_display +#if cimg_OS==0 +#define cimg_display 0 +#elif cimg_OS==1 +#define cimg_display 1 +#elif cimg_OS==2 +#define cimg_display 2 +#endif +#elif !(cimg_display==0 || cimg_display==1 || cimg_display==2) +#error CImg Library: Configuration variable 'cimg_display' is badly defined. +#error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }). +#endif + +// Include display-specific headers. +#if cimg_display==1 +#include +#include +#include +#include +#ifdef cimg_use_xshm +#include +#include +#include +#endif +#ifdef cimg_use_xrandr +#include +#endif +#endif +#ifndef cimg_appname +#define cimg_appname "CImg" +#endif + +// Configure OpenCV support. +// (http://opencv.willowgarage.com/wiki/) +// +// Define 'cimg_use_opencv' to enable OpenCV support. +// +// OpenCV library may be used to access images from cameras +// (see method 'CImg::load_camera()'). +#ifdef cimg_use_opencv +#ifdef True +#undef True +#define _cimg_redefine_True +#endif +#ifdef False +#undef False +#define _cimg_redefine_False +#endif +#ifdef Status +#undef Status +#define _cimg_redefine_Status +#endif +#include +#include +#if CV_MAJOR_VERSION>=3 +#define _cimg_fourcc cv::VideoWriter::fourcc +#define _cimg_cap_prop_frame_width cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH +#define _cimg_cap_prop_frame_height cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT +#define _cimg_cap_prop_frame_count cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT +#else +#define _cimg_fourcc CV_FOURCC +#define _cimg_cap_prop_frame_width CV_CAP_PROP_FRAME_WIDTH +#define _cimg_cap_prop_frame_height CV_CAP_PROP_FRAME_HEIGHT +#define _cimg_cap_prop_frame_count CV_CAP_PROP_FRAME_COUNT +#endif +#endif + +// Configure LibPNG support. +// (http://www.libpng.org) +// +// Define 'cimg_use_png' to enable LibPNG support. +// +// PNG library may be used to get a native support of '.png' files. +// (see methods 'CImg::{load,save}_png()'. +#ifdef cimg_use_png +extern "C" { +#include "png.h" +} +#endif + +// Configure LibJPEG support. +// (http://en.wikipedia.org/wiki/Libjpeg) +// +// Define 'cimg_use_jpeg' to enable LibJPEG support. +// +// JPEG library may be used to get a native support of '.jpg' files. +// (see methods 'CImg::{load,save}_jpeg()'). +#ifdef cimg_use_jpeg +extern "C" { +#include "jpeglib.h" +#include "setjmp.h" +} +#endif + +// Configure LibTIFF support. +// (http://www.libtiff.org) +// +// Define 'cimg_use_tiff' to enable LibTIFF support. +// +// TIFF library may be used to get a native support of '.tif' files. +// (see methods 'CImg[List]::{load,save}_tiff()'). +#ifdef cimg_use_tiff +extern "C" { +#define uint64 uint64_hack_ +#define int64 int64_hack_ +#include "tiffio.h" +#undef uint64 +#undef int64 +} +#endif + +// Configure HEIF support +// (https://github.com/strukturag/libheif) +// +// Define 'cimg_use_heif' to enable HEIF support. +// +// HEIF library may be used to get a native support of '.heic' and '.avif' files. +// (see method 'CImg::load_heif()'). +#ifdef cimg_use_heif +#include +#endif + +// Configure LibMINC2 support. +// (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference) +// +// Define 'cimg_use_minc2' to enable LibMINC2 support. +// +// MINC2 library may be used to get a native support of '.mnc' files. +// (see methods 'CImg::{load,save}_minc2()'). +#ifdef cimg_use_minc2 +#include "minc_io_simple_volume.h" +#include "minc_1_simple.h" +#include "minc_1_simple_rw.h" +#endif + +// Configure Zlib support. +// (http://www.zlib.net) +// +// Define 'cimg_use_zlib' to enable Zlib support. +// +// Zlib library may be used to allow compressed data in '.cimgz' files +// (see methods 'CImg[List]::{load,save}_cimg()'). +#ifdef cimg_use_zlib +extern "C" { +#include "zlib.h" +} +#endif + +// Configure libcurl support. +// (http://curl.haxx.se/libcurl/) +// +// Define 'cimg_use_curl' to enable libcurl support. +// +// Libcurl may be used to get a native support of file downloading from the network. +// (see method 'cimg::load_network()'.) +#ifdef cimg_use_curl +#include "curl/curl.h" +#endif + +// Configure Magick++ support. +// (http://www.imagemagick.org/Magick++) +// +// Define 'cimg_use_magick' to enable Magick++ support. +// +// Magick++ library may be used to get a native support of various image file formats. +// (see methods 'CImg::{load,save}()'). +#ifdef cimg_use_magick +#include "Magick++.h" +#endif + +// Configure FFTW3 support. +// (http://www.fftw.org) +// +// Define 'cimg_use_fftw3' to enable libFFTW3 support. +// +// FFTW3 library may be used to efficiently compute the Fast Fourier Transform +// of image data, without restriction on the image size. +// (see method 'CImg[List]::FFT()'). +#ifdef cimg_use_fftw3 +extern "C" { +#include "fftw3.h" +} +#endif + +// Configure LibBoard support. +// (http://libboard.sourceforge.net/) +// +// Define 'cimg_use_board' to enable Board support. +// +// Board library may be used to draw 3D objects in vector-graphics canvas +// that can be saved as '.ps' or '.svg' files afterwards. +// (see method 'CImg::draw_object3d()'). +#ifdef cimg_use_board +#include "Board.h" +#endif + +// Configure OpenEXR support. +// (http://www.openexr.com/) +// +// Define 'cimg_use_openexr' to enable OpenEXR support. +// +// OpenEXR library may be used to get a native support of '.exr' files. +// (see methods 'CImg::{load,save}_exr()'). +#ifdef cimg_use_openexr +#if __GNUC__>=5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" +#pragma GCC diagnostic ignored "-Wdeprecated-copy" +#pragma GCC diagnostic ignored "-Wshadow" +#endif +#include "ImfRgbaFile.h" +#include "ImfInputFile.h" +#include "ImfChannelList.h" +#include "ImfMatrixAttribute.h" +#include "ImfArray.h" +#if __GNUC__>=5 +#pragma GCC diagnostic pop +#endif +#endif + +// Configure TinyEXR support. +// (https://github.com/syoyo/tinyexr) +// +// Define 'cimg_use_tinyexr' to enable TinyEXR support. +// +// TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images. +#ifdef cimg_use_tinyexr +#ifndef TINYEXR_IMPLEMENTATION +#define TINYEXR_IMPLEMENTATION +#endif +#include "tinyexr.h" +#endif + +// Lapack configuration. +// (http://www.netlib.org/lapack) +// +// Define 'cimg_use_lapack' to enable LAPACK support. +// +// Lapack library may be used in several CImg methods to speed up +// matrix computations (eigenvalues, inverse, ...). +#ifdef cimg_use_lapack +extern "C" { + extern void sgetrf_(int*, int*, float*, int*, int*, int*); + extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); + extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); + extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); + extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); + extern void dgetrf_(int*, int*, double*, int*, int*, int*); + extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); + extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); + extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, + int*, double*, int*, double*, int*, int*); + extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); + extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*); + extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*); +} +#endif + +// Check if min/max/PI macros are defined. +// +// CImg does not compile if macros 'min', 'max' or 'PI' are defined, +// because it redefines functions min(), max() and const variable PI in the cimg:: namespace. +// so it '#undef' these macros if necessary, and restore them to reasonable +// values at the end of this file. +#ifdef min +#undef min +#define _cimg_redefine_min +#endif +#ifdef max +#undef max +#define _cimg_redefine_max +#endif +#ifdef PI +#undef PI +#define _cimg_redefine_PI +#endif + +// Define 'cimg_library' namespace suffix. +// +// You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work +// with several versions of the library at the same time. +#ifdef cimg_namespace_suffix +#define __cimg_library_suffixed(s) cimg_library_##s +#define _cimg_library_suffixed(s) __cimg_library_suffixed(s) +#define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix) +#else +#define cimg_library_suffixed cimg_library +#endif + +/*------------------------------------------------------------------------------ + # + # Define user-friendly macros. + # + # These CImg macros are prefixed by 'cimg_' and can be used safely in your own + # code. They are useful to parse command line options, or to write image loops. + # + ------------------------------------------------------------------------------*/ + +// Macros to define program usage, and retrieve command line arguments. +#define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false) +#define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0) +#define cimg_option(name,_default,usage) cimg_library_suffixed::cimg::option(name,argc,argv,_default,usage) + +// Macros to define and manipulate local neighborhoods. +#define CImg_2x2(I,T) T I[4]; \ + T& I##cc = I[0]; T& I##nc = I[1]; \ + T& I##cn = I[2]; T& I##nn = I[3]; \ + I##cc = I##nc = \ + I##cn = I##nn = 0 + +#define CImg_3x3(I,T) T I[9]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ + T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ + T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ + I##pp = I##cp = I##np = \ + I##pc = I##cc = I##nc = \ + I##pn = I##cn = I##nn = 0 + +#define CImg_4x4(I,T) T I[16]; \ + T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ + T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ + T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ + T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ + I##pp = I##cp = I##np = I##ap = \ + I##pc = I##cc = I##nc = I##ac = \ + I##pn = I##cn = I##nn = I##an = \ + I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_5x5(I,T) T I[25]; \ + T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \ + T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \ + T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \ + T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \ + T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \ + I##bb = I##pb = I##cb = I##nb = I##ab = \ + I##bp = I##pp = I##cp = I##np = I##ap = \ + I##bc = I##pc = I##cc = I##nc = I##ac = \ + I##bn = I##pn = I##cn = I##nn = I##an = \ + I##ba = I##pa = I##ca = I##na = I##aa = 0 + +#define CImg_2x2x2(I,T) T I[8]; \ + T& I##ccc = I[0]; T& I##ncc = I[1]; \ + T& I##cnc = I[2]; T& I##nnc = I[3]; \ + T& I##ccn = I[4]; T& I##ncn = I[5]; \ + T& I##cnn = I[6]; T& I##nnn = I[7]; \ + I##ccc = I##ncc = \ + I##cnc = I##nnc = \ + I##ccn = I##ncn = \ + I##cnn = I##nnn = 0 + +#define CImg_3x3x3(I,T) T I[27]; \ + T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ + T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ + T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ + T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ + T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ + T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ + T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ + T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ + T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ + I##ppp = I##cpp = I##npp = \ + I##pcp = I##ccp = I##ncp = \ + I##pnp = I##cnp = I##nnp = \ + I##ppc = I##cpc = I##npc = \ + I##pcc = I##ccc = I##ncc = \ + I##pnc = I##cnc = I##nnc = \ + I##ppn = I##cpn = I##npn = \ + I##pcn = I##ccn = I##ncn = \ + I##pnn = I##cnn = I##nnn = 0 + +#define cimg_def2x2(img,x,y) \ + int _n1##x = x<(img).width() - 1?x + 1:(img).width() - 1, \ + _n1##y = y<(img).height() - 1?y + 1:(img).height() - 1 + +#define cimg_def3x3(img,x,y) \ + cimg_def2x2(img,x,y); \ + int _p1##x = x>1?x - 1:0, \ + _p1##y = y>1?y - 1:0 + +#define cimg_def4x4(img,x,y) \ + cimg_def3x3(img,x,y); \ + int _n2##x = x<(img).width() - 2?x + 2:(img).width() - 1, \ + _n2##y = y<(img).height() - 2?y + 2:(img).height() - 1 + +#define cimg_def5x5(img,x,y) \ + cimg_def4x4(img,x,y); \ + int _p2##x = x>2?x - 2:0, \ + _p2##y = y>2?y - 2:0 + +#define cimg_def6x6(img,x,y) \ + cimg_def5x5(img,x,y); \ + int _n3##x = x<(img).width() - 3?x + 3:(img).width() - 1, \ + _n3##y = y<(img).height() - 3?y + 3:(img).height() - 1 + +#define cimg_def7x7(img,x,y) \ + cimg_def6x6(img,x,y); \ + int _p3##x = x>3?x - 3:0, \ + _p3##y = y>3?y - 3:0 + +#define cimg_def8x8(img,x,y) \ + cimg_def7x7(img,x,y); \ + int _n4##x = x<(img).width() - 4?x + 4:(img).width() - 1, \ + _n4##y = y<(img).height() - 4?y + 4:(img).height() - 1 + +#define cimg_def9x9(img,x,y) \ + cimg_def8x8(img,x,y); \ + int _p4##x = x>4?x - 4:0, \ + _p4##y = y>4?y - 4:0 + +#define cimg_def2x2x2(img,x,y,z) \ + cimg_def2x2(img,x,y); \ + int _n1##z = z<(img).depth() - 1?z + 1:(img).depth() - 1 + +#define cimg_def3x3x3(img,x,y,z) \ + cimg_def2x2x2(img,x,y,z); \ + int _p1##x = x>1?x - 1:0, \ + _p1##y = y>1?y - 1:0, \ + _p1##z = z>1?z - 1:0 + +#define cimg_get2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ + I[3] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \ + I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c) + +#define cimg_get4x4(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \ + I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \ + I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \ + I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[15] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get5x5(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ + I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \ + I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), \ + I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \ + I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_n2##y,z,c) + +#define cimg_get6x6(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \ + I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \ + I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \ + I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), \ + I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \ + I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \ + I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), \ + I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \ + I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \ + I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get7x7(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ + I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \ + I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), \ + I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \ + I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \ + I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \ + I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), \ + I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \ + I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), \ + I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \ + I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[48] = (T)(img)(_n3##x,_n3##y,z,c) + +#define cimg_get8x8(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \ + I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \ + I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \ + I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \ + I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), \ + I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), \ + I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \ + I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \ + I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \ + I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), \ + I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), \ + I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \ + I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \ + I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \ + I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), \ + I[63] = (T)(img)(_n4##x,_n4##y,z,c); + +#define cimg_get9x9(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), \ + I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \ + I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \ + I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \ + I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), \ + I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \ + I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \ + I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \ + I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), \ + I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \ + I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \ + I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \ + I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), \ + I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \ + I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \ + I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \ + I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), \ + I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \ + I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \ + I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \ + I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), \ + I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \ + I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \ + I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \ + I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), \ + I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \ + I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c) + +#define cimg_get2x2x2(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \ + I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \ + I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +#define cimg_get3x3x3(img,x,y,z,c,I,T) \ + I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \ + I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \ + I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \ + I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \ + I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \ + I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \ + I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \ + I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \ + I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \ + I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c) + +// Macros to perform various image loops. +// +// These macros are simpler to use than loops with C++ iterators. +#define cimg_for(img,ptrs,T_ptrs) \ + for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs) +#define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs) +#define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off) +#define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off) + +#define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) +#define cimg_forX(img,x) cimg_for1((img)._width,x) +#define cimg_forY(img,y) cimg_for1((img)._height,y) +#define cimg_forZ(img,z) cimg_for1((img)._depth,z) +#define cimg_forC(img,c) cimg_for1((img)._spectrum,c) +#define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) +#define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) +#define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) +#define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x) +#define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y) +#define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z) +#define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) +#define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y) +#define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z) +#define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z) +#define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z) + +#define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i) +#define cimg_rofX(img,x) cimg_rof1((img)._width,x) +#define cimg_rofY(img,y) cimg_rof1((img)._height,y) +#define cimg_rofZ(img,z) cimg_rof1((img)._depth,z) +#define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c) +#define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x) +#define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x) +#define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y) +#define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x) +#define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y) +#define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z) +#define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y) +#define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y) +#define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z) +#define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z) +#define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z) + +#define cimg_for_in1(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i) +#define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x) +#define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y) +#define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z) +#define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c) +#define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x) +#define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y) +#define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z) +#define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y) +#define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) +#define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x) +#define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y) +#define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z) +#define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c) +#define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) +#define cimg_for_insideXYZ(img,x,y,z,n) \ + cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) +#define cimg_for_insideXYZC(img,x,y,z,c,n) \ + cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) + +#define cimg_for_out1(boundi,i0,i1,i) \ + for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i) +#define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \ + for (int j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ + ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \ + for (int k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \ + ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \ + for (int l = 0; l<(int)(boundl); ++l) \ + for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \ + for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \ + for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \ + i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i)) +#define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x) +#define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y) +#define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z) +#define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c) +#define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y) +#define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z) +#define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c) +#define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z) +#define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c) +#define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c) +#define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \ + cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z) +#define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \ + cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c) +#define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \ + cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c) +#define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \ + cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c) +#define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) +#define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x) +#define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y) +#define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z) +#define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c) +#define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y) +#define cimg_for_borderXYZ(img,x,y,z,n) \ + cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z) +#define cimg_for_borderXYZC(img,x,y,z,c,n) \ + cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \ + (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c) + +#define cimg_for_spiralXY(img,x,y) \ + for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \ + --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\ + ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1) + +#define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ + for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ + _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \ + _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \ + _counter = _dx, \ + _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ + _counter>=0; \ + --_counter, x+=_steep? \ + (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ + (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) + +#define cimg_for2(bound,i) \ + for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + ++i, ++_n1##i) +#define cimg_for2X(img,x) cimg_for2((img)._width,x) +#define cimg_for2Y(img,y) cimg_for2((img)._height,y) +#define cimg_for2Z(img,z) cimg_for2((img)._depth,z) +#define cimg_for2C(img,c) cimg_for2((img)._spectrum,c) +#define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) +#define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) +#define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x) +#define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) +#define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y) +#define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z) +#define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) +#define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z) +#define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z) +#define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z) + +#define cimg_for_in2(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + ++i, ++_n1##i) +#define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x) +#define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y) +#define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z) +#define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c) +#define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x) +#define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y) +#define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z) +#define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i) +#define cimg_for3X(img,x) cimg_for3((img)._width,x) +#define cimg_for3Y(img,y) cimg_for3((img)._height,y) +#define cimg_for3Z(img,z) cimg_for3((img)._depth,z) +#define cimg_for3C(img,c) cimg_for3((img)._spectrum,c) +#define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) +#define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) +#define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x) +#define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) +#define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y) +#define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z) +#define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) +#define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z) +#define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z) +#define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z) + +#define cimg_for_in3(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \ + i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ + _p1##i = i++, ++_n1##i) +#define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x) +#define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y) +#define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z) +#define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c) +#define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x) +#define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y) +#define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z) +#define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for4(bound,i) \ + for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for4X(img,x) cimg_for4((img)._width,x) +#define cimg_for4Y(img,y) cimg_for4((img)._height,y) +#define cimg_for4Z(img,z) cimg_for4((img)._depth,z) +#define cimg_for4C(img,c) cimg_for4((img)._spectrum,c) +#define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) +#define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) +#define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x) +#define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) +#define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y) +#define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z) +#define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) +#define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z) +#define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z) +#define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z) + +#define cimg_for_in4(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x) +#define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y) +#define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z) +#define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c) +#define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x) +#define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y) +#define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z) +#define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for5(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2; \ + _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for5X(img,x) cimg_for5((img)._width,x) +#define cimg_for5Y(img,y) cimg_for5((img)._height,y) +#define cimg_for5Z(img,z) cimg_for5((img)._depth,z) +#define cimg_for5C(img,c) cimg_for5((img)._spectrum,c) +#define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) +#define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) +#define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x) +#define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) +#define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y) +#define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z) +#define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) +#define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z) +#define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z) +#define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z) + +#define cimg_for_in5(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \ + i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) +#define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x) +#define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y) +#define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z) +#define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c) +#define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x) +#define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y) +#define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z) +#define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for6(bound,i) \ + for (int i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for6X(img,x) cimg_for6((img)._width,x) +#define cimg_for6Y(img,y) cimg_for6((img)._height,y) +#define cimg_for6Z(img,z) cimg_for6((img)._depth,z) +#define cimg_for6C(img,c) cimg_for6((img)._spectrum,c) +#define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) +#define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) +#define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x) +#define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) +#define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y) +#define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z) +#define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) +#define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z) +#define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z) +#define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z) + +#define cimg_for_in6(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ + i<=(int)(i1) && \ + (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x) +#define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y) +#define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z) +#define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c) +#define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x) +#define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y) +#define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z) +#define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for7(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3; \ + _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for7X(img,x) cimg_for7((img)._width,x) +#define cimg_for7Y(img,y) cimg_for7((img)._height,y) +#define cimg_for7Z(img,z) cimg_for7((img)._depth,z) +#define cimg_for7C(img,c) cimg_for7((img)._spectrum,c) +#define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) +#define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) +#define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x) +#define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) +#define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y) +#define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z) +#define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) +#define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z) +#define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z) +#define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z) + +#define cimg_for_in7(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \ + i<=(int)(i1) && \ + (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) +#define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x) +#define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y) +#define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z) +#define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c) +#define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x) +#define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y) +#define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z) +#define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for8(bound,i) \ + for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(bound)?(int)(bound) - 1:3, \ + _n4##i = 4>=(bound)?(int)(bound) - 1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for8X(img,x) cimg_for8((img)._width,x) +#define cimg_for8Y(img,y) cimg_for8((img)._height,y) +#define cimg_for8Z(img,z) cimg_for8((img)._depth,z) +#define cimg_for8C(img,c) cimg_for8((img)._spectrum,c) +#define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x) +#define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x) +#define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x) +#define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y) +#define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y) +#define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z) +#define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y) +#define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z) +#define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z) +#define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z) + +#define cimg_for_in8(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ + _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x) +#define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y) +#define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z) +#define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c) +#define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x) +#define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y) +#define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z) +#define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for9(bound,i) \ + for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ + _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \ + _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \ + _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \ + _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \ + _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for9X(img,x) cimg_for9((img)._width,x) +#define cimg_for9Y(img,y) cimg_for9((img)._height,y) +#define cimg_for9Z(img,z) cimg_for9((img)._depth,z) +#define cimg_for9C(img,c) cimg_for9((img)._spectrum,c) +#define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x) +#define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x) +#define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x) +#define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y) +#define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y) +#define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z) +#define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y) +#define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z) +#define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z) +#define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z) + +#define cimg_for_in9(bound,i0,i1,i) \ + for (int i = (int)(i0)<0?0:(int)(i0), \ + _p4##i = i - 4<0?0:i - 4, \ + _p3##i = i - 3<0?0:i - 3, \ + _p2##i = i - 2<0?0:i - 2, \ + _p1##i = i - 1<0?0:i - 1, \ + _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \ + _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \ + _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \ + _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \ + i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \ + i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \ + _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i) +#define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x) +#define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y) +#define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z) +#define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c) +#define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x) +#define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y) +#define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z) +#define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y) +#define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z) +#define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) +#define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \ + cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) + +#define cimg_for2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], \ + I[2] = I[3], \ + ++x, ++_n1##x) + +#define cimg_for3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[4] = (T)(img)(x,y,z,c)), \ + (I[7] = (T)(img)(x,_n1##y,z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for4x4(img,x,y,z,c,I,T) \ + cimg_for4((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = I[5] = (T)(img)(0,y,z,c)), \ + (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width() - 1:2); \ + (_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[4] = (T)(img)(_p1##x,y,z,c)), \ + (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(x,_p1##y,z,c)), \ + (I[5] = (T)(img)(x,y,z,c)), \ + (I[9] = (T)(img)(x,_n1##y,z,c)), \ + (I[13] = (T)(img)(x,_n2##y,z,c)), \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[6] = (T)(img)(_n1##x,y,z,c)), \ + (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[7] = (T)(img)(_n2##x,y,z,c)), \ + (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], \ + I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for5x5(img,x,y,z,c,I,T) \ + cimg_for5((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \ + (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \ + (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + 2>=(img)._width?(img).width() - 1:2); \ + (_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[10] = (T)(img)(_p2##x,y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[11] = (T)(img)(_p1##x,y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[7] = (T)(img)(x,_p1##y,z,c)), \ + (I[12] = (T)(img)(x,y,z,c)), \ + (I[17] = (T)(img)(x,_n1##y,z,c)), \ + (I[22] = (T)(img)(x,_n2##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_n1##x,y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \ + x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \ + x<=(int)(x1) && ((_n2##x<(img).width() && ( \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n2##x,y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \ + _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ + I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ + I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ + I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ + I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) + +#define cimg_for6x6(img,x,y,z,c,I,T) \ + cimg_for6((img)._height,y) for (int x = 0, \ + _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = 2>=(img)._width?(img).width() - 1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \ + (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \ + (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \ + (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width() - 1:3); \ + (_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p2##x,y,z,c)), \ + (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[13] = (T)(img)(_p1##x,y,z,c)), \ + (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(x,_p2##y,z,c)), \ + (I[8] = (T)(img)(x,_p1##y,z,c)), \ + (I[14] = (T)(img)(x,y,z,c)), \ + (I[20] = (T)(img)(x,_n1##y,z,c)), \ + (I[26] = (T)(img)(x,_n2##y,z,c)), \ + (I[32] = (T)(img)(x,_n3##y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[15] = (T)(img)(_n1##x,y,z,c)), \ + (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[16] = (T)(img)(_n2##x,y,z,c)), \ + (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[17] = (T)(img)(_n3##x,y,z,c)), \ + (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ + I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ + I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ + _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for7x7(img,x,y,z,c,I,T) \ + cimg_for7((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=(img)._width?(img).width() - 1:1, \ + _n2##x = 2>=(img)._width?(img).width() - 1:2, \ + _n3##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \ + (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \ + (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \ + (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \ + (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \ + (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + 3>=(img)._width?(img).width() - 1:3); \ + (_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \ + _n3##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[21] = (T)(img)(_p3##x,y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[22] = (T)(img)(_p2##x,y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[23] = (T)(img)(_p1##x,y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[10] = (T)(img)(x,_p2##y,z,c)), \ + (I[17] = (T)(img)(x,_p1##y,z,c)), \ + (I[24] = (T)(img)(x,y,z,c)), \ + (I[31] = (T)(img)(x,_n1##y,z,c)), \ + (I[38] = (T)(img)(x,_n2##y,z,c)), \ + (I[45] = (T)(img)(x,_n3##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_n1##x,y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_n2##x,y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \ + x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \ + x<=(int)(x1) && ((_n3##x<(img).width() && ( \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[27] = (T)(img)(_n3##x,y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \ + _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ + I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ + I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ + I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ + I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ + I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ + I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) + +#define cimg_for8x8(img,x,y,z,c,I,T) \ + cimg_for8((img)._height,y) for (int x = 0, \ + _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ + _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ + _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \ + (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \ + (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \ + (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \ + (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \ + (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \ + (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width() - 1:4); \ + (_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ + _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[24] = (T)(img)(_p3##x,y,z,c)), \ + (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[25] = (T)(img)(_p2##x,y,z,c)), \ + (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[26] = (T)(img)(_p1##x,y,z,c)), \ + (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(x,_p3##y,z,c)), \ + (I[11] = (T)(img)(x,_p2##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,z,c)), \ + (I[27] = (T)(img)(x,y,z,c)), \ + (I[35] = (T)(img)(x,_n1##y,z,c)), \ + (I[43] = (T)(img)(x,_n2##y,z,c)), \ + (I[51] = (T)(img)(x,_n3##y,z,c)), \ + (I[59] = (T)(img)(x,_n4##y,z,c)), \ + (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[28] = (T)(img)(_n1##x,y,z,c)), \ + (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[29] = (T)(img)(_n2##x,y,z,c)), \ + (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[30] = (T)(img)(_n3##x,y,z,c)), \ + (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x + 4>=(img).width()?(img).width() - 1:x + 4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[31] = (T)(img)(_n4##x,y,z,c)), \ + (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \ + I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \ + I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ + I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \ + _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for9x9(img,x,y,z,c,I,T) \ + cimg_for9((img)._height,y) for (int x = 0, \ + _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \ + _n1##x = 1>=((img)._width)?(img).width() - 1:1, \ + _n2##x = 2>=((img)._width)?(img).width() - 1:2, \ + _n3##x = 3>=((img)._width)?(img).width() - 1:3, \ + _n4##x = (int)( \ + (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \ + (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \ + (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \ + (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \ + (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \ + (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \ + (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \ + (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + 4>=((img)._width)?(img).width() - 1:4); \ + (_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ + I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ + I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ + I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \ + cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p4##x = x - 4<0?0:x - 4, \ + _p3##x = x - 3<0?0:x - 3, \ + _p2##x = x - 2<0?0:x - 2, \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \ + _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \ + _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \ + _n4##x = (int)( \ + (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \ + (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \ + (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \ + (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \ + (I[36] = (T)(img)(_p4##x,y,z,c)), \ + (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \ + (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \ + (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \ + (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \ + (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \ + (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \ + (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \ + (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \ + (I[37] = (T)(img)(_p3##x,y,z,c)), \ + (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \ + (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \ + (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \ + (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \ + (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \ + (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \ + (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \ + (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \ + (I[38] = (T)(img)(_p2##x,y,z,c)), \ + (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \ + (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \ + (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \ + (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \ + (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \ + (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \ + (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[39] = (T)(img)(_p1##x,y,z,c)), \ + (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \ + (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \ + (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \ + (I[4] = (T)(img)(x,_p4##y,z,c)), \ + (I[13] = (T)(img)(x,_p3##y,z,c)), \ + (I[22] = (T)(img)(x,_p2##y,z,c)), \ + (I[31] = (T)(img)(x,_p1##y,z,c)), \ + (I[40] = (T)(img)(x,y,z,c)), \ + (I[49] = (T)(img)(x,_n1##y,z,c)), \ + (I[58] = (T)(img)(x,_n2##y,z,c)), \ + (I[67] = (T)(img)(x,_n3##y,z,c)), \ + (I[76] = (T)(img)(x,_n4##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \ + (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \ + (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[41] = (T)(img)(_n1##x,y,z,c)), \ + (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \ + (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \ + (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \ + (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \ + (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \ + (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \ + (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \ + (I[42] = (T)(img)(_n2##x,y,z,c)), \ + (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \ + (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \ + (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \ + (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \ + (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \ + (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \ + (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \ + (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \ + (I[43] = (T)(img)(_n3##x,y,z,c)), \ + (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \ + (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \ + (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \ + (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \ + x + 4>=(img).width()?(img).width() - 1:x + 4); \ + x<=(int)(x1) && ((_n4##x<(img).width() && ( \ + (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \ + (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \ + (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \ + (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \ + (I[44] = (T)(img)(_n4##x,y,z,c)), \ + (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \ + (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \ + (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \ + (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \ + _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \ + I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \ + I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ + I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \ + I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \ + I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ + I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \ + I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \ + I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \ + I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \ + I[79] = I[80], \ + _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x) + +#define cimg_for2x2x2(img,x,y,z,c,I,T) \ + cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(0,y,z,c)), \ + (I[2] = (T)(img)(0,_n1##y,z,c)), \ + (I[4] = (T)(img)(0,y,_n1##z,c)), \ + (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ + cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _n1##x = (int)( \ + (I[0] = (T)(img)(x,y,z,c)), \ + (I[2] = (T)(img)(x,_n1##y,z,c)), \ + (I[4] = (T)(img)(x,y,_n1##z,c)), \ + (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[1] = (T)(img)(_n1##x,y,z,c)), \ + (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ + ++x, ++_n1##x) + +#define cimg_for3x3x3(img,x,y,z,c,I,T) \ + cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \ + (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \ + (I[12] = I[13] = (T)(img)(0,y,z,c)), \ + (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \ + (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \ + (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \ + (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \ + cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ + _p1##x = x - 1<0?0:x - 1, \ + _n1##x = (int)( \ + (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \ + (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \ + (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \ + (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[12] = (T)(img)(_p1##x,y,z,c)), \ + (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \ + (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \ + (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \ + (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \ + (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \ + (I[4] = (T)(img)(x,y,_p1##z,c)), \ + (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \ + (I[10] = (T)(img)(x,_p1##y,z,c)), \ + (I[13] = (T)(img)(x,y,z,c)), \ + (I[16] = (T)(img)(x,_n1##y,z,c)), \ + (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \ + (I[22] = (T)(img)(x,y,_n1##z,c)), \ + (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \ + x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \ + x<=(int)(x1) && ((_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \ + (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \ + (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[14] = (T)(img)(_n1##x,y,z,c)), \ + (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \ + (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \ + (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \ + (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \ + x==--_n1##x); \ + I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ + I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ + I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ + _p1##x = x++, ++_n1##x) + +#define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l) +#define cimglist_rof(list,l) for (int l = (int)(list)._width - 1; l>=0; --l) +#define cimglist_for_in(list,l0,l1,l) \ + for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \ + l<=_max##l; ++l) + +#define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn + +// Macros used to display error messages when exceptions are thrown. +// You should not use these macros is your own code. +#define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::" +#define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']' +#define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::" +#define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type() +#define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::" +#define cimglist_instance _width,_allocated_width,_data,pixel_type() + +/*------------------------------------------------ + # + # + # Define cimg_library:: namespace + # + # + -------------------------------------------------*/ +//! Contains all classes and functions of the \CImg library. +/** + This namespace is defined to avoid functions and class names collisions + that could happen with the inclusion of other C++ header files. + Anyway, it should not happen often and you should reasonably start most of your + \CImg-based programs with + \code + #include "CImg.h" + using namespace cimg_library; + \endcode + to simplify the declaration of \CImg Library objects afterwards. +**/ +namespace cimg_library_suffixed { + + // Declare the four classes of the CImg Library. + template struct CImg; + template struct CImgList; + struct CImgDisplay; + struct CImgException; + + // Declare cimg:: namespace. + // This is an incomplete namespace definition here. It only contains some + // necessary stuff to ensure a correct declaration order of the classes and functions + // defined afterwards. + namespace cimg { + + // Define character sequences for colored terminal output. +#ifdef cimg_use_vt100 + static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 }; + static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 }; + static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 }; + static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 }; + static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 }; + static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 }; + static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 }; + static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 }; + static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 }; + static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 }; + static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 }; +#else + static const char t_normal[] = { 0 }; + static const char *const t_black = cimg::t_normal, + *const t_red = cimg::t_normal, + *const t_green = cimg::t_normal, + *const t_yellow = cimg::t_normal, + *const t_blue = cimg::t_normal, + *const t_magenta = cimg::t_normal, + *const t_cyan = cimg::t_normal, + *const t_white = cimg::t_normal, + *const t_bold = cimg::t_normal, + *const t_underscore = cimg::t_normal; +#endif + + inline std::FILE* output(std::FILE *file=0); + inline void info(); + + //! Avoid warning messages due to unused parameters. Do nothing actually. + template + inline void unused(const T&, ...) {} + + // [internal] Lock/unlock a mutex for managing concurrent threads. + // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }. + // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg. + inline int mutex(const unsigned int n, const int lock_mode=1); + + inline unsigned int& exception_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = cimg_verbosity; + if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); } + return mode; + } + + // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. + inline FILE* _stdin(const bool throw_exception=true); + inline FILE* _stdout(const bool throw_exception=true); + inline FILE* _stderr(const bool throw_exception=true); + + // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character + // at the end of the string. +#if cimg_OS==2 && defined(_MSC_VER) + inline int _snprintf(char *const s, const size_t size, const char *const format, ...) { + va_list ap; + va_start(ap,format); + const int result = _vsnprintf(s,size,format,ap); + va_end(ap); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) { + int result = -1; + cimg::mutex(6); + if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap); + if (result==-1) result = _vscprintf(format,ap); + cimg::mutex(6,0); + return result; + } + + // Mutex-protected version of sscanf, sprintf and snprintf. + // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX. +#elif defined(__MACOSX__) || defined(__APPLE__) + inline int _sscanf(const char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsscanf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _sprintf(char *const s, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsprintf(s,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _snprintf(char *const s, const size_t n, const char *const format, ...) { + cimg::mutex(6); + va_list args; + va_start(args,format); + const int result = std::vsnprintf(s,n,format,args); + va_end(args); + cimg::mutex(6,0); + return result; + } + + inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) { + cimg::mutex(6); + const int result = std::vsnprintf(s,size,format,ap); + cimg::mutex(6,0); + return result; + } +#endif + + //! Set current \CImg exception mode. + /** + The way error messages are handled by \CImg can be changed dynamically, using this function. + \param mode Desired exception mode. Possible values are: + - \c 0: Hide library messages (quiet mode). + - \c 1: Print library messages on the console. + - \c 2: Display library messages on a dialog window. + - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!). + - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!). + **/ + inline unsigned int& exception_mode(const unsigned int mode) { + return exception_mode(mode,true); + } + + //! Return current \CImg exception mode. + /** + \note By default, return the value of configuration macro \c cimg_verbosity + **/ + inline unsigned int& exception_mode() { + return exception_mode(0,false); + } + + inline unsigned int openmp_mode(const unsigned int value, const bool is_set) { + static unsigned int mode = 2; + if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); } + return mode; + } + + //! Set current \CImg openmp mode. + /** + The way openmp-based methods are handled by \CImg can be changed dynamically, using this function. + \param mode Desired openmp mode. Possible values are: + - \c 0: Never parallelize. + - \c 1: Always parallelize. + - \c 2: Adaptive parallelization mode (default behavior). + **/ + inline unsigned int openmp_mode(const unsigned int mode) { + return openmp_mode(mode,true); + } + + //! Return current \CImg openmp mode. + inline unsigned int openmp_mode() { + return openmp_mode(0,false); + } + +#ifndef cimg_openmp_sizefactor +#define cimg_openmp_sizefactor 1 +#endif +#define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond)))) +#define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size)) +#ifdef _MSC_VER +// Disable 'collapse()' directive for MSVC (supports only OpenMP 2.0). +#define cimg_openmp_collapse(k) +#else +#define cimg_openmp_collapse(k) collapse(k) +#endif + +#if cimg_OS==2 +// Disable parallelization of simple loops on Windows, due to noticed performance drop. +#define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr); +#else +#define cimg_openmp_for(instance,expr,min_size) \ + cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \ + cimg_rof((instance),ptr,T) *ptr = (T)(expr); +#endif + + // Display a simple dialog box, and wait for the user's response. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label="OK", const char *const button2_label=0, + const char *const button3_label=0, const char *const button4_label=0, + const char *const button5_label=0, const char *const button6_label=0, + const bool centering=false); + + // Evaluate math expression. + inline double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0); + + } // namespace cimg { ... + + /*--------------------------------------- + # + # Define the CImgException structures + # + --------------------------------------*/ + //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call. + /** + \par Overview + + CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException). + CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead. + These classes can be: + + - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal. + This is the only \c non-derived exception class. + + - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid. + This is probably one of the most thrown exception by \CImg. + For instance, the following example throws a \c CImgArgumentException: + \code + CImg img(100,100,1,3); // Define a 100x100 color image with float-valued pixels + img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis + \endcode + + - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances. + + - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit + the function requirements. For instance, the following example throws a \c CImgInstanceException: + \code + const CImg img; // Define an empty image + const float value = img.at(0); // Try to read first pixel value (does not exist) + \endcode + + - \b CImgIOException: Thrown when an error occurred when trying to load or save image files. + This happens when trying to read files that do not exist or with invalid formats. + For instance, the following example throws a \c CImgIOException: + \code + const CImg img("missing_file.jpg"); // Try to load a file that does not exist + \endcode + + - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and + when a \CImg function has to display a warning message (see cimg::warn()). + + It is not recommended to throw CImgException instances by yourself, + since they are expected to be thrown only by \CImg. + When an error occurs in a library function call, \CImg may display error messages on the screen or on the + standard output, depending on the current \CImg exception mode. + The \CImg exception mode can be get and set by functions cimg::exception_mode() and + cimg::exception_mode(unsigned int). + + \par Exceptions handling + + In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown. + This may lead the program to break (this is the default behavior), but you can bypass this behavior by + handling the exceptions by yourself, + using a usual try { ... } catch () { ... } bloc, as in the following example: + \code + #define "CImg.h" + using namespace cimg_library; + int main() { + cimg::exception_mode(0); // Enable quiet exception mode + try { + ... // Here, do what you want to stress CImg + } catch (CImgException& e) { // You succeeded: something went wrong! + std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message + ... // Do what you want now to save the ship! + } + } + \endcode + **/ + struct CImgException : public std::exception { +#define _cimg_exception_err(etype,disp_flag) \ + std::va_list ap, ap2; \ + va_start(ap,format); va_start(ap2,format); \ + int size = cimg_vsnprintf(0,0,format,ap2); \ + if (size++>=0) { \ + delete[] _message; \ + _message = new char[(size_t)size]; \ + cimg_vsnprintf(_message,(size_t)size,format,ap); \ + if (cimg::exception_mode()) { \ + std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \ + if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \ + catch (CImgException&) {} \ + if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \ + } \ + } \ + va_end(ap); va_end(ap2); + + char *_message; + CImgException() { _message = new char[1]; *_message = 0; } + CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); } + CImgException(const CImgException& e):std::exception(e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgException() throw() { delete[] _message; } + CImgException& operator=(const CImgException& e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; // struct CImgException { ... + + // The CImgAbortException class is used to throw an exception when + // a computationally-intensive function has been aborted by an external signal. + struct CImgAbortException : public std::exception { + char *_message; + CImgAbortException() { _message = new char[1]; *_message = 0; } + CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); } + CImgAbortException(const CImgAbortException& e):std::exception(e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + } + ~CImgAbortException() throw() { delete[] _message; } + CImgAbortException& operator=(const CImgAbortException& e) { + const size_t size = std::strlen(e._message); + _message = new char[size + 1]; + std::strncpy(_message,e._message,size); + _message[size] = 0; + return *this; + } + //! Return a C-string containing the error message associated to the thrown exception. + const char *what() const throw() { return _message; } + }; // struct CImgAbortException { ... + + // The CImgArgumentException class is used to throw an exception related + // to invalid arguments encountered in a library function call. + struct CImgArgumentException : public CImgException { + CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); } + }; // struct CImgArgumentException { ... + + // The CImgDisplayException class is used to throw an exception related + // to display problems encountered in a library function call. + struct CImgDisplayException : public CImgException { + CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); } + }; // struct CImgDisplayException { ... + + // The CImgInstanceException class is used to throw an exception related + // to an invalid instance encountered in a library function call. + struct CImgInstanceException : public CImgException { + CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); } + }; // struct CImgInstanceException { ... + + // The CImgIOException class is used to throw an exception related + // to input/output file problems encountered in a library function call. + struct CImgIOException : public CImgException { + CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); } + }; // struct CImgIOException { ... + + // The CImgWarningException class is used to throw an exception for warnings + // encountered in a library function call. + struct CImgWarningException : public CImgException { + CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); } + }; // struct CImgWarningException { ... + + /*------------------------------------- + # + # Define cimg:: namespace + # + -----------------------------------*/ + //! Contains \a low-level functions and variables of the \CImg Library. + /** + Most of the functions and variables within this namespace are used by the \CImg library for low-level operations. + You may use them to access specific const values or environment variables internally used by \CImg. + \warning Never write using namespace cimg_library::cimg; in your source code. Lot of functions in the + cimg:: namespace have the same names as standard C functions that may be defined in the global + namespace ::. + **/ + namespace cimg { + + // Define traits that will be used to determine the best data type to work in CImg functions. + // + template struct type { + static const char* string() { + static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", + "unknown32", "unknown40", "unknown48", "unknown56", + "unknown64", "unknown72", "unknown80", "unknown88", + "unknown96", "unknown104", "unknown112", "unknown120", + "unknown128" }; + return s[(sizeof(T)<17)?sizeof(T):0]; + } + static bool is_float() { return false; } + static bool is_inf(const T) { return false; } + static bool is_nan(const T) { return false; } + static bool is_finite(const T) { return true; } + static T min() { return ~max(); } + static T max() { return (T)1<<(8*sizeof(T) - 1); } + static T inf() { return max(); } + static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; } + static const char* format() { return "%s"; } + static const char* format_s() { return "%s"; } + static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "bool"; return s; } + static bool is_float() { return false; } + static bool is_inf(const bool) { return false; } + static bool is_nan(const bool) { return false; } + static bool is_finite(const bool) { return true; } + static bool min() { return false; } + static bool max() { return true; } + static bool inf() { return max(); } + static bool is_inf() { return false; } + static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; } + static const char* format() { return "%s"; } + static const char* format_s() { return "%s"; } + static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned char) { return false; } + static bool is_nan(const unsigned char) { return false; } + static bool is_finite(const unsigned char) { return true; } + static unsigned char min() { return 0; } + static unsigned char max() { return (unsigned char)-1; } + static unsigned char inf() { return max(); } + static unsigned char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned char val) { return (unsigned int)val; } + }; + +#if defined(CHAR_MAX) && CHAR_MAX==255 + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static bool is_finite(const char) { return true; } + static char min() { return 0; } + static char max() { return (char)-1; } + static char inf() { return max(); } + static char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const char val) { return (unsigned int)val; } + }; +#else + template<> struct type { + static const char* string() { static const char *const s = "char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const char) { return false; } + static bool is_nan(const char) { return false; } + static bool is_finite(const char) { return true; } + static char min() { return ~max(); } + static char max() { return (char)((unsigned char)-1>>1); } + static char inf() { return max(); } + static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const char val) { return (int)val; } + }; +#endif + + template<> struct type { + static const char* string() { static const char *const s = "signed char"; return s; } + static bool is_float() { return false; } + static bool is_inf(const signed char) { return false; } + static bool is_nan(const signed char) { return false; } + static bool is_finite(const signed char) { return true; } + static signed char min() { return ~max(); } + static signed char max() { return (signed char)((unsigned char)-1>>1); } + static signed char inf() { return max(); } + static signed char cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(signed char)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const signed char val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned short) { return false; } + static bool is_nan(const unsigned short) { return false; } + static bool is_finite(const unsigned short) { return true; } + static unsigned short min() { return 0; } + static unsigned short max() { return (unsigned short)-1; } + static unsigned short inf() { return max(); } + static unsigned short cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned short val) { return (unsigned int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "short"; return s; } + static bool is_float() { return false; } + static bool is_inf(const short) { return false; } + static bool is_nan(const short) { return false; } + static bool is_finite(const short) { return true; } + static short min() { return ~max(); } + static short max() { return (short)((unsigned short)-1>>1); } + static short inf() { return max(); } + static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const short val) { return (int)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const unsigned int) { return false; } + static bool is_nan(const unsigned int) { return false; } + static bool is_finite(const unsigned int) { return true; } + static unsigned int min() { return 0; } + static unsigned int max() { return (unsigned int)-1; } + static unsigned int inf() { return max(); } + static unsigned int cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; } + static const char* format() { return "%u"; } + static const char* format_s() { return "%u"; } + static unsigned int format(const unsigned int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int"; return s; } + static bool is_float() { return false; } + static bool is_inf(const int) { return false; } + static bool is_nan(const int) { return false; } + static bool is_finite(const int) { return true; } + static int min() { return ~max(); } + static int max() { return (int)((unsigned int)-1>>1); } + static int inf() { return max(); } + static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; } + static const char* format() { return "%d"; } + static const char* format_s() { return "%d"; } + static int format(const int val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "unsigned int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_uint64) { return false; } + static bool is_nan(const cimg_uint64) { return false; } + static bool is_finite(const cimg_uint64) { return true; } + static cimg_uint64 min() { return 0; } + static cimg_uint64 max() { return (cimg_uint64)-1; } + static cimg_uint64 inf() { return max(); } + static cimg_uint64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; } + static const char* format() { return cimg_fuint64; } + static const char* format_s() { return cimg_fuint64; } + static cimg_uint64 format(const cimg_uint64 val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "int64"; return s; } + static bool is_float() { return false; } + static bool is_inf(const cimg_int64) { return false; } + static bool is_nan(const cimg_int64) { return false; } + static bool is_finite(const cimg_int64) { return true; } + static cimg_int64 min() { return ~max(); } + static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); } + static cimg_int64 inf() { return max(); } + static cimg_int64 cut(const double val) { + return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val; + } + static const char* format() { return cimg_fint64; } + static const char* format_s() { return cimg_fint64; } + static long format(const long val) { return (long)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const double val) { // Custom version that works with '-ffast-math' + if (sizeof(double)==8) { + cimg_uint64 u; + std::memcpy(&u,&val,sizeof(double)); + return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000; + } +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const double val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static double min() { return -DBL_MAX; } + static double max() { return DBL_MAX; } + static double inf() { +#ifdef INFINITY + return (double)INFINITY; +#else + return max()*max(); +#endif + } + static double nan() { +#ifdef NAN + return (double)NAN; +#else + const double val_nan = -std::sqrt(-1.); return val_nan; +#endif + } + static double cut(const double val) { return val; } + static const char* format() { return "%.17g"; } + static const char* format_s() { return "%g"; } + static double format(const double val) { return val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "float"; return s; } + static bool is_float() { return true; } + static bool is_inf(const float val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const float val) { // Custom version that works with '-ffast-math' + if (sizeof(float)==4) { + unsigned int u; + std::memcpy(&u,&val,sizeof(float)); + return (u&0x7fffffff)>0x7f800000; + } +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const float val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static float min() { return -FLT_MAX; } + static float max() { return FLT_MAX; } + static float inf() { return (float)cimg::type::inf(); } + static float nan() { return (float)cimg::type::nan(); } + static float cut(const double val) { return (float)val; } + static float cut(const float val) { return (float)val; } + static const char* format() { return "%.9g"; } + static const char* format_s() { return "%g"; } + static double format(const float val) { return (double)val; } + }; + + template<> struct type { + static const char* string() { static const char *const s = "long double"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const long double val) { +#ifdef isnan + return (bool)isnan(val); +#else + return !(val==val); +#endif + } + static bool is_finite(const long double val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static long double min() { return -LDBL_MAX; } + static long double max() { return LDBL_MAX; } + static long double inf() { return max()*max(); } + static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; } + static long double cut(const long double val) { return val; } + static const char* format() { return "%.17g"; } + static const char* format_s() { return "%g"; } + static double format(const long double val) { return (double)val; } + }; + +#ifdef cimg_use_half + template<> struct type { + static const char* string() { static const char *const s = "half"; return s; } + static bool is_float() { return true; } + static bool is_inf(const long double val) { +#ifdef isinf + return (bool)isinf(val); +#else + return !is_nan(val) && (val::min() || val>cimg::type::max()); +#endif + } + static bool is_nan(const half val) { // Custom version that works with '-ffast-math' + if (sizeof(half)==2) { + short u; + std::memcpy(&u,&val,sizeof(short)); + return (bool)((u&0x7fff)>0x7c00); + } + return cimg::type::is_nan((float)val); + } + static bool is_finite(const half val) { +#ifdef isfinite + return (bool)isfinite(val); +#else + return !is_nan(val) && !is_inf(val); +#endif + } + static half min() { return (half)-65504; } + static half max() { return (half)65504; } + static half inf() { return max()*max(); } + static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; } + static half cut(const double val) { return (half)val; } + static const char* format() { return "%.9g"; } + static const char* format_s() { return "%g"; } + static double format(const half val) { return (double)val; } + }; +#endif + + template struct superset { typedef T type; }; + template<> struct superset { typedef unsigned char type; }; + template<> struct superset { typedef char type; }; + template<> struct superset { typedef signed char type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef short type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef unsigned int type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef int type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_uint64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef cimg_int64 type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + template<> struct superset { typedef double type; }; + +#ifdef cimg_use_half + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef float type; }; + template<> struct superset { typedef double type; }; +#endif + + template struct superset2 { + typedef typename superset::type>::type type; + }; + + template struct superset3 { + typedef typename superset::type>::type type; + }; + + template struct last { typedef t2 type; }; + +#define _cimg_Tt typename cimg::superset::type +#define _cimg_Tfloat typename cimg::superset::type +#define _cimg_tfloat typename cimg::superset::type +#define _cimg_Ttfloat typename cimg::superset2::type +#define _cimg_Ttdouble typename cimg::superset2::type + + // Define variables used internally by CImg. +#if cimg_display==1 + struct X11_static { + unsigned int nb_wins; + pthread_t *events_thread; + pthread_cond_t wait_event; + pthread_mutex_t wait_event_mutex; + CImgDisplay **wins; + Display *display; + unsigned int nb_bits; + bool is_blue_first; + bool is_shm_enabled; + bool byte_order; + +#ifdef cimg_use_xrandr + XRRScreenSize *resolutions; + Rotation curr_rotation; + unsigned int curr_resolution; + unsigned int nb_resolutions; +#endif + X11_static():nb_wins(0),events_thread(0),display(0), + nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) { +#ifdef __FreeBSD__ + XInitThreads(); +#endif + wins = new CImgDisplay*[1024]; + pthread_mutex_init(&wait_event_mutex,0); + pthread_cond_init(&wait_event,0); + +#ifdef cimg_use_xrandr + resolutions = 0; + curr_rotation = 0; + curr_resolution = nb_resolutions = 0; +#endif + } + + ~X11_static() { + delete[] wins; + /* + if (events_thread) { + pthread_cancel(*events_thread); + delete events_thread; + } + if (display) { } // XCloseDisplay(display); } + pthread_cond_destroy(&wait_event); + pthread_mutex_unlock(&wait_event_mutex); + pthread_mutex_destroy(&wait_event_mutex); + */ + } + }; // struct X11_static { ... +#if defined(cimg_module) + X11_static& X11_attr(); +#elif defined(cimg_main) + X11_static& X11_attr() { static X11_static val; return val; } +#else + inline X11_static& X11_attr() { static X11_static val; return val; } +#endif + +#elif cimg_display==2 + struct Win32_static { + HANDLE wait_event; + Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); } + }; // struct Win32_static { ... +#if defined(cimg_module) + Win32_static& Win32_attr(); +#elif defined(cimg_main) + Win32_static& Win32_attr() { static Win32_static val; return val; } +#else + inline Win32_static& Win32_attr() { static Win32_static val; return val; } +#endif +#endif +#define cimg_lock_display() cimg::mutex(15) +#define cimg_unlock_display() cimg::mutex(15,0) + + struct Mutex_static { +#if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1) + pthread_mutex_t mutex[32]; + Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); } + void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); } + void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); } + int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); } +#elif cimg_OS==2 + HANDLE mutex[32]; + Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); } + void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); } + void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); } + int trylock(const unsigned int) { return 0; } +#else + Mutex_static() {} + void lock(const unsigned int) {} + void unlock(const unsigned int) {} + int trylock(const unsigned int) { return 0; } +#endif + }; // struct Mutex_static { ... +#if defined(cimg_module) + Mutex_static& Mutex_attr(); +#elif defined(cimg_main) + Mutex_static& Mutex_attr() { static Mutex_static val; return val; } +#else + inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; } +#endif + +#if defined(cimg_use_magick) + struct Magick_static { + Magick_static() { + Magick::InitializeMagick(""); + } + }; // struct Magick_static { ... + static Magick_static _Magick_static; +#endif + +#if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread) + struct FFTW3_static { + FFTW3_static() { + fftw_init_threads(); + } + }; // struct FFTW3_static { ... + static FFTW3_static _FFTW3_static; +#endif + +#if cimg_display==1 + // Define keycodes for X11-based graphical systems. + const unsigned int keyESC = XK_Escape; + const unsigned int keyF1 = XK_F1; + const unsigned int keyF2 = XK_F2; + const unsigned int keyF3 = XK_F3; + const unsigned int keyF4 = XK_F4; + const unsigned int keyF5 = XK_F5; + const unsigned int keyF6 = XK_F6; + const unsigned int keyF7 = XK_F7; + const unsigned int keyF8 = XK_F8; + const unsigned int keyF9 = XK_F9; + const unsigned int keyF10 = XK_F10; + const unsigned int keyF11 = XK_F11; + const unsigned int keyF12 = XK_F12; + const unsigned int keyPAUSE = XK_Pause; + const unsigned int key1 = XK_1; + const unsigned int key2 = XK_2; + const unsigned int key3 = XK_3; + const unsigned int key4 = XK_4; + const unsigned int key5 = XK_5; + const unsigned int key6 = XK_6; + const unsigned int key7 = XK_7; + const unsigned int key8 = XK_8; + const unsigned int key9 = XK_9; + const unsigned int key0 = XK_0; + const unsigned int keyBACKSPACE = XK_BackSpace; + const unsigned int keyINSERT = XK_Insert; + const unsigned int keyHOME = XK_Home; + const unsigned int keyPAGEUP = XK_Page_Up; + const unsigned int keyTAB = XK_Tab; + const unsigned int keyQ = XK_q; + const unsigned int keyW = XK_w; + const unsigned int keyE = XK_e; + const unsigned int keyR = XK_r; + const unsigned int keyT = XK_t; + const unsigned int keyY = XK_y; + const unsigned int keyU = XK_u; + const unsigned int keyI = XK_i; + const unsigned int keyO = XK_o; + const unsigned int keyP = XK_p; + const unsigned int keyDELETE = XK_Delete; + const unsigned int keyEND = XK_End; + const unsigned int keyPAGEDOWN = XK_Page_Down; + const unsigned int keyCAPSLOCK = XK_Caps_Lock; + const unsigned int keyA = XK_a; + const unsigned int keyS = XK_s; + const unsigned int keyD = XK_d; + const unsigned int keyF = XK_f; + const unsigned int keyG = XK_g; + const unsigned int keyH = XK_h; + const unsigned int keyJ = XK_j; + const unsigned int keyK = XK_k; + const unsigned int keyL = XK_l; + const unsigned int keyENTER = XK_Return; + const unsigned int keySHIFTLEFT = XK_Shift_L; + const unsigned int keyZ = XK_z; + const unsigned int keyX = XK_x; + const unsigned int keyC = XK_c; + const unsigned int keyV = XK_v; + const unsigned int keyB = XK_b; + const unsigned int keyN = XK_n; + const unsigned int keyM = XK_m; + const unsigned int keySHIFTRIGHT = XK_Shift_R; + const unsigned int keyARROWUP = XK_Up; + const unsigned int keyCTRLLEFT = XK_Control_L; + const unsigned int keyAPPLEFT = XK_Super_L; + const unsigned int keyALT = XK_Alt_L; + const unsigned int keySPACE = XK_space; + const unsigned int keyALTGR = XK_Alt_R; + const unsigned int keyAPPRIGHT = XK_Super_R; + const unsigned int keyMENU = XK_Menu; + const unsigned int keyCTRLRIGHT = XK_Control_R; + const unsigned int keyARROWLEFT = XK_Left; + const unsigned int keyARROWDOWN = XK_Down; + const unsigned int keyARROWRIGHT = XK_Right; + const unsigned int keyPAD0 = XK_KP_0; + const unsigned int keyPAD1 = XK_KP_1; + const unsigned int keyPAD2 = XK_KP_2; + const unsigned int keyPAD3 = XK_KP_3; + const unsigned int keyPAD4 = XK_KP_4; + const unsigned int keyPAD5 = XK_KP_5; + const unsigned int keyPAD6 = XK_KP_6; + const unsigned int keyPAD7 = XK_KP_7; + const unsigned int keyPAD8 = XK_KP_8; + const unsigned int keyPAD9 = XK_KP_9; + const unsigned int keyPADADD = XK_KP_Add; + const unsigned int keyPADSUB = XK_KP_Subtract; + const unsigned int keyPADMUL = XK_KP_Multiply; + const unsigned int keyPADDIV = XK_KP_Divide; + +#elif cimg_display==2 + // Define keycodes for Windows. + const unsigned int keyESC = VK_ESCAPE; + const unsigned int keyF1 = VK_F1; + const unsigned int keyF2 = VK_F2; + const unsigned int keyF3 = VK_F3; + const unsigned int keyF4 = VK_F4; + const unsigned int keyF5 = VK_F5; + const unsigned int keyF6 = VK_F6; + const unsigned int keyF7 = VK_F7; + const unsigned int keyF8 = VK_F8; + const unsigned int keyF9 = VK_F9; + const unsigned int keyF10 = VK_F10; + const unsigned int keyF11 = VK_F11; + const unsigned int keyF12 = VK_F12; + const unsigned int keyPAUSE = VK_PAUSE; + const unsigned int key1 = '1'; + const unsigned int key2 = '2'; + const unsigned int key3 = '3'; + const unsigned int key4 = '4'; + const unsigned int key5 = '5'; + const unsigned int key6 = '6'; + const unsigned int key7 = '7'; + const unsigned int key8 = '8'; + const unsigned int key9 = '9'; + const unsigned int key0 = '0'; + const unsigned int keyBACKSPACE = VK_BACK; + const unsigned int keyINSERT = VK_INSERT; + const unsigned int keyHOME = VK_HOME; + const unsigned int keyPAGEUP = VK_PRIOR; + const unsigned int keyTAB = VK_TAB; + const unsigned int keyQ = 'Q'; + const unsigned int keyW = 'W'; + const unsigned int keyE = 'E'; + const unsigned int keyR = 'R'; + const unsigned int keyT = 'T'; + const unsigned int keyY = 'Y'; + const unsigned int keyU = 'U'; + const unsigned int keyI = 'I'; + const unsigned int keyO = 'O'; + const unsigned int keyP = 'P'; + const unsigned int keyDELETE = VK_DELETE; + const unsigned int keyEND = VK_END; + const unsigned int keyPAGEDOWN = VK_NEXT; + const unsigned int keyCAPSLOCK = VK_CAPITAL; + const unsigned int keyA = 'A'; + const unsigned int keyS = 'S'; + const unsigned int keyD = 'D'; + const unsigned int keyF = 'F'; + const unsigned int keyG = 'G'; + const unsigned int keyH = 'H'; + const unsigned int keyJ = 'J'; + const unsigned int keyK = 'K'; + const unsigned int keyL = 'L'; + const unsigned int keyENTER = VK_RETURN; + const unsigned int keySHIFTLEFT = VK_SHIFT; + const unsigned int keyZ = 'Z'; + const unsigned int keyX = 'X'; + const unsigned int keyC = 'C'; + const unsigned int keyV = 'V'; + const unsigned int keyB = 'B'; + const unsigned int keyN = 'N'; + const unsigned int keyM = 'M'; + const unsigned int keySHIFTRIGHT = VK_SHIFT; + const unsigned int keyARROWUP = VK_UP; + const unsigned int keyCTRLLEFT = VK_CONTROL; + const unsigned int keyAPPLEFT = VK_LWIN; + const unsigned int keyALT = VK_LMENU; + const unsigned int keySPACE = VK_SPACE; + const unsigned int keyALTGR = VK_CONTROL; + const unsigned int keyAPPRIGHT = VK_RWIN; + const unsigned int keyMENU = VK_APPS; + const unsigned int keyCTRLRIGHT = VK_CONTROL; + const unsigned int keyARROWLEFT = VK_LEFT; + const unsigned int keyARROWDOWN = VK_DOWN; + const unsigned int keyARROWRIGHT = VK_RIGHT; + const unsigned int keyPAD0 = 0x60; + const unsigned int keyPAD1 = 0x61; + const unsigned int keyPAD2 = 0x62; + const unsigned int keyPAD3 = 0x63; + const unsigned int keyPAD4 = 0x64; + const unsigned int keyPAD5 = 0x65; + const unsigned int keyPAD6 = 0x66; + const unsigned int keyPAD7 = 0x67; + const unsigned int keyPAD8 = 0x68; + const unsigned int keyPAD9 = 0x69; + const unsigned int keyPADADD = VK_ADD; + const unsigned int keyPADSUB = VK_SUBTRACT; + const unsigned int keyPADMUL = VK_MULTIPLY; + const unsigned int keyPADDIV = VK_DIVIDE; + +#else + // Define random keycodes when no display is available. + // (should rarely be used then!). + const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent) + const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent) + const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent) + const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent) + const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent) + const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent) + const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent) + const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent) + const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent) + const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent) + const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent) + const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent) + const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent) + const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent) + const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent) + const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent) + const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent) + const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent) + const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent) + const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent) + const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent) + const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent) + const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent) + const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent) + const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent) + const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent) + const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent) + const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent) + const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent) + const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent) + const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent) + const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent) + const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent) + const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent) + const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent) + const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent) + const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent) + const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent) + const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent) + const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent) + const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent) + const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent) + const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent) + const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent) + const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent) + const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent) + const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent) + const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent) + const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent) + const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent) + const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent) + const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent) + const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent) + const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent) + const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent) + const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent) + const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent) + const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent) + const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent) + const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent) + const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent) + const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent) + const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent) + const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent) + const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent) + const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent) + const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent) + const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent) + const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent) + const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent) + const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent) + const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent) + const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent) + const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent) + const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent) + const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent) + const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent) + const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent) + const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent) + const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent) + const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent) + const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent) + const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent) + const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent) + const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent) + const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent) + const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent) + const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent) +#endif + + const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI + + // Define a 10x13 binary font (small sans). + static const char *const data_font_small[] = { + " UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw Mw (wnwnwuwpwuypwuwoy" + "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y Hw cwnw >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w " + "ry qw %wuw !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow" + "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq" + "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd" + "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp" + "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw " + "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwswy~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~" + "}y|xy|}w~| s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x" + "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w" + "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f" + "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|" + "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{" + "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~} Pw~}y{|x~}cY~ i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~| ;{}`~} -" + "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~ +{|w~Xs~y}s~|\\m~}X{}" + "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~ hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{" + "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|" + "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~ " + " Ov~ !{}w~}Mw~|N{|v~ :{}v~|s{|v~V{|t}|V{|t~s}w~| p{|v~ {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~" + "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~} t{}" + "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{" + "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_" + "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{" + "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}" + "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{" + "|t}|Lw~|xw~c{|[~} iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~ ={|^~} .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x" + "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{} 4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{" + "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~} iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y" + "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}" + "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc" + "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~ Ov~ !{}w~|Mw~|M{}w~ :v~|q{}w~|Xp~}X{}v~|p{|" + "}| o{}w~| v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p" + "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~| w{|w~}|o{|}w~|(x~}tw~ rw~K{}x" + "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|" + "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~" + "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`" + "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|" + "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~} j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~} b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|" + "P{|w~|xx|av~|fv~| j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~} ?{}t~}y| u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{" + "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}| `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~" + "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~| jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n" + "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|" + "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a" + "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{|w~" + "}p{|w~}Xn~|Zv~ _{|v~ !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|" + "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~| x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}" + "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w" + "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|" + "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v" + "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v" + "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy| g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~} d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}" + "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~| A{}u~} q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^" + "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~ a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o" + "{}v~|a{|v~}p{}v~ j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|" + "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|" + "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~| vw~} `{|w~|$" + "w~} w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{}w~|ow~}Xm~|[v~ ^v~| \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}" + "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~} y{|" + "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~" + "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|" + "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" + "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|" + "x}rx}| 6w|Nw|Ox|rw| Nw~} e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|" + "Oy|Uw|jw|Vu|Wv|kw|b{}v~} p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}" + "w~}xx~x{}w~}|U{}w~ a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~| \\{}w~}" + "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}" + "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}" + "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~| ww~} `{|w~|$w~} w~} >w~}Dv~ " + "Ov~ !v~Lw~|M{|w~| <{}w~|ow~}Xy~}w|}t~[v~| _{}w~} #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~" + "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\" + "{|}p~} {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}" + "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~" + "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}" + "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~ Py~}|u{|v~} 2w~} f{" + "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}" + "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~| ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{" + "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~ aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}" + "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}" + "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v" + "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~| xw~} `{|w~|$w" + "~} w~} >w~}Dv~ Ov~ !w~}Lw~|M{|w~| {|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{" + "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v" + "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|" + "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|" + "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|" + "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~| n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{" + "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|" + "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|" + "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~" + "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv" + "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z" + "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{" + "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|" + "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|" + "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}" + "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}" + "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~ lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~" + "}H{}w~|Q{|t~|N{}w~ c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v" + "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w" + "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~" + "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m" + "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}" + "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w" + "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c" + "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}" + "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|" + "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]" + "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}" + "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}" + "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}[" + "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~" + "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a" + "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}" + "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~} pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v" + "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~ c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v" + "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m" + "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~" + "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~" + "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}" + "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}" + "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u" + "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t" + "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r" + "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w" + "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~" + "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv" + "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d" + "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v" + "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}" + "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~" + "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~" + "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o" + "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~ pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v" + "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~ c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|" + "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{" + "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n" + "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{" + "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|" + "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|" + "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w" + "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w" + "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v" + "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~" + "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]" + "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}" + "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{" + "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r" + "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|" + "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v" + "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{" + "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}" + "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~} q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw" + "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~ cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|" + "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{" + "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{" + "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w" + "~|H{|w~| s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}" + "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|" + "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s" + "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{" + "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}" + "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}" + "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t" + "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w" + "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|" + "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m" + "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~" + "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~" + "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~" + "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~" + "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~| q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x" + "x~x{|}w~}U{}w~ d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{" + "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{" + "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~" + "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~| bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U" + "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w" + "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_" + "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}" + "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|" + "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v" + "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} " + "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~" + "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v" + "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~" + "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}" + "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|" + "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}" + "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~" + "r{}w~}dw~| lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~ d{}x~}Y{|v~k{}w~}W{}w" + "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}" + "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|" + "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}" + "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~| b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T" + "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~" + "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o" + "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a" + "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{" + "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~" + "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v" + "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}" + "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|" + "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|" + "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}" + "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`" + "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u" + "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~" + "|yv~|X{}w~|sv~|dV~} 2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ " + " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}" + "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}" + "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u" + "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~| aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~" + "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{" + "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`" + "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~" + "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|" + "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~" + "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}" + "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w" + "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v" + "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}[" + "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V" + "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{" + "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}" + "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~} 2v~| k{}w~| {{|x~" + "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~ e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~" + "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w" + "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b" + "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~| a{}w~^v~|m{|" + "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c" + "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}" + "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m" + "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}" + "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|" + "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|" + "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\" + "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w" + "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}" + "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~" + "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}" + "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}" + "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~} 2v~| k{}w~| {{}" + "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~} t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{" + "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}" + "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv" + "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I" + "{}w~I{|w~| a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{" + "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K" + "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~" + "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|" + "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X" + "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}" + "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o", + "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}" + "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|" + "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~" + "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}" + "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|" + "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{" + "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~} 2v~| k{}w~| {{w~|tw~|W{|}m~}T{" + "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{" + "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|" + "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~" + "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~| " + "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v" + "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|" + "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~" + "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~" + "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~" + "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~ p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~| jv~}" + "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|" + "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}" + "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w" + "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}" + "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}" + "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd" + "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw" + "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z| 5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox" + "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}" + "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{" + "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w" + "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~| p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~" + "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~" + "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~" + "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{" + "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c" + "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~" + "}8{|y~|sw~vw~}q{|y~| Q{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{" + "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}" + "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}" + "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~" + "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v" + "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j" + "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{" + "|w~}b{}x~} pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k" + "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}" + "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u" + "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~" + "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~| r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~" + "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|" + "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}" + "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv" + "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]" + "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{" + "|w~|r{}y~ P{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|" + "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}" + "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_" + "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|" + "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}" + "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~" + "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{" + "}w~|b{}x~} q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w" + "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv" + "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|" + "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|" + "v~|Ru~|M{|w~}H{|w~J{|w~| s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}" + "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|" + "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{" + "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v" + "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}" + "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~ p{|" + "w~|m{}w~|Ux~}w{|x~} w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|" + "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{" + "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{" + "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|" + "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m" + "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|" + "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~| " + " r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}" + "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k" + "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~" + "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|" + "v~|S{}v~|L{|w~}Gw~|K{|w~| t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~" + "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~" + "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~" + "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y" + "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\" + "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~" + "} P{}w~ p{|w~|m{}w~|Ux~}w{|x~} w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b" + "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v" + "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w" + "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u" + "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{" + "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}" + "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x" + "{|v~`w~} m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~ g{}w~Uv~|lv~|W{}w~}P{}v~" + "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{" + "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv" + "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{" + "|w~}G{}x~}K{|w~| tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~" + "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L" + "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~" + "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v" + "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|" + "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~ " + "p{|w~|m{}w~|Ux~}w{|x~} w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{" + "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k" + "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V" + "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~" + "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}" + "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~" + "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|" + " mv~| o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~ gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~" + "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}" + "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}" + "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w" + "~| u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~" + "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|" + "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{" + "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}" + "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W" + "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~ p{|w~|m{}w" + "~|Ux~}w{|x~} B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~" + "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}" + "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v" + "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{" + "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v" + "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|" + "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~} mv~} g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw" + "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~ h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|" + " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}" + "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v" + "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~| u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv" + "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~" + "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{" + "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~" + "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}" + "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|" + "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~ p{|w~|m{}w~|Ux~}w{|x~} C{}w" + "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~" + "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~" + "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv" + "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m" + "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w" + "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}" + "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X" + "{|v~{|v~^{}w~} n{}v~ gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~ h{|w~T{|v~m{}w~}V{}w~}" + "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}" + "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g" + "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w" + "~}F{}x~}L{|w~| u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv" + "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w" + "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~" + "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~" + "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{" + "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|" + "w~|y{|y~} N{}w~ p{|w~}m{}w~|Ux~}w{|x~} Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|" + "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{" + "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v" + "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{" + "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w" + "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y" + "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w" + "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~| o{|v~| hw" + "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~ h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~" + "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~" + "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y" + "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~| u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}" + "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv" + "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a" + "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u" + "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}" + "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x" + "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~| >{|w~}mv~|Ux~}w{|x~} Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w" + "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w" + "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}" + "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m" + "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv" + "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}" + "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y" + "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~| o{}v~j{} {|x~}t{|" + "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~ hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|" + "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~" + "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}" + "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~| u{}w~|nu~|_" + "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv" + "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~" + "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|" + "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|" + "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x" + "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~| ={|v~n{|v~|Ux~}w{|x~} Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}" + "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w" + "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{" + "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n" + "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v" + "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}" + "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|" + "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~} p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~" + "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{" + "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a" + "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}" + "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~| u{}w~|o{}u~|_t~|q{|v~}_" + "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u" + "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~" + "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_" + "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y" + "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x" + "~|g{|x~} <{|v~|o{}v~|Ux~}w{|x~} Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f" + "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~" + "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~" + "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{" + "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{" + "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~" + "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p" + "{|v~X{|r~}Z{}u~} q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{" + "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|" + "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~" + "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u" + "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~| u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{" + "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~" + "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~| r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w" + "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}" + "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U" + "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~} ;{|u~p{|u~|Ux~}w{|x~} Ey~|s{|~}T" + "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u" + "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u" + "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v" + "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{" + "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{" + "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} " + "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y| tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|" + "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}" + "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}" + "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~" + "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|" + "v~|[{|u~}b|^{|w~}E{|w~|N{|w~| tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}" + "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|" + "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~| qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~" + "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}" + "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#" + "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{" + "|t~}Ux~}w{|x~} E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f" + "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}" + "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~" + "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}" + "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}" + "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v" + "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M" + "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~} -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o" + "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z" + "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y" + "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~" + "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~| t{}u~y" + "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv" + "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{" + "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~| q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}" + "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{" + "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~" + "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~| E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~" + "} l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|" + "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}" + "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}" + "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}" + "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{" + "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{" + "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~" + "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~} -{|h~|$v~}#{}x~}t{}x" + "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~" + "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv" + "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}" + "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~| sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw" + "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d" + "~Uw~}Lw~|M{|w~| p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~" + "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~" + "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~} A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~} m{|x~}b{}x~hw~lk~k{|x~}b{}x~" + "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}" + "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}" + "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~" + "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|" + "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~} +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~" + "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~| " + "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{" + "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~" + "| s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{" + "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~| o{|n~|w{}u~b" + "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|" + "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${" + "}m~} ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~} mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d" + "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}[" + "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~" + "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~" + "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V" + "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~| i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~} *{|}p~" + "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}" + "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~ h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w" + "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}" + "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~| q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}" + "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|" + "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~| n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~" + "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V" + "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y| 9y|}u~}y| " + "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}| q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|" + "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{" + "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}" + "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U" + "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\" + "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~} 3{|~} ;f| '{|y}w~}y| 8{|y~|X{|x~}" + "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|" + "w~| B{|v~| 1{|y}u~y}| o{|x}u~y}y| Fv~| 7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y| a{|w~}C{}x~}O{|w~| oy}" + "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}" + "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~| ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~" + "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}| Ry|y}v~y}|" + " Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~ 5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~} r{|y}|Kw~|L{|y}|Hv~| E" + "{|y}u~y}| qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y" + "}y|S{|y}v~y}y| oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}" + "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~" + "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~| Mw~| K{|y~| e{|w~Nw~" + "| ?{}w~ Cw~} .{}w~ @{|v~|d{}| Kv~| !u~| J{|w~}C{|w~O{|w~| 9w~} Iv~ bw~}9{|w~| X{|v~ rv" + "~Lw~|M{}w~| w~| D{|w~| .w~| ?{|v~}g{|x~| M{|v~ {|u~| K{|w~}Bw~|P{|w~| :{}w~} Iw~} bw~}9{" + "|w~| X{}w~| r{}w~|Mw~|Mv~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|l~| 4{|w~" + "|Ax~}w{|x~} {{}y~} /v~| ?x~| f{|x~ M{} %{}w~|Uw~}D{}w~| Lw~| K" + "{|y~| d{|w~Pw~| ?{|w~ C{}w~ .{|w~ ={|u~}|l{|u~| N{}v~ {{|u~| L{|q~}H{}x~}V{}q~| :v~| Iw~}" + " bw~}9{|w~| Xv~ q{}w~}Mw~|N{|v~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|}o~}| " + " 3{|w~|Ax~}w{|x~} {{|x~| 0v~}m{} N{|x~ e{}y~} Rv~Tw~}Dv~ S{}x~x{|w~| " + " K{|y~| c{}x~}R{}x~} >{|x~| Cw~} .{|x~| ;{}t~}|sy|}t~| N{|v~} y{|u~| M{|q~}H{|w~V" + "{}q~| ;{}v~ I{|w~} bw~}9{|w~| Y{}w~} q{|v~}|Ow~|P{|}v~} ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} " + " )v~}Iy~} gw~|Q{|y}v~y}| 1{|w~|Ax~}w{|x~} yx~| 0{}v~|p{|~} N{|x~| f{|x~ " + " S{}w~}Tw~}E{}w~} S{}x~|y{|w~ J{|y~| bw~|Sw~| >{}y~} K{}y~} 9{|p~x}q~}| N{|u~" + "| x{|u~ M{|q~} y{}q~| K{|}|p{|u~| I{}w~| bw~}9{|w~| Z{|v~ o{}q~}Tw~|U{|p~ :v~ S{|w~}W{|w~|#{|" + "w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~|Aw|vx| y{|x~} 0{|u~|s{}x~} N{|x~| " + " f{|x~| U{|v~Sw~}F{|v~ R{|x~}y{}w~ J{|y~| b{|x}|T{|x}| w{}g~}| Q" + "x|y}u~} v{|u~ N{|p} yp}| K{|x~}y|wy|}u~} J{|}v~ aw~}9{|w~| \\{|}v~} nq~}Tw~|U{|q~| :v~ S{|w~}" + "W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~| :{|}w|}w~| /t~y}x|y}v~} U{|}|x{|w~| " + " f{}x~| W{|}v~}Sw~}H{|}v~} Qq~| J{|y} *{|}l~}| O{}q" + "~ tt| `{|i~} Lr~| aw~}9{|w~| `{}q~ l{}s~}Tw~|U{|s~}| 9v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~" + "} )v~}Iy~} gw~| W{|w~| :{|q~ .{|i~} U{|q~ ly}w|}w~| [{}q~Rw~}" + "L{}q~ P{}r~ M{|y}u~y}y| L{}r~| R{|j~} Ks~} `w~}9{|w~| " + " `{}r~| jy|v}|Tw~|U{|u}| 6v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy}| gw~| W{|w~| :{|r~| " + " -{|k~}| U{|r~} l{}r~} Z{}r~|Rw~}L{}r~| O{}t~ " + " k{}t~} -{|`}| `{|}m~}| Jt~} _w~}9{|w~| `{}s~| :w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}" + "w~Uw~} )v~} d{|w~| 9y}w~y} ){}o~}| S{|}u~}| k{}r~ Y{}s~|Qw~" + "}L{}s~| M{}w~} j{}w~}| +{}`~} ]{|x}v~y}| Gw~y} ]w~}9{|w~" + "| `{}v~}| 8w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} g{|w~| 8{|}v~y}| Ly| " + " g{|y}w~}| X{}v~}|Ow~}L{}v~}| Iy| " + "l{}`~} Ww~| " + " L{}`~} Ww}| " + " r{" }; + + // Define a 104x128 binary font (huge sans). + static const char *const data_font_huge[] = { + " " + " " + " " + " " + " " + " " + " " + " " + " FY AY " + "'Z ;W @Y @Y 'Z Y @Y (Z :Y ?Y (Z 0Y ?Y (Z >X " + " " + " " + " " + " " + " )X AX '\\ )XAV 7YDY -] BY BY '[ +YEY 2X AY (\\ -YDY 'XAU 3Y AY (\\ )XAV 8YD" + "Y LY AY (\\ ,YEY #Y " + " " + " " + " " + " (X CX '^ +[CU 6ZEY .` C" + "X CY '] -ZEZ 2X CY (^ .ZEZ )[CU 2Y CY (] *[CU 7ZEZ LY CY (] -ZEZ %Y " + " " + " " + " " + " " + " 'Y EY '^ ,^FV 6ZEY /b CX DX '_ .ZEZ 2Y DX '_ /ZEZ +_FV 1X CX (_ ,^FV 7ZEZ " + " KX CX (_ .ZEZ &Y " + " " + " " + " " + " %Y GY '` .aHV 6ZEY 1e DY FX" + " 'a /ZEZ 1Y FX '` /ZEZ +aHV 0X EX '` .aHV 7ZEZ JX EX (a /ZEZ &X " + " " + " " + " " + " " + " #X GX 'XNX 0dKW 6ZEY 1f DY HX &WMX 0ZEZ 0X GX 'XMW 0ZEZ ,dLX /X GX 'WMX 0dLX 7ZEZ" + " IX GX 'WMX 0ZEZ 'X :T " + " " + " " + " " + " ;X IX 'XLX 1o 5ZEY 2ZLY " + " CX IX &WKW 0ZEZ /X HX (XLX 1ZEZ ,o .Y HX (WKX 1o 6ZEZ IY IY (WKW 0ZEZ (X X MX &WH" + "W 3VHa 4ZEY 3WDW CX LX 'WGW 2ZEZ -X LX 'WHW 2ZEZ -VHa +X KX (XHW 3VHa 5ZEZ GX KX (WGW 2ZEZ )X " + " ?b " + " " + " " + " " + " ?W MW &WFW 4VF^ 3ZEY 4WBV BW MX 'WEW 3ZEZ ,W M" + "X 'WFW 3ZEZ -VF^ )X MX 'WFW 4VF^ 4ZEZ FX MX 'WFW 3ZEZ *X ?d " + " " + " " + " " + " " + " ?W X 'WDW 5UC[ 2ZEY 4VAV AW X &WDW 4ZEZ +W NW 'WDW 4ZEZ -UC[ 'W MW 'WDW 5UC[ 3ZEZ " + "EW MW 'WDW 4ZEZ +X ?f " + " " + " " + " " + " @X \"X 'WBW 6UAW 0ZEY 4V@V B" + "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ .VAW $W W 'WBW 6UAW 1ZEZ DW W 'WBV 4ZEZ +W >f " + " " + " " + " " + " " + " ?X #W 'W@W U?V AX #W &W@V NX #W &V@W 9W \"W 'W@V .W " + "\"W 'W@V !W >XHX " + " 3Y " + " " + " " + " 6W $W &V>V U?V @W $W &W>V " + " NW $X 'V>V 8W $X (W>V /X $W 'W>V #W >XFX " + " 5Z " + " " + " ,Z " + " GZ " + " #U?V NY 7Z ,X CVCW MY " + " 7Z ,X $Z 7Z ,X >Z 6Y ,X 4Z 7Y +W 7Y @Z " + " " + " +Z " + " " + " HY \"U?V " + " MY 8Y ,Y CVBV LY 9Z ,Y #Z 9Z ,Z >Z 8Y ,Y 3Y 8Z ,Y 9Y " + " ?Z " + " *Y " + " " + " IY !U?V " + " LY :Y ,[ $R>U ,V@V MZ :Y +Z #Y 9Y +Z ?R" + ">U 8Y 9Y +Z %S?U HY :Z ,[ ;Y ?[ " + " " + " )Y " + " 8U " + " 9Y V@U JY Y @Y /X 0Y K` .X " + " ^ =ZEY @Y " + " NVAV

Y E^ /X 0_ %f 1] 'c " + " @ZEZ AY MV" + "CW X *^ +]DU 7ZEZ 5U>U JY ?Y *^ -YEZ 4Y " + " ?Y *^ .ZEZ 5[ ]DU 5Y >Y +^ ,]DU 6ZEZ Y ?Y +_ .ZEZ \"Y Z G[ G\\ @e !f JX !Y " + "LY %d :Y Y Ha /X 0b *j L] D_ " + " +g A[ LY 8Z -ZEZ \"Y 1o )V FX NZ FY " + "%Y ,X NX*Z NW 3WEW H\\ #[ !Z \"[ \"[ \"[ G[7T 8g 0Y " + "@Y +_ ,_FV 7ZEZ 5U>U IY @Y +` .YEZ 3X ?X *` /ZEZ 4[:P 8_FV 4X ?Y +` ._EU 6ZEZ NX @Y *_ .ZEZ #Y ;Y" + " FYEZ ;] GU W ,X " + " FV a \"d -g >d (d +b %b 4f Bg Ie \"e \"h " + " Ge !f IX \"Y LY &e :Y Y Jc /X 0c " + " -n $g I` .j >a ;e HU .U +b Ac 2ZEZ 'b " + " 5o -] Na (c KY .Y #_ 8Y!W'Y\"X.c$X 3XGX Mf -e +d " + ",e ,e ,e \"e=V ;k 1Y BY +XNW .aGV 7ZEZ 5V@V HX AY +XNW .YEZ 3Y AY *WNW /ZEZ 4\\>T 9`GV 3" + "X AY +XNW .`GV 6ZEZ NY AX *XNW /ZEZ $Y :Y FYEZ <_ IU (Q LZ 4Z2Z 1Q " + " &g %Z +XCX MT Y Kd /X 0e 0p " + " (m Lb 1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik LW DX DW /i ?Y(Y 4h 5ZEZ" + " ,\\ ,h 7\\ -o .` $f -h NY No %_ %c @_\"X-_\"W0h&W .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S " + "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ Im 1X CY *WMX /bHV 7ZEZ 5V@V G" + "X CY *WLW /YEZ 2Y CY *WLW 0ZEZ 3[AW :bHV 3Y BX *WLW 0bHV 6ZEZ MY CX *XMX 0ZEZ $X 9Y FYEZ " + " =a M~i 7U (Q N_ 9_8_ 3R )k 'Z +XCX +X@X 4T >e,X Cl &X IX *X GV " + " GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j /l N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z" + " Ep =t 5o Au 1u N~d'Z(Z)Z MZY " + " Le /X 0e 1r +r c 3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko " + " Y EX EY 2m @Y)Y 6l 7ZEZ 0e 2k >e 1o 0c 'j /i X !r (b 'g Eb\"W0c#X0i(W -" + "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ Kp" + " 1X DX *WKW /WMYJV 6ZEZ 5V@V GY EY *WKX 0YEZ 1Y EY *XKW 1ZEZ 2[EZ :WMZKV 1Y DX *WKX 1WLYKW 6ZEZ L" + "Y EY *WKW 0ZEZ %X 8Y FYEZ >c M~h 7T (S !a Y >X 8f /X 0f 3t -s c " + " 4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms #[ FX F[ 4n @Y*Y 6m 7ZEZ 3k 5l Bk 4o 1f )k 0k #" + "X #u (b (i Fb#X0c#W/k+X .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t " + " 0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ Ls 2X FX *WIW 1WJc 6ZEZ 4VBV EY FX *XJW 0YEZ 0X EX )WJW 1ZEZ 1[I^ x %_ ?y 5r F~S Ct :p" + " 6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ " + "@e 2X Gf +a MX %Y LY *i :Y Y >Y 9f /X 0g 5v " + " 0u d 6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w &] GX G] 6U &o ?Y+Y 7X )n 7ZEZ " + "6p 7m Eo 6o 2h *l 1l %X #v (b )k Gb$X/c$X/l,W -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z " + "!Z \"Z :~ D_-Y Hw =v >w >w >w 4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ Mt 1X HX )WHW 2VHb 6ZEZ 4WDW DX GX )WHW 1YE" + "Z /X GX )WHW 2ZEZ 0[M` ;VHb /X GY *WHW 3VHb 5ZEZ JX GX )WHW 2ZEZ 'Y 7Y FYEZ ?e M~f " + " 7U )U %g Bh@g :W .~T 't +Z +XCX ,X@X 3T Ak1X Er (X JX 'X IV HX 8q" + " =m 7y ?y '` ?y 6s F~S Dv Y >Y " + " :] %X &] 5]C\\ 1v Nc 7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y (_ H" + "X H_ 7U 'p ?Y,Y 6X *o 7ZEZ 8t 9YH] Ht 9o 3i *XG[ 1VE[ &Y %x (b *[I[ Hb$W.c%X.VE[-X " + " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az 7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ Na" + "J_ 2X IX )WGW 2VG` 5ZEZ 4XFX CX IX )WFW 2YEZ .X IX )WFW 3ZEZ /j 8VG` -X HX *WFW 4VG` 4ZEZ IX IX " + ")WGW 2ZEZ 'X 6Y FYEZ ?XKX M~f 7T )W 'i DiAi ;X 1~V (w -Z " + "+XCX ,X@X 3T AZI[2W Es (X KX &X IV HX 9s >m 7z @z )a ?y 7t F~R Dx >t 9v 8s 2` :~P <~Q&~S" + " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X ;Y -e MX 'Y " + "LY +[ +Y Y >Y :[ #X #Z 6\\?[ 2v F\\ " + " 8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#} +` HX Ia 8U (q >Y-Y 6X +p 7ZEZ 9bMb ;U@Y JbMb :" + "n 3ZIZ +T@Y 2R>Y 'X %y (XLV +ZEZ IXMW%X.YMW%W-R>Y.W -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z " + "!Z \"Z :~S Ha/Y K| B| C| D} D| 9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ N]B\\ 2X JX *WEW 3UE_ 5ZEZ 3YJY AX JW )WE" + "W 2YEZ -X KX (WFW 3ZEZ .f 5UE_ ,X JX )WFW 4VF_ 4ZEZ HX KX )WEW 3ZEZ (X 5Y FYEZ @YJW M~" + "e 7U *X (j EkCk =Y 3~X )x -Z +XCX ,W?X 3T BYEY3X Ft (X KX %X JV " + " IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D" + "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X ;Y .g MW 'Y LY +Y )Y Y " + " >Y :Z \"X \"Z 7[=Z 3aE[ E[ 9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$" + "~P -b IX Jc 9U )r >Y.Y 5X ,]DX 7ZEZ ;\\>\\ \\ 0XDX ,R=Y MX (X %hEW (SG" + "V ,YAY JSHW%W-SGW&X GX/W ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P" + " <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ \\>Z 1X LX )VCW 4UD] 4ZEZ 2f ?X LX )WDW 3YEZ ,W KX )WDW 4ZEZ -b 2UD] *W" + " KX )WDW 5UD] 3ZEZ GW LX (VCW 4ZEZ )X 4Y FYEZ @XIX M~d 7U *Y *l GmDl ?[ " + " 6~Z *`C\\ -Z +XCX ,W?W 2T CYCY5X E]CZ (X LX $X JV IX 9]E^ @m 7aGb B^Ec ,b ?y " + "9aF[ F~R E_C_ B_E^ ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa" + "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X ;Y /i MW (Y LY ,Y (Y Y >Y " + " :Y !X !Y 8[;Z 1\\ 0\\:U D[ ;ZbCh%Z(Z" + "#Z(Z$Z(Z$Y'Y![.Z HZ Z;Z 1X NX )WBV 5VBZ $e >W MX )WBW !X MX )WBW #` /UBZ (W MX )WBW 6UBZ " + " 9X MW (WCW MX 3Y GXHW M~d 8U *[ +m HnFn A] 9~\\ +^=Y" + " -Z +XCX -X@X 2U DXAX5W E\\=V (X LX #X .R@V?Q ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@] " + " j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`" + "2Z0[4[ LZ/[\"~^ @X #X Y >Y ;Z " + "!X !Y 8Z9Y 6d 4[5R CZ ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=` 1f KX Lg " + " ;U *\\=T =Y0Y 4X ,Z;R 5Z3Y &W !Y3Y 3W@W EW LX *W %jEW KV -X=X @W'X W'X EX1W ,b " + "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=` @`=e%Z(Z#Z(Z$Z(Z$Y'Y" + " Z/[ HZ !Z9Y 0W X )WAW 6VAW \"d Y >Y ;Y X !Y " + " 8Y8Y 6f 6Z2P BY j BZ(Z+[;Z;Z'_9_ 3h LX Mi <" + "U *[:R V EW KW +W %kEW KV .X;W @W'W NW(X CW2X -c *c )b " + "*c )c +c 7ZHZ 2_5[ NZ !Z Z !Z >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_ B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z" + "8Y 0W !W (V?W I` :X !W (V?W X \"X (W@W *d EX !W (W@W 0X \"X (V?W !W 1Y #d ," + "e +d +d ,e #XHW LZ#Z 7U +] -o KqHp C_ X #X " + " Y >Y ;Y X X 9Z7X 6g 7Y" + " #Z =Y8Z 7d 7[ Z )_7_ Bp EZ(Z+[;Z;Z(^5^ 5j MX Nk =U +[7P Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5] C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V H^" + " 8X #W (W>V NW \"W (W>W .h EW \"X )W>W 0W #X (V=V \"W 0Y &j 1i 0j 1j 1i &X ` .\\5U -Z +XCX -W?W =r'X>W8X EZ ;X NY !X 1XDVDX 2X " + " &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y L\\ 2Z E[7[ G\\9[ >S5[ F`7` ?YNY Y >Y ;Y X Y :Y6Y 7i 9Y \"Y " + " >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2] 6l NX m >U +Z !Y4Z 3X -Y NW(W (W " + " &X)X 8VZ !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2] E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'VR4[ G^1^ AZNY Y >Y ;Y X Y :Y6Y 7j :Y \"Y " + " >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\ 8n X !o ?U ,[ Y5Y 2X -Y W&W )W 'W%W 9V" + "Z " + "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0] F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X +P " + " %_K[ CY *r 9q 8r 9r 9q *X ;Z%Z >Q JT ,b 0q MsKs Ge " + "C^ *[0R -Z +XCX .X@X @v)X=X:W CY :X Y NX 1[HVH[ 1X 'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y M[" + " 1Z EZ4[ I[5Z ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5" + "\\!\\-\\ Z4[ GZ ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z [ ;X \"X =Y 5\\C[ #Y LY -Y 'Y 8X >Y " + " >Y ;Y X Y :Y6Y 7k ;Y \"Z @Z5Y 9YLY 5[ #Z +\\.] J| KZ" + "(Z+[;Z;Z*\\-\\ :p !X \"q @U ,Z NY6Y 1X -X W#V *W (W#W :U;V +X DW LW )mEW KV" + " /X9X BW*X LW*X BW3W +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z Z !Z >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\" + "#\\-\\#\\-\\#\\-\\ H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y /[G[ " + " DY +u =u S LU ,c 1q MtLt Hf E] )[.Q " + " -Z +XCX .W?X Bx)X=X;X DZ :X X MY 0ZIVIZ /X 'X ;Z7[ 1Z AZ ![ 4XKZ ?Y MZ 0Z EZ3Z I[5Z " + "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[ ;Y 9Z(Z$Z" + "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X =Y 6\\A[ $Y LY -Y 'Y 8X >Y >Y " + " ;Y X Y :Y6Y 7l Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP" + " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X /XC[ EY " + " -x @x >x ?x @x -X :Z'Z ?U MU -e 2q MtLt Ig E[ 'Z,P -Z +XCX .W?W By)" + "XZ0Z" + " J\\#Q'Z*\\ KZ MZ +[ Q$Z(Z :Z \"Z 4Z<] Y 7[>[ %Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" + "5Y 7UH_ Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z" + "(Z$Z(Z$Y'Y K[9[ Ct =Y3X /U@[ \"Q EY .z B{ " + "B{ Az B{ /X :Z'Y >V U -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[ '[ 8Z +XCX /X@X C`MTL_)W;" + "WZ0Z " + "J[ 'Z)\\ LZ MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !" + "\\ 9X !X >Y 8[<[ &Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y5Y " + "7RB] =\\ $Z BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\ ?\\MXL[ $X %\\LXM\\ CU" + " ,Y *Q\"R DY9Y 0X -Y #V=_?V Cm *V LV Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z(" + "Z$Z(Z$Y'Y J[:Z Bw @Y6[ .Q<[ #S GY /`Da E`C" + "` DaD` C`Da E`C` 0X 9Y(Z ?X !U .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[ &Z 7Z +XCX /X@X C\\" + "ITFY)W;W=X BY 9X !X KY +YNVNZ *X (X ;Z4Z 2Z @Z !Z 6YJZ ?Y Z /Z DY2Z JZ1Y ,T T MZ N[ NZ HZJ" + "Y >Z0Z K[ &Z(\\ MZ MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;" + "[ ![ 8X !X >Y 9[:[ 'Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y" + "5Y %\\ =] %Y BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[ @\\LXK[ %X &\\KXL\\ " + " DU -Z +S$T EY:Y /X -Z %V?fBU Eo +VEg=V =VZ !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$" + "Z(Z$Y'Y IZ;Z Ay BY9^ G[ %U HY 0]<^ G^=^ F" + "^<] E]<^ G^=^ 1X 9Z)Z @Z \"U .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[ &[ 7Z +XCX /W?X D[GTC" + "V)W;W=W AZ :X \"Y KY *j (X (X ZY .Y3Y 3Z '\\ MZ )Z ;Z 2^ +Y ;Y " + "X Y 6Y /Y5Y $[ =` G^ !Z IZ M\\ #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[ B\\KXJ[" + " &X '\\JXK\\ H\\ 1Z ,U&V EY;Y /X ,Z 'V@jDV Gp +UDj?V >VZ !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)" + "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[ \"[ &Z &[ ![" + " #\\ #[ ![ G[@W IYBZ J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z @Z \"U .k 5q N~o Mm 4l =X" + ".X 7^ 7Z3Z NZ %Z 6Z +XCX /W?W D[FT@S)W;W>X AZ :X \"Y JX (f &X )X ;Z3Z 2Z @Z !Z 7" + "XHZ ?Y !Z /Z CY1Y JZ1Z 2Y Y $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ MZ -[ Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'[" + "$[ NZ2Z%[%[#Z2[ JZ :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X X ?Y ;[6[ (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X NX " + "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y X Y 9_>W KY5Y #[ =c h >XD` " + "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[ C\\JXI[ 'X (\\IXJ\\ " + " (Y d 5Z -W(X FYV=W +X HX )^ ,Y1Y HnEW KV 0X7W BW-W HW.X M^/X )" + "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#" + "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y NX NX X E[ >XD` -c )c *b *c )c '\\ &bDX L" + "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y 8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z AZ !U /m 6q N~o No 6o ?X.X 8_ " + "6Y3Z Z $Z 6Z +XCX 0X@X DZET>Q)W;W>W ?Y :X \"X IY 'b $X )X ;Z2Y 2Z @Z !Z 8YHZ ?Y " + "!Z 0[ CY1Y JZ1Z 5\\ \\ 'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ" + "2Z&[#Z#Z2[ JZ :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z C[IXH[ (X ([HXI[ (" + "Z $k 9Z .Y*Z FY=Y .X ,\\ *UAnCU J^CW -VCmAV ?W>V *X IX (a /Y1Y HnEW KV 0X7W BW.X HW.W La3X " + "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z(" + "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y Y NX Y E[ ?XFd 1g .h /h /h /h )\\ )hHX " + "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y 9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y A[ !T /n 6q N~o q 8q @X.X 8` 7" + "Y3Y Z $Z 5Z +XCX 0X@X DYDT EW;W?X ?Y :X #Y IY %^ \"X )X k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[ CYHXGY 'X 'YGXHY 'Z &o" + " ;Z /[,[ FZ?Y -X +\\ +UBoBU LZ>W -UBnAU >W@W *X JX 'c 1Y1Y HnEW KV /W7W BW.W GW/X Lc5W 'Y ," + "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#" + "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y Y NX Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0" + "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z AZ !U /o 7p M~n s :s AX.X 8` 7Z4Y Y" + " #Z 5Z +XCX 0W?X EYCT EW;W@X >Z ;X #Y HX #Z X *X ;Z1Z 3Z @Z !Z 9XFZ ?Y \"Z /Z " + "BY2Z KZ0[ [/Z 4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y NX 0X5\\ 6Y FY" + "JiBi$YJk 8o ?YJj 9kJX ;YJc Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\" + "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t t TAU NX;W )P9P =UAWAYAU >XDX )X LX HY 3Y1Y HnEW KV /W7W " + "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-" + "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u oLX ;YLe ?u VAW?XAU ?ZHY (X MX EX 4Y1Y HnE" + "W KV /W7W AQ:Q :W0W EW1X Z !Z !Z \"Z :Z%['YHZ" + "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y Y N" + "X Y *r BXKn qMY GYMp 0Y NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u U@W?XAU >j (X " + " NX CX 5Y1Y HnEW KV /W7W AR;R ;W1X EW1W :XZ " + "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ w ?x >x ?w >w#wKv Nu ?v" + " =v =v =v 0Y Y NX Y +s BXLp >u \\ DX.X :c 7Z7Z!Y \"Z 4Z +XCX C~d&XBT DW=XB" + "X :[ >X $Y FY +f &X +X ;Z/Z 4Z AZ !Z ;YDZ ?YFP -Z?Q BZ ?Z5Z JZ/Z 5Z \"[ Gj Ii ;[\"X1Q,W\"YCZ BZ1" + "Z MZ \"Z$[!Z MZ /Z LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[ ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\" + " 1X MX AY BZ&Z 8^Ga AYN[H_ " + "YDY *X )b 6UDY%U V9W ,SU@W>W@T =h 'X X AW 5Y1Y HnEW KV /X9X ASZ !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z(" + "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y Y NX Y ,u CXM^Nb" + " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0" + "Z CZ ;P4U 1w 9l J~m#z B[;[ EX.X :d 7Y7Y X )~Q #Z +XCX C~d&XBT DW=XCX 9\\ ?X $Y FY " + "-j (X +X ;Z/Z 4Z AZ \"Z :XCZ ?YM_ 5ZE^ IZ >Y6Z IZ0[ 5Z \"[ Jj Ci ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z " + "MZ 0[ LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[ ^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\" + ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y X Y M];\\ F]E[JX IY9[ LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^" + " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[ W@U =f &X !X @W 5Y1Y HnEW KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC" + "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z" + "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y Y NX Y -w DXNY" + "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1" + "Z DZ =S4U 2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y *~R #Z +XCX C~d'YBT DX?XBW 7\\ @X $Y FY " + "/ZNVNZ *X ,X :Z/Z 4Z AZ #Z :XBZ ?o 9ZGc MZ =Z8[ HY0\\ 6Z \"[ Li >j C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!" + "Z MZ 0[ LZ(Z :Z \"Z 4ZJ] .Z 2YHXY 9Z(Z NZ2Z,Z\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6" + "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y X Y `>` I\\B[KX IY:\\ LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L" + "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\ XAU V ?W3X CW3X 8X>W #Y /Z" + "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*" + "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y Y " + "NX Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z ~d W5T 2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X )~R #Z 0~d&XBT DX?XCX 6\\ " + " =Y EY 0ZMVMZ +X ,X :Z/Z 4Z B[ %\\ :XBZ ?q ;YHg Z \\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;ZW>X@T ;a #X #X =W 6Y1Y GmEW KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :" + "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z" + "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y Y NX Y /_B] E_<" + "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y ;X~d#Z6U 3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X *~R \"Z 0~d&YCT CXAXBW 5] " + " >Y EY 2ZKVKZ -X ,X :Z/Z 4Z BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki J\\!X:hKVAW%Y@Y CZ5\\ L" + "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F` BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X" + " LX BY JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y EY .Y1Y " + "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y X Y \"eCd L[>YLX HY>^ IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G" + "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.ZUDX!T\"XW>X@U :] !X $X Z !Z !Z \"Z :Z#Z(YEZ~d&^7U 4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X )~R \"Z NW?W BYCT CYBXCX 6_ ?Y EZ 5ZI" + "VIZ /X ,X :Z.Y 4Z C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z " + " MZ 0Z KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X KX CY " + " )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y EY .Y1Y GY3Y#Y=WNX=Y M" + "ZAZ AY3Y %[ /Y X Y #gEf N[W>W?U 7W <~d BX ;W 6Y1Y GmEW KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y" + "?Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZY D~P JZ !Z#[\"~Q Dy Z K~] :Z \"Z 4ZN] *Z 2YFX?XF" + "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X KX CY (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] " + "G]7Z 4Y NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X NX Y $iGh Z:XNX" + " GYHg HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[W>W?U K~d CX ;X " + " 6Y1Y FlEW KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y Y Y ;Y?Z JZ>~Q3[ I~Q G~Q F~Q G~Q 5Z !Z !Z " + "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY @Y Y !Z Y Y Y 4_4Y)[ %Z3" + "Y GZ3Y FZ4Y FZ4Y 4Y Y NX Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[ Y D~ IZ !Z#[\"~Q Dy![ K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck Y 9Z(Z LZ6Z(" + "Z?XDX?Z G` *d #[ +X KX CY 'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I" + "Z5\\ ;] -X DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y NX Y $XIZHZIY!Z:XNX GYHf GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#" + "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[Z EiKh 6X /XC^ BTDX U\"YA\\ 4ZCZ N~d &U>W?X>T K~d EY :W 5Y1Y EkEW KV ,YAY =ZCZ DW6X @W6" + "X 5W@W 'Z>Y Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L" + "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY @Y Y Y NY Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y Y NX Y 2[6Z G" + "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z Z6Y &[ .Y NX Y %WEYJYEX#Z8a GYHe FY DX4[ DY $\\5Y CY8Z 5d Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$" + "Y'Y >c 6Z2Z KYDY ?Y X NX NY Y Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y Y NX Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ" + "4[ >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z <~d'RKY:U 5~U J" + "~T$~g'~X KY1X GX.X Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)YZ=Z =YZ=Z =YZ=Z LZ=~Q3Z H~Q G~Q F~Q G~Q" + " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY ?Y Y X MX Y Y" + " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y Y NX Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z >Z:a IY0X HY0X GX0X GX0Y FZ7Y E[" + "3Z GY6Y ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z <~d NX:U 5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W " + " NZ 3Y X@X ?]IT ?hCW 7h2X ;Y CY 7TAVAT 1X .X 8Z.Y 4Z G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5" + "^ 4i ;~d :i 1[ LWr *Y " + "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X JX DY &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y NX 0XE\\ &Y FZ4[5Y*[4Z IZ" + "2Z H[2Y KY2[ ;[ +X DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y NX NY *Q NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y" + " 5YMY [/[IuI[.\\ 4X 4\\ =X =\\$\\" + " =X MZAU -Z &X8Y G~W 6X 0W<\\ FUEX MT iNW 8[D[ K~d &T=WE\\QZZeBX] ,Z 1j <[7_ 7i 8~d 7i 5[ KW=Z=" + "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z[/[IuI[/\\ 3X 3\\ >X >\\\"\\ >X MZAU -Z 'X6X 5c " + "%X 1X;\\ GUEX MT NgMW 9[D[ J~d &T=m;T K~d In 4TA[ 4Y1Y BhEW 3Z DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z KY" + ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8" + "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[ @X NX Y NY X NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y Y NX Y 4Z2Z HZ3Y IZ1Z I" + "Z1Z JY1Z JZ1Z IZ1Z @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z I" + "[ 7YY ~m 4Z 3Y W?X >g =cAW?]'[K\\5Y ;Y CZ %V M" + "X /X 7Y-Z 5Z H[ 4l ;XZ>Z.[IuI[0\\ 2X 2\\ ?X ?\\ \\ ?X MY@U 8y ;X6X 4a $X 1X9[ HUEX MT MeLW :[D[ I~d &T=l:T " + "K~d Io 5m 3Y1Y AgEW 3Z Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[ LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z H~Q G~Q F~Q G" + "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[ AX NX Y NY Y X" + " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y Y NX Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D" + "Z0Y GY9Z ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y HZ 5X_@XAa*[I\\6Y ;Y CZ %V MX /X 7Y-Z 5Z I[ 3n >X;Z ] G`9\\ .Z 4s @[9` " + " =i /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z" + "$ZAW@WAZ F_ %\\ $[ &X IX EY &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0" + "Z ;Z *Z FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z MX N[ 7g1U U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BYY1Y%Z" + " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\ 1X 1\\ @X @\\ M\\ @X NZ" + "@U 8y ;W4X 5` #X 1X8Z HUEX MT LbJW ;ZC[ H~d &T=j8U L~d Io 5l 2Y1Y @fEW 3Z Nl 0c 0[CZ&lDW5[>mEXE\\N^" + "AlAX6\\ LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K" + "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\ BY X NX NY Y X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y Y NX Y 5Z0Y HY" + "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /" + "Y:Z IZ 4Y=T 6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m 4Z 3Y !X@X ;l @[>WBe,ZG\\7Y ;Y" + " CZ %V ;~c LX 7Y-Z 5Z J\\ 2n @Y;Z N\\ G`8\\ /Z 5u A\\V+Y8Y G~R LZ !Z\"Z\"~Q" + " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZY1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z " + "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX NZ@U 8y dEW 3Z Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6] LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z H~Q " + "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\ CY X NX" + " NY Y Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y Y NX Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$ZX /Z;Z JZ 2X>U 6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y " + "N~m 4Z 3Y !X@X :n 'WBg.ZE\\8X :Y CZ %V <~e NX 6Y-Y 4Z K\\ #a AX:Z M\\ H_6[ 0Z" + " 6aI` A]?c ?f $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z " + "K['v +o 2Y 9Z(Z IZq:X !U:[9U&Y5] DY?d =jLX FY/Z C[ " + ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX" + " B~o BX NZ@U 8y mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ" + "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y Y NX Y 5Y/" + "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CYX .Y;Y JZ 1Y?U 6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m 4Z 3Y !W?X 9p +XCi0ZC\\9X " + " :Y CZ %V <~e NX 6Z.Y 4Z L\\ M^ CY:Z L[ H^4Z 0Z 7^A^ C_Ce ?c Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\"" + "Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X HX FY " + " >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^ KY .Y1Y CY;Y KYCXIXCY " + "Bc 4Y\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ" + "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX NY?U 8y mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7" + "Y CY7Z#Z:Z Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ " + "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y Y NX Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X" + " HY0X GX0X GX0Y CYX .YW-Y6Y HZ2\\ Z !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z" + "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X HX FY At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y NX" + " 0XM\\ MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e JX Ke KzF^ !U9Y7T'Z4[ CY7] @[E" + "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L[" + "4~p BX B~o BX C~q CX NY?U 8y Z !Z !Z \"Z :Z#[)Y>ZCY*Z K" + "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z 9WK~KW/WJ}JW.W:\\:W.W" + "9[:W/W9[9W >X .Z=Y JZ /X@U 6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l 4Z 3Y \"X@X 3n /X" + "CZIZ2Z@\\W.Z6" + "Z IZ1[ Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ " + "\"X GX GY Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0XN\\ LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z" + " KYDXGWDY @a 3Z>Y +[ 5d IX Ic L~d !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y " + "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX NY?U 8y Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?" + "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0" + "X GX0Y BY>Z BY.Y EY>Y 8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z KZ .YAU 6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z" + "CZ L~k :y KY \"X@X 0m 1WCYEY3Y>\\=X 9Y BY %V <~e =l X 5Z.Y 4Z \\ E[ GY8Z JZ I]" + "2Z 2Z 8[7[ BqMZ ?^ C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s" + " I[ LZ&[Cc Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X GX GY Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0e KY" + " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b GX Ga L~c T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f " + "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[WEY9T -X EY1Y 1WEW 3Z 6ZCZ 7X7" + "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z " + "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` Y 8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z " + " LZ -YBU 5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW 'y JY \"W?X ,j 3WCYCY4Y=\\>X 9Y CZ" + " %V <~e =l X 5Z.Y 4Z !\\ C[ IY7Z JZ I]2Z 3[ 9[5[ BoLZ ?a Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z MZ 0" + "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^ M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X GX GY " + "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ " + "3` EX E_ L\\Cx NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ" + "4Y 4\\ 1Z 1[ NZ.[" + "WDX:U -X EY1Y 1WEW 3Z 5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[ IZ !Z " + " Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ " + "B{'~x/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y " + "DY@Z 8WK~KW/WJ}JW.W=aX ,Y?Y LZ +XBU 6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX " + " (y JY \"W?W (h 5XCXAX5Z<\\@Y 9Y CZ $T ;~e =l X 5Z/Z 4Z \"\\ AZ IX6Z JZ I\\1[ 4Z 8Z3Z AmKZ" + " ?d d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(YWCX;U -X EY1Y 1WEW 3Z Is 0YAX 8W6UJV IW)W" + " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Yc=W.W=[6W/X:[:X >X ,Y@Z M[ " + "+YCT 5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX )y HX #X@X (TNc 6WCX@X5Y:\\AX 8Y CZ :~e" + " =l !X 4Z/Z 4Z #\\ @[ KY6Z IZ I[0Z 4Z 9Z2[ @jJZ ?f %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z MZ 0Z Z'Z(Z" + " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(YWBXZ !Z !Z \"Z :Z#Z(YW.W>[5W.W:[:W =W +ZAY LZ *YDU 5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX " + ")y HX 6~e 9TJ_ 7XCX?X6Y9\\BX 8Y CZ KX Nl !X 4Z/Z 4Z $\\ >Z LY5Z IZ I[0Z 5Z 8Z1Z >fHY =h " + " +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[ K\\ 6Y 9Z(Z FZ" + "BZ MYFXY FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0e KY FY3Y2Y+Y1Y KZ-Y JY.Y" + " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e JX Ke LU4k IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&" + "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1] 1X 1\\ @X @\\ L\\ AX DX 4" + "Z?U -Z (X4X H~W ;\\;W GTDX\"U s A[D[ 6X %T>WBXZ !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC" + "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY" + "-Y LZ-Y MY-Z MY-Y LZ-Y CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY 6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ " + " MZ (XDU 5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX +y GX 6~e 9TG] 8WBW>X6Y8\\DY 8Y CZ " + " KX Nl !X 4Z/Z 4Z %\\ =Z LX4Z IZ I[0Z 5Z 9Z0Z X3W?W/~S KZ-Z\"Z \"Z#Z!Z MZ 0[!Z" + "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[ K\\ 7Y 9Z(Z FZCZ LZGX^ .YCZ ." + "[ )_ KX L_ ES/e FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J" + "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\ 1X 2\\ ?X ?[ M\\ @X DX 4Z?U -Z 'W4W G~W :]>X GTDY#U s @[D[ 7" + "X %U?WAX>U ,X EY1Y 1WEW \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z IZ !Z Z" + " !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8" + "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y BYDXAY KY0X HY0X GX0X GX0Y" + " @ZCY ?Y.Y CYBY 5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ 6~d IYET 4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX " + " Z 3X 6~e 9TF\\ 9WBX=W7Z7\\EX 7Y CZ KX Nl \"X 3Z/Z 4Z &\\ ;Z M~Z %Z I[0Z 6[ 9Z/" + "Y 8ZCZ 8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6[" + " J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\ LX EX IY L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y NX 0XM\\ MY " + "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\ LX M\\ AR+` CT9[:U'Z3X AY0Y FY9Z FY-Z " + "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\ 2X 3" + "\\ >X >[ \\ ?X DX 4Z?U -Z 'X6X G~W 9^@X GUDY$T Ns ?[CZ 8X %U?WAY?U ,X EY1Y 1WEW \"s 4" + "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N" + "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2ZX )YCY 5~d IYFU 4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX Z 3X 6~e 9TD[ ;XBX=X8" + "Z6\\GY 7Y CY JX Nl \"X 2Y/Z 4Z '\\ :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z 5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ " + "#Z$[!Z MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[ K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\ KX EX" + " IY LZ4Y FY.Y KZ %Z.Y KZ X DX 4Z?U -Z 'X6X G~W " + "8^BX FUDY%U Ns =ZCZ 9X $U@W@X?T +X EY1Y 1WEW \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~" + "T J~U K~T*~ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ " + "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y Y Y Y 9Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYEW?Y" + " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY 4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ 5~d HXFU 4~_+~i z @w DX.Y" + " IX.X BZ@Y 6YGZ IY Y @~e 9TCZ ;WAX=X8Y4\\HX 6Y CY JX Mj !X 2Y/Y 3Z (\\ 9Z" + " M~Z %Z I[0Z 6Z 8Z/Z \"Z 2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ " + "Z Z .Z [%Z4Z JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\ JX EX IY MZ3Y FY.Y JY %Z/Z JY Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z" + "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y Y Y Y 9Y Y " + "NX Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ 4X:X9W,W JW+WE\\H[EX,X" + "E[1W,W JW =X )ZEY 4~d HYHU 2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX Z A~e 9TCZ ~d >i 2Z GV>X3W@W0~V LZ-[\"Z " + "#Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[ KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[ IX DX J" + "Y MY2Y FY.Y JY %Z/Z JY Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N" + "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\ 5X 6\\ JTEXET J[&\\ KSDXES $Y 3Y?U -Z &Y:Y F~W 5_GX DU" + "CZ9QAU DZCZ ;X $VAW?YBU +X EY1Y 1WEW DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V" + ",~P :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y " + "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y Y Y Y 9Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z BYGX?Z KY1Y HY0X" + " GX0X GX0Y >YFZ >Y.Y AYFY 2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY 4~d GXHU 2kNRMk*tNq Mv Y 7ZIZ GY !Z A~e 9TBY `=Y(Y8ZJZ([\"[ Z " + ".[!Z$Z3Z KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\ IX DX JY MY2Y FY.Y JY $Y/Z JY YEY CYIWBXIX @f 0YGZ 0[ LZ NX NY 'U>WMW?V&Z4Y AY/Y HY8Y" + " EZ.Y FZ %Y1Y Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\ 6" + "X 7\\ JVFXFV J[(\\ KUEXFU %Y 3Y?U -Z %YXCU *X EY1Y 1WEW" + " F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+[" + "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y Y Y Z :Y Y" + " NX Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY 2X;X:W+X LX*WH\\D[HX" + "*WG[0W+X LX =X (YFZ 4~d GYIU 2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX !Z A~e 9T" + "BZ >W?W;W8Z2\\MY 4Y DY JX 4X 1Z1Z 3Z +\\ 6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z )i D~d Ci -Z GV=W4XAW/~W M" + "Z-[\"[ $Z&[ NZ MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[ L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\" + " HX DX JY NY1Y FZ0Z JY $Y/Z JY YEY BXJXAWJY A[N[ 1YGY 0[ JY NX NY 'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y Z " + "/Z K_MZ BUC]BVBU A[D[ >X #VBW=XDU *X EY1Y 1WEW G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M" + "~W N~X M~W N~X.~Q :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z" + " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z :Y Y NX Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z " + " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY 1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW Z 7ZJY EY !Z 1X@X &TAY ?X?W;W8Z1\\NX 3Y DY JX 5Y 0" + "Y1Z 3Z ,\\ 5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2" + "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[ M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\ GX CX KY NY1Y FZ0Z JZ %Y/Z JZ =Y 4" + "Y0Z GY1Y 5Y NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[ IY NX Z &VB" + "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y Z !Z !Z \"Z :Z&[&" + "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z" + " !Z :Y Y NX Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y AYIWW;W8Z0e 3Y EZ JX 5X /Z2Y 2Z -\\ 4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z #j J~d Ii CW>X6Y" + "BX0~Y NZ-[![ %Z'\\ NZ MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<][ 0" + "Z 3\\ FX CX KY NY2Z FZ0Y IZ %Y/Z JZ =Y 4Y0Z GY1Y 5Y NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 7Z 8Y" + " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[ HY NX Y %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ " + "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\ 9X :\\ J\\IXI[ I\\/\\ K[HXI[ (Y 3Z@U -Z " + "%^F^ /Z X \"f >VBnCU >[D[ @X \"VCWZ !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v " + "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y Y Y Y :Y Y NX Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW" + ";Y IX1Y GX1Y GY2Y GY2Z YJX(XJY/X)X Y W;W7Y/c 2Y EY IX 5X /Z3Z 2Z .\\" + " 3Z M~Z &Z FY1Z 8[ 6Z/Z $Z i L~d Li @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ MZ -[$Z'Z(Z :Z \"Z 4Z<] Z !Z !Z \"Z :Z(\\%" + "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s YJY .X=X=Y(" + "X!X'YJWX.Y HY2Y CZW=X8ZC" + "W/Z*Z Z-Z N[ &Z(\\ MZ MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]Y 4Z2[ GY1Y 5Y NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y 6Y" + " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[ EX NX Y $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB" + "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\ ;X <\\ F\\KXK\\ F\\3\\ H\\JXK\\ 'Y " + "2ZAU -Z 'z 1Z X Na ;V@jDV :ZCZ BX UDW;XIU 'X EY2Z 1WEW KZCZ 3X9U@V\"W*X LX9VAW H[ " + "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!" + "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z ;Y Y NX Y 5Z0Y HX0X IZ1Z IY0Z KZ0" + "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ #Z JXLU" + " -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ #Z 3X@X %TAX @WZ 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[ Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\ DX BX LY NY3[ F[2Z HZ %Y1" + "[ IZ >Y 3Y2[ GY1Y 5Y NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y 6Z 9Y .Y2Z Z !Z" + " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2" + "[.Y2\\ 2Z $Z !Z !Z Y ;Y Y NX Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z =" + "YLY ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY #Z IYNU ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ " + " $[,P )W?X %TBY AXXMY DZDZ 2YLY 3[ DY X Y \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y " + "FY2Z%[<\\a@V 7YBY CX NV LV BZ3Z 1WEW LYBY 2W8U?V#W+X KX9U" + "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z Z !Z >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8" + "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z ZMZ DZMW4WMZ![7Z 3Z 7\\ BX AX MY NY3" + "[ F\\4Z FZ &Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y 5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ " + "3YMY 3[ CY X Y !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3" + "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z i i 2WZ4" + "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW :X :" + "V >r >V/V @s #Z 2[CU -Z +[MeL[ 5Z X G\\ :W!V 3W@W 7V!W AZ4[ 1WEW LW@W 1W7s,X-" + "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z Z !Z >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\," + "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z =Y Y NX Y " + "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8" + "X $YMY %[ F^ '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[ %[1S -Y 'X@X ;Q:TCZ CX:X=X" + "5[.] /Y HY HX NZ GZ 'X +[8Z 0Z 4\\ 0[ 'Z M\\ CZ6[ 9Z 2[3[ '[ 0Y Y ?f f BX DW=\\C_J[.Z&Z\"Z0\\ " + "J\\(T'Z._ JZ MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[ @X" + " AX MY NY5] F]6Z DZ &Z5] G[ AY 2[8^ GY1Y 5Y NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y 6Y 8Y -Z6\\ ;Z" + "MZ =b=b EZ@Z 3d 5[ AY X Y L[:\\ IZ;` D[4Z FXZ5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z " + "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU 9X 9T

q \"Z 1ZCU -Z ,[JaI[ 6Z X F\\ :W#V 1" + "V?V 7W#W @[5[ 1WEW LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z Z " + "!Z >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY" + "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[ >Y Y NX Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ " + "FY6] 9c 9]6Z :d )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ %Z D] $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\ " + " '\\3T -Z (W?X ;Sd c @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-" + "Z NS*\\ 6Y 6[1[ Z 4c 5[ @Y X Y HS3V FZZ%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS 8X 8R :n :R+R U 6W%W ?[6\\ 1WEW LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z" + "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z Z !Z >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\" + "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[ ?Y Y NX Y 3[7[ FX0X G[7[ E[7[ FZ7[ F" + "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d %Z C\\ ?S 2\\ETD" + "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\ '\\5U -Z (W?W :U>TE[ CX8X?X3\\3b 1Y IY GX NZ GZ (" + "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[ ;a ` =Z EX[ 4b 6[ ?Y X Y " + "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ" + "(Z :Z 8]3] FQ 7X 7P 8l 8P)P :m Z 0[EU -Z .[?P?[ 8Z X D[ 9W(W -T\\8] 1WEW " + " LSZ !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3" + "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q " + "G\\-Q G\\-Q 5Y Y NX Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b &[2[" + " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c &Z B\\ ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :] (\\7V " + "-Z )X@X :W@TF[ BW7X?X3]6e 1X IY GX NZ GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[ 8^ " + " ^ ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a" + "\"Z0[ 7Z ;\\ >X @X NY MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ " + "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[ >Y X Y F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#[" + "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\ Z #^A] :n DZ(Z :Z 7]5] +X Mj (k NZ 0\\FUBP ;Z /[,[ " + "9Z X CZ 8X+W *R;R 4X+X =]:^ 1WEW LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z" + "8Z/Z 3_2Y NZ !Z Z !Z >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #" + "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y Y NX Y 1[:[ EX0X F\\;\\ C\\;[ C[:" + "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c 'Z " + "@[ @T JT _ %] 7X.Y D^D^ BZ6Y 6b 9_ *];X -Z )X@X :ZCTH] CX7YAX1^:h 2Y JY GX NZ" + " GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[ 5[ [ 8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ MZ &`4^" + "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[ =X @X NY M[\\ @]7R" + " D\\=a E]4U 7Y /]Bc GY1Y 5Y NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[ >Y " + "X Y E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J" + "Z$Z N[ NZ \"^C^ 7g @Z(Z :Z 7_9_ +X Lh &i MZ /]HUDR ;Z .Y*Y 8Z X BZ 8Y/X (Q:Q 2X/Y " + " <^<` 2WEW LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z Z !Z >Z !Z !Z \"Z :" + "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[=" + "` E[=a 8a 8`=\\ 6` #]:] H]9] G]:] G]:] H]9] 4W !a 'Z ?Z ?U KT N] $] 7X.Y Cv AZ6Z 7a 7a " + " -_?Z -Z )W?X :^GTK_ CX5XAX0_>k 3Y JX FX NZ GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX" + "@_ ,Z \"[ 3Y X 5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^_ 8b @a2a$[.[ 8Z <~` AX ?X Y L\\@c Fb@] ?^` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>" + "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[ 0V U 2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H" + "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX ?X Y L^DZNY FYNZF_ =`CY B^EZNY CaB] 7" + "Y .qMY GY1Y 5Y NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y X Y " + " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :" + "Z 5dGd )X Jd \"e KZ -`MUKY H~U IU&U 6Z X AY 5Z7Z LZ7Z ;~d 3cFk 8WEW " + " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd" + " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]" + " DaA] DaA] DaA] 5Y Y NX Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_ 7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^" + " dLd CdMd BdLd CdLd DeMd 2X !` %X =Y ?U LV MZ !Y 5X.Y As AZ4Y 6` 5~] )x -Z " + "*X@X 9} BX3YFZ-{L] 4Y LY FX NZ GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[ -S S 0Z BW8aG[%[\"Z$~R" + " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX ?X Y KsN" + "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y X " + " Y CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~ 'X " + " Ib c JZ ,u H~U HS$S 5Z X AY 4\\>\\ I]>\\ :~d 3~Y 8WEW CW KV)W0X FX LW" + "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} " + "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y Y NX Y .v BX0X Cw =w >v >w =w 8{ ?qMX CqMX C" + "qMX CqMY 6] 6YNr 3^ My Ay @y @z Ay 1X _ $V X !" + "Y JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6" + "] 5~P 2Y X Y CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z" + " :Z 2{ &X H` Ma IZ +t H~U GQ\"Q 4Z X AY 2aLb FaKa 8~d 3YNlN_ 8WEW " + "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM" + "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y Y NX Y -t AX0X Bu ;u pLX CpLX CpLX BoLY 6] 6YMp 1] Lv >w =v =v >w 0X _ #T ;X ?W MV LW LV 4X.Y ?n >Y3Z 7_ 1~Z " + " 't +Z *W?X 8y @X1j)vG] 5X MY EX NZ GZ *X !p <} I~S Iz Z By 6r 5Z )w As (Z \"[ " + " 5Z AX HZ Z%~ 9|$~P >~S FZ ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX " + ">X !Y JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y Y NX Y ,r @X0X As 9s :r :s 9s 7z <" + "nKX BnKX BnKX BnKY 6] 6YLn 0\\ Jt ;s :t ;t ;s .X N] !R 9V >W NX LU KU 3X.Y >l =Y2Y 7_ /~X " + " %p )Z *W?W 4u @X/i(tE] 6Y NX DX NZ GZ *X m :} I~S Iy NZ Bw 2o 5Z 'u @r 'Z \"Z " + " 4Z AY J[ Z%} 6x\"} <~S FZ N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX >X" + " !Y InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2" + "[ 7\\ 5~P 2Y X Y AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z " + ":Z(Z :Z /u #X F\\ I] GZ )r H~U *Z X AY /p >o 4~d 3YMiK^ 8WEW EX " + "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv p %Z \"Z " + " 4Z @X JZ MZ&{ 3u z 9~S FZ Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z Lr BZ(Z!y -Y -s /] <^.]&[&[ m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7" + "\\ K[0Z 6Z 4~P 2Y X Y ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o" + "Do @Z :Z(Z :Z .s \"X EZ G[ FZ 'p H~U *Z X AY ,k :k 2~d 3YLgJ^ 8WEW " + " EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r " + " AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y Y NX Y *m =X0X >m 3m 5n 5m" + " 3m 6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z El 3k 2l 3l 4l *X N\\ 5U ?Y Y KR HQ 1X.Y 9b 9Y1Z 7" + "] )~S \"j &Z +X@X -h ;X+c!l?\\ 6X Y DX Z FZ +X Kh 8} I~S Fr JZ As ,i 3[ $n ;m " + "#Z \"Y 3Z ?X KZ MZ&x -p Mu 4~S FZ Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i L^ 4e " + "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n NX DX EY EZ %m G~U *Z X BZ )e 4e /~d 3YKeH] 8" + "WEW FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4" + "n 5n 6o 6n @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y Y NX Y (i ;X0X " + "fGX >fGX >fGY 4Y 4YHf +Z Bg /g .g -g /g (X M[ 5T ?Z !Z JP 'X.Y 5" + "[ 6Y0Y 7] &~P Ne $Z +W?X '] 6W)a Mh<\\ 7Y !X CX Y EZ +X Id 6} I~S Cm HZ =l 'e " + "1Z i 6h !Z #Z 3Z ?Y M[ M['s &k Jo .~S FZ Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z Gl AZ'Z Jm (Y (i )\\ " + ";].]'[#Z =Z =~a AX =X \"Y DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y" + " $k ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y X Y ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e " + "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i LX CV CW DZ #h D~U *Z X -R9Z #[ *[ *~d 3" + "YIaE\\ 8WEW GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o " + "5Y,^ Ai /h 0i 0i 0i >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y Y NX Y" + " &e 9X0X :e ,f -f -e ,f 4XFe 0cEX a NU CZ N` 9X -T<[ " + " LYG]BX 5WEW %U HW NX MX GZ (d +b (b )b )a )b 9V;a " + ")c *c *c *c =a 4_ &^ %^ $^ &_ &_ :_/c RM] !R Z 5\\ " + " 9X ;X $Y HY NY 0Y 'X NY BY X !Y " + ":Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X -p " + " IY 8WEW #V &Z MV " + " 0U 'P ;Y 2Y >Z 8X " + " MT *X &X 9X DX " + " 5X ?\\%W ?Z 4\\ :X ;X $Y " + " IZ NY 0Y 'X NY BZ !X !Y :Y 8Y 4Y *Y 1Y EX 3Y " + " CZ IU 3X -o HY 8WEW \"V " + " 'Z LU 0V " + " CZ 2Y >Y 7X " + " MT )X 'X 9X DX 5W <\\(X ?" + "Z 3\\ ;Y e GX 2f KZ LY 0Y 'X !Y >" + "\\ %X &] 9Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3" + "X $^ @Y 8WEW !V '\\:V ;V " + " 1W GZ 0Y @Z " + " FWHX LT 'X +W 7W " + " V 5b?c A[ -\\ ?e !f " + " f /X 0g 9Y 8Y 4Y *Y " + " 1Y EX 3Y CZ IU 3X 5Y " + " NV &\\=X ;V " + "1W GY /Y AZ EWHX " + " LT &W ,X 7V V 3~T " + " A] ,\\ @e !f d " + " %e -Y Nd @c " + " (m @c " + " +u $b -Y 'X 0d 2^ /X 0_ 1Y 8Y 4Y *Y " + " 1Y EX 3Y CZ IT 2X 5Y " + "-c !q Hd >c " + " $d ,Y Nd ?b " + " %g =" + "b *t #a ,Y 'X 0d " + " ,X /X 0Y +Y 8Y 4Y *Y 1Y EX 3Y CZ '" + "X 5Y -c Nm Fc " + " =c $c +Y Nc " + " >a " + " M\\ 8a \"~Y 1" + "r !` +Y 'X 0c 1X 1Y 8Y 4Y *Y 1Y EX 3Y " + " CZ &W 5Y -b Lj " + " Db std::printf(). + \note If configuration macro \c cimg_strict_warnings is set, this function throws a + \c CImgWarningException instead. + \warning As the first argument is a format string, it is highly recommended to write + \code + cimg::warn("%s",warning_message); + \endcode + instead of + \code + cimg::warn(warning_message); + \endcode + if \c warning_message can be arbitrary, to prevent nasty memory access. + **/ + inline void warn(const char *const format, ...) { + if (cimg::exception_mode()>=1) { + char *const message = new char[16384]; + std::va_list ap; + va_start(ap,format); + cimg_vsnprintf(message,16384,format,ap); + va_end(ap); +#ifdef cimg_strict_warnings + throw CImgWarningException(message); +#else + std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message); +#endif + delete[] message; + } + } + + // Execute an external system command. + /** + \param command C-string containing the command line to execute. + \param module_name Module name. + \return Status value of the executed command, whose meaning is OS-dependent. + \note This function is similar to std::system() + but it does not open an extra console windows + on Windows-based systems. + **/ + inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) { + cimg::unused(module_name); +#ifdef cimg_no_system_calls + return -1; +#else + if (is_verbose) return std::system(command); +#if cimg_OS==1 + const unsigned int l = (unsigned int)std::strlen(command); + if (l) { + char *const ncommand = new char[l + 24]; + std::memcpy(ncommand,command,l); + std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent + const int out_val = std::system(ncommand); + delete[] ncommand; + return out_val; + } else return -1; +#elif cimg_OS==2 + PROCESS_INFORMATION pi; + STARTUPINFOA si; + std::memset(&pi,0,sizeof(PROCESS_INFORMATION)); + std::memset(&si,0,sizeof(STARTUPINFO)); + GetStartupInfoA(&si); + si.cb = sizeof(si); + si.wShowWindow = SW_HIDE; + si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW; + const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi); + if (res) { + WaitForSingleObject(pi.hProcess,INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return 0; + } else { + char* lpMsgBuf; + + // Get the error message. + DWORD errorCode = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0); + cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s", + module_name==0?"(null)":module_name, + command==0?"(null)":command, + errorCode,lpMsgBuf); + return -1; + } +#else + return std::system(command); +#endif +#endif + } + + //! Return a reference to a temporary variable of type T. + template + inline T& temporary(const T&) { + static T temp; + return temp; + } + + //! Exchange values of variables \c a and \c b. + template + inline void swap(T& a, T& b) { T t = a; a = b; b = t; } + + //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) { + cimg::swap(a1,b1); cimg::swap(a2,b2); + } + + //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) { + cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) { + cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); + } + + //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8). + template + inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6, + T7& a7, T7& b7, T8& a8, T8& b8) { + cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); + } + + //! Return the endianness of the current architecture. + /** + \return \c false for Little Endian or \c true for Big Endian. + **/ + inline bool endianness() { + const int x = 1; + return ((unsigned char*)&x)[0]?false:true; + } + + //! Reverse endianness of all elements in a memory buffer. + /** + \param[in,out] buffer Memory buffer whose endianness must be reversed. + \param size Number of buffer elements to reverse. + **/ + template + inline void invert_endianness(T* const buffer, const cimg_ulong size) { + if (size) switch (sizeof(T)) { + case 1 : break; + case 2 : { + for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) { + const unsigned short val = *(--ptr); + *ptr = (unsigned short)((val>>8) | ((val<<8))); + } + } break; + case 4 : { + for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) { + const unsigned int val = *(--ptr); + *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24); + } + } break; + case 8 : { + const cimg_uint64 + m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24, + m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56; + for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) { + const cimg_uint64 val = *(--ptr); + *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) | + ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56)); + } + } break; + default : { + for (T* ptr = buffer + size; ptr>buffer; ) { + unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); + for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe)); + } + } + } + } + inline void invert_endianness(bool* const, const cimg_ulong) {} + inline void invert_endianness(unsigned char* const, const cimg_ulong) {} + inline void invert_endianness(char* const, const cimg_ulong) {} + + //! Reverse endianness of a single variable. + /** + \param[in,out] a Variable to reverse. + \return Reference to reversed variable. + **/ + template + inline T& invert_endianness(T& a) { + invert_endianness(&a,1); + return a; + } + + // Conversion functions to get more precision when trying to store unsigned ints values as floats. + inline unsigned int float2uint(const float f) { + int tmp = 0; + std::memcpy(&tmp,&f,sizeof(float)); + if (tmp>=0) return (unsigned int)f; + unsigned int u; + // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&u,&f,sizeof(float)); + return ((u)<<2)>>2; // set sign & exponent bit to 0 + } + + inline float uint2float(const unsigned int u) { + if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287) + float f; + const unsigned int v = u|(3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1 + // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler. + std::memcpy(&f,&v,sizeof(float)); + return f; + } + + //! Return the value of a system timer, with a millisecond precision. + /** + \note The timer does not necessarily starts from \c 0. + **/ + inline cimg_uint64 time() { +#if cimg_OS==1 + struct timeval st_time; + gettimeofday(&st_time,0); + return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000; +#elif cimg_OS==2 + ULARGE_INTEGER ul; + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ul.LowPart = ft.dwLowDateTime; + ul.HighPart = ft.dwHighDateTime; + return (cimg_uint64)ul.QuadPart/10000; +#else + return 0; +#endif + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline cimg_uint64 tictoc(const bool is_tic); + + //! Start tic/toc timer for time measurement between code instructions. + /** + \return Current value of the timer (same value as time()). + **/ + inline cimg_uint64 tic() { + return cimg::tictoc(true); + } + + //! End tic/toc timer and displays elapsed time from last call to tic(). + /** + \return Time elapsed (in ms) since last call to tic(). + **/ + inline cimg_uint64 toc() { + return cimg::tictoc(false); + } + + //! Sleep for a given numbers of milliseconds. + /** + \param milliseconds Number of milliseconds to wait for. + \note This function frees the CPU resources during the sleeping time. + It can be used to temporize your program properly, without wasting CPU time. + **/ + inline void sleep(const unsigned int milliseconds) { +#if cimg_OS==1 + struct timespec tv; + tv.tv_sec = milliseconds/1000; + tv.tv_nsec = (milliseconds%1000)*1000000; + nanosleep(&tv,0); +#elif cimg_OS==2 + Sleep(milliseconds); +#else + cimg::unused(milliseconds); +#endif + } + + inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) { + if (!*p_timer) *p_timer = cimg::time(); + const cimg_uint64 current_time = cimg::time(); + if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; } + const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time); + *p_timer = current_time + time_diff; + cimg::sleep(time_diff); + return time_diff; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \return Number of milliseconds elapsed since the last call to wait(). + \note Same as sleep() with a waiting time computed with regard to the last call + of wait(). It may be used to temporize your program properly, without wasting CPU time. + **/ + inline unsigned int wait(const unsigned int milliseconds) { + cimg::mutex(3); + static cimg_uint64 timer = cimg::time(); + cimg::mutex(3,0); + return cimg::wait(milliseconds,&timer); + } + + // Custom random number generator (allow re-entrance). + inline cimg_uint64& rng() { // Used as a shared global number for rng + static cimg_uint64 rng = 0xB16B00B5U; + return rng; + } + + inline unsigned int _rand(cimg_uint64 *const p_rng) { + *p_rng = *p_rng*1103515245 + 12345U; + return (unsigned int)*p_rng; + } + + inline unsigned int _rand() { + cimg::mutex(4); + const unsigned int res = cimg::_rand(&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline void srand(cimg_uint64 *const p_rng) { +#if cimg_OS==1 + *p_rng = cimg::time() + (cimg_uint64)getpid(); +#elif cimg_OS==2 + *p_rng = cimg::time() + (cimg_uint64)_getpid(); +#endif + } + + inline void srand() { + cimg::mutex(4); + cimg::srand(&cimg::rng()); + cimg::mutex(4,0); + } + + inline void srand(const cimg_uint64 seed) { + cimg::mutex(4); + cimg::rng() = seed; + cimg::mutex(4,0); + } + + inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) { + const double val = cimg::_rand(p_rng)/(double)~0U; + return val_min + (val_max - val_min)*val; + } + + inline double rand(const double val_min, const double val_max) { + cimg::mutex(4); + const double res = cimg::rand(val_min,val_max,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline double rand(const double val_max, cimg_uint64 *const p_rng) { + const double val = cimg::_rand(p_rng)/(double)~0U; + return val_max*val; + } + + inline double rand(const double val_max=1) { + cimg::mutex(4); + const double res = cimg::rand(val_max,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline double grand(cimg_uint64 *const p_rng) { + double x1, w; + do { + const double x2 = cimg::rand(-1,1,p_rng); + x1 = cimg::rand(-1,1,p_rng); + w = x1*x1 + x2*x2; + } while (w<=0 || w>=1.); + return x1*std::sqrt((-2*std::log(w))/w); + } + + inline double grand() { + cimg::mutex(4); + const double res = cimg::grand(&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + inline unsigned int prand(const double z, cimg_uint64 *const p_rng) { + if (z<=1.e-10) return 0; + if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z); + unsigned int k = 0; + const double y = std::exp(-z); + for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng); + return k - 1; + } + + inline unsigned int prand(const double z) { + cimg::mutex(4); + const unsigned int res = cimg::prand(z,&cimg::rng()); + cimg::mutex(4,0); + return res; + } + + //! Cut (i.e. clamp) value in specified interval. + template + inline T cut(const T& val, const t& val_min, const t& val_max) { + return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val; + } + + //! Bitwise-rotate value on the left. + template + inline T rol(const T& a, const unsigned int n=1) { + return n?(T)((a<>((sizeof(T)<<3) - n))):a; + } + + inline float rol(const float a, const unsigned int n=1) { + return (float)rol((int)a,n); + } + + inline double rol(const double a, const unsigned int n=1) { + return (double)rol((cimg_long)a,n); + } + + inline double rol(const long double a, const unsigned int n=1) { + return (double)rol((cimg_long)a,n); + } + +#ifdef cimg_use_half + inline half rol(const half a, const unsigned int n=1) { + return (half)rol((int)a,n); + } +#endif + + //! Bitwise-rotate value on the right. + template + inline T ror(const T& a, const unsigned int n=1) { + return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a; + } + + inline float ror(const float a, const unsigned int n=1) { + return (float)ror((int)a,n); + } + + inline double ror(const double a, const unsigned int n=1) { + return (double)ror((cimg_long)a,n); + } + + inline double ror(const long double a, const unsigned int n=1) { + return (double)ror((cimg_long)a,n); + } + +#ifdef cimg_use_half + inline half ror(const half a, const unsigned int n=1) { + return (half)ror((int)a,n); + } +#endif + + //! Return absolute value of a value. + template + inline T abs(const T& a) { + return a>=0?a:-a; + } + inline bool abs(const bool a) { + return a; + } + inline int abs(const unsigned char a) { + return (int)a; + } + inline int abs(const unsigned short a) { + return (int)a; + } + inline int abs(const unsigned int a) { + return (int)a; + } + inline int abs(const int a) { + return std::abs(a); + } + inline cimg_int64 abs(const cimg_uint64 a) { + return (cimg_int64)a; + } + inline double abs(const double a) { + return std::fabs(a); + } + inline float abs(const float a) { + return (float)std::fabs((double)a); + } + + //! Return hyperbolic arcosine of a value. + inline double acosh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::acosh(x); +#else + return std::log(x + std::sqrt(x*x - 1)); +#endif + } + + //! Return hyperbolic arcsine of a value. + inline double asinh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::asinh(x); +#else + return std::log(x + std::sqrt(x*x + 1)); +#endif + } + + //! Return hyperbolic arctangent of a value. + inline double atanh(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::atanh(x); +#else + return 0.5*std::log((1. + x)/(1. - x)); +#endif + } + + //! Return the sinc of a given value. + inline double sinc(const double x) { + return x?std::sin(x)/x:1; + } + + //! Return base-2 logarithm of a value. + inline double log2(const double x) { +#if cimg_use_cpp11==1 && !defined(_MSC_VER) + return std::log2(x); +#else + const double base2 = std::log(2.); + return std::log(x)/base2; +#endif + } + + //! Return square of a value. + template + inline T sqr(const T& val) { + return val*val; + } + + // Return inverse of error function. + template + inline T erfinv(const T& val) { + const T + sgn = val<0?-1:1, + x = (1 - val)*(1 + val), + lnx = std::log(x), + tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx), + tt2 = lnx/(T)0.147; + return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2)); + } + + //! Return cubic root of a value. + template + inline double cbrt(const T& x) { +#if cimg_use_cpp11==1 + return std::cbrt(x); +#else + return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3); +#endif + } + + template + inline T pow3(const T& val) { + return val*val*val; + } + template + inline T pow4(const T& val) { + return val*val*val*val; + } + + //! Return the minimum between three values. + template + inline t min(const t& a, const t& b, const t& c) { + return std::min(std::min(a,b),c); + } + + //! Return the minimum between four values. + template + inline t min(const t& a, const t& b, const t& c, const t& d) { + return std::min(std::min(a,b),std::min(c,d)); + } + + //! Return the minabs between two values. + template + inline t minabs(const t& a, const t& b) { + return cimg::abs(b) + inline t minabs(const t& a, const t& b, const t& abs_b) { + return abs_b + inline t max(const t& a, const t& b, const t& c) { + return std::max(std::max(a,b),c); + } + + //! Return the maximum between four values. + template + inline t max(const t& a, const t& b, const t& c, const t& d) { + return std::max(std::max(a,b),std::max(c,d)); + } + + //! Return the maxabs between two values. + template + inline t maxabs(const t& a, const t& b) { + return cimg::abs(b)>cimg::abs(a)?b:a; + } + + template + inline t maxabs(const t& a, const t& b, const t& abs_b) { + return abs_b>cimg::abs(a)?b:a; + } + + //! Return the sign of a value. + template + inline T sign(const T& x) { + return (T)(cimg::type::is_nan(x)?0:x<0?-1:x>0); + } + + //! Return the nearest power of 2 higher than given value. + template + inline cimg_uint64 nearest_pow2(const T& x) { + cimg_uint64 i = 1; + while (x>i) i<<=1; + return i; + } + + //! Return the modulo of a value. + /** + \param x Input value. + \param m Modulo value. + \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type. + **/ + template + inline T mod(const T& x, const T& m) { + const double dx = (double)x, dm = (double)m; + if (!cimg::type::is_finite(dm)) return x; + if (cimg::type::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm)); + return (T)0; + } + inline int mod(const bool x, const bool m) { + return m?(x?1:0):0; + } + inline int mod(const unsigned char x, const unsigned char m) { + return x%m; + } + inline int mod(const char x, const char m) { +#if defined(CHAR_MAX) && CHAR_MAX==255 + return x%m; +#else + return x>=0?x%m:(x%m?m + x%m:0); +#endif + } + inline int mod(const unsigned short x, const unsigned short m) { + return (int)(x%m); + } + inline int mod(const short x, const short m) { + return (int)(x>=0?x%m:(x%m?m + x%m:0)); + } + inline int mod(const unsigned int x, const unsigned int m) { + return (int)(x%m); + } + inline int mod(const int x, const int m) { + return (int)(x>=0?x%m:(x%m?m + x%m:0)); + } + inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) { + return (cimg_int64)(x%m); + } + inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) { + return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0)); + } + + //! Return the min-mod of two values. + /** + \note minmod(\p a,\p b) is defined to be: + - minmod(\p a,\p b) = min(\p a,\p b), if \p a and \p b have the same sign. + - minmod(\p a,\p b) = 0, if \p a and \p b have different signs. + **/ + template + inline T minmod(const T& a, const T& b) { + return a*b<=0?0:(a>0?(a + inline T round(const T& x) { + return (T)std::floor((_cimg_Tfloat)x + 0.5f); + } + + template + inline int uiround(const T x) { + return cimg::type::is_float()?(int)(x + 0.5f):(int)x; + } + + //! Return rounded value. + /** + \param x Value to be rounded. + \param y Rounding precision. + \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward). + \return Rounded value, having the same type as input value \c x. + **/ + template + inline T round(const T& x, const double y, const int rounding_type=0) { + if (y<=0) return x; + if (y==1) switch (rounding_type) { + case 0 : return cimg::round(x); + case 1 : return (T)std::ceil((_cimg_Tfloat)x); + default : return (T)std::floor((_cimg_Tfloat)x); + } + const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor; + return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx))); + } + + // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values. + // (contribution by RawTherapee: http://rawtherapee.com/). + template + inline T median(T val0, T val1) { + return (val0 + val1)/2; + } + + template + inline T median(T val0, T val1, T val2) { + return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1))); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4) { + T tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); + val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2); + val1 = tmp; tmp = std::min(val2,val3); + return std::max(val1,tmp); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) { + T tmp = std::min(val0,val5); + val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp; + tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4); + val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5); + val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); + val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp); + tmp = std::min(val1,tmp); val3 = std::max(tmp,val3); + return std::min(val3,val4); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) { + T tmp = std::min(val1,val2); + val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); + val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); + val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); + val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7); + val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2); + val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5); + val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8); + val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8); + val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6); + val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7); + tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp); + return std::min(val4,val2); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8, T val9, T val10, T val11, + T val12) { + T tmp = std::min(val1,val7); + val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; + tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8); + val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12); + val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp; + tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11); + val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp; + tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2); + val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; + tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4); + val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; + tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12); + tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10); + val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; + tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9); + val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp; + tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3); + tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10); + val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8); + val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp); + val5 = std::max(tmp,val5); val6 = std::min(val6,val7); + return std::max(val5,val6); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, + T val5, T val6, T val7, T val8, T val9, + T val10, T val11, T val12, T val13, T val14, + T val15, T val16, T val17, T val18, T val19, + T val20, T val21, T val22, T val23, T val24) { + T tmp = std::min(val0,val1); + val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); + val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3); + val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; + tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6); + tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10); + val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9); + tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13); + val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12); + tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16); + val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15); + tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19); + val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18); + tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22); + val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21); + tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5); + val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp; + tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3); + tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7); + val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14); + val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14); + val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15); + val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15); + val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16); + val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16); + val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23); + val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23); + val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24); + val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24); + val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22); + val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18); + val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18); + val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp; + tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10); + val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp; + tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11); + tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21); + val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12); + tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22); + val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23); + val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23); + val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24); + val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15); + val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19); + tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp); + val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11); + val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17); + tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12); + val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14); + val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7); + tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14); + tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12); + val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp); + val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp; + val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp; + tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp); + val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp; + tmp = std::min(val10,val20); + return std::max(tmp,val12); + } + + template + inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, + T val7, T val8, T val9, T val10, T val11, T val12, T val13, + T val14, T val15, T val16, T val17, T val18, T val19, T val20, + T val21, T val22, T val23, T val24, T val25, T val26, T val27, + T val28, T val29, T val30, T val31, T val32, T val33, T val34, + T val35, T val36, T val37, T val38, T val39, T val40, T val41, + T val42, T val43, T val44, T val45, T val46, T val47, T val48) { + T tmp = std::min(val0,val32); + val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp; + tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35); + val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp; + tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38); + val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp; + tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41); + val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42); + val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp; + tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45); + val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46); + val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp; + tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16); + val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17); + val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19); + val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp; + tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22); + val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp; + tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25); + val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26); + val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp; + tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29); + val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30); + val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp; + tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32); + val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33); + val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp; + tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36); + val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37); + val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp; + tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40); + val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41); + val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp; + tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44); + val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45); + val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp; + tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8); + val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp; + tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11); + val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp; + tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14); + val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp; + tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25); + val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26); + val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp; + tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29); + val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30); + val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp; + tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41); + val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42); + val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp; + tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45); + val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46); + val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp; + tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33); + val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34); + val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp; + tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37); + val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38); + val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp; + tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16); + val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17); + val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp; + tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20); + val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21); + val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp; + tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32); + val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33); + val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp; + tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36); + val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37); + val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp; + tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48); + val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4); + val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6); + val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp; + tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13); + val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14); + val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp; + tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21); + val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22); + val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp; + tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29); + val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30); + val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp; + tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37); + val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38); + val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp; + tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45); + val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46); + val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp; + tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33); + val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34); + val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp; + tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41); + val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42); + val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp; + tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16); + val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17); + val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp; + tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24); + val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25); + val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp; + tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32); + val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33); + val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp; + tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40); + val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41); + val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp; + tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48); + val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8); + val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10); + val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp; + tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17); + val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18); + val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp; + tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25); + val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26); + val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp; + tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33); + val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34); + val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp; + tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41); + val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42); + val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp; + tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2); + val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp; + tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7); + val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp; + tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14); + val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15); + val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp; + tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22); + val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23); + val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp; + tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30); + val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31); + val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp; + tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38); + val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39); + val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp; + tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46); + val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47); + val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33); + val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp; + tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40); + val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41); + val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp; + tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48); + val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16); + val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp; + tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21); + val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24); + val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp; + tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29); + val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32); + val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp; + tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37); + val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40); + val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp; + tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45); + val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48); + val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9); + val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp; + tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16); + val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17); + val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp; + tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24); + val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25); + val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp; + tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32); + val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33); + val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp; + tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40); + val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41); + val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp; + tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48); + val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); + val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8); + val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp; + tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13); + val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16); + val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp; + tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21); + val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24); + val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp; + tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29); + val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32); + val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp; + tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37); + val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40); + val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp; + tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45); + val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48); + val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5); + val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11); + val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17); + val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23); + val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29); + val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35); + val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41); + val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47); + val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36); + val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42); + val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48); + val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28); + val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34); + val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24); + val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30); + val24 = std::max(val21,val24); val23 = std::min(val23,val26); + return std::max(val23,val24); + } + + //! Return sqrt(x^2 + y^2). + template + inline T hypot(const T x, const T y) { + return std::sqrt(x*x + y*y); + } + + template + inline T hypot(const T x, const T y, const T z) { + return std::sqrt(x*x + y*y + z*z); + } + + template + inline T _hypot(const T x, const T y) { // Slower but more precise version + T nx = cimg::abs(x), ny = cimg::abs(y), t; + if (nx0) { t/=nx; return nx*std::sqrt(1 + t*t); } + return 0; + } + + //! Return the factorial of n + inline double factorial(const int n) { + if (n<0) return cimg::type::nan(); + if (n<2) return 1; + double res = 2; + for (int i = 3; i<=n; ++i) res*=i; + return res; + } + + //! Return the number of permutations of k objects in a set of n objects. + inline double permutations(const int k, const int n, const bool with_order) { + if (n<0 || k<0) return cimg::type::nan(); + if (k>n) return 0; + double res = 1; + for (int i = n; i>=n - k + 1; --i) res*=i; + return with_order?res:res/cimg::factorial(k); + } + + inline double _fibonacci(int exp) { + double + base = (1 + std::sqrt(5.))/2, + result = 1/std::sqrt(5.); + while (exp) { + if (exp&1) result*=base; + exp>>=1; + base*=base; + } + return result; + } + + //! Calculate fibonacci number. + // (Precise up to n = 78, less precise for n>78). + inline double fibonacci(const int n) { + if (n<0) return cimg::type::nan(); + if (n<3) return 1; + if (n<11) { + cimg_uint64 fn1 = 1, fn2 = 1, fn = 0; + for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } + return (double)fn; + } + if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10 + return (double)((cimg_uint64)(_fibonacci(n) + 0.5)); + + if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93 + cimg_uint64 + fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL) + fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL + fn = 0; + for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; } + return (double)fn; + } + return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation + } + + //! Calculate greatest common divisor. + inline long gcd(long a, long b) { + while (a) { const long c = a; a = b%a; b = c; } + return b; + } + + //! Convert character to lower case. + inline char lowercase(const char x) { + return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a'); + } + inline double lowercase(const double x) { + return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a'); + } + + //! Convert C-string to lower case. + inline void lowercase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr); + } + + //! Convert character to upper case. + inline char uppercase(const char x) { + return (char)((x<'a'||x>'z')?x:x - 'a' + 'A'); + } + + inline double uppercase(const double x) { + return (double)((x<'a'||x>'z')?x:x - 'a' + 'A'); + } + + //! Convert C-string to upper case. + inline void uppercase(char *const str) { + if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr); + } + + //! Return \c true if input character is blank (space, tab, or non-printable character). + inline bool is_blank(const char c) { + return c>=0 && (unsigned char)c<=' '; + } + + //! Read value in a C-string. + /** + \param str C-string containing the float value to read. + \return Read value. + \note Same as std::atof() extended to manage the retrieval of fractions from C-strings, + as in "1/2". + **/ + inline double atof(const char *const str) { + double x = 0, y = 1; + return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0; + } + + //! Compare the first \p l characters of two C-strings, ignoring the case. + /** + \param str1 C-string. + \param str2 C-string. + \param l Number of characters to compare. + \return \c 0 if the two strings are equal, something else otherwise. + \note This function has to be defined since it is not provided by all C++-compilers (not ANSI). + **/ + inline int strncasecmp(const char *const str1, const char *const str2, const int l) { + if (!l) return 0; + if (!str1) return str2?-1:0; + const char *nstr1 = str1, *nstr2 = str2; + int k, diff = 0; for (k = 0; kp && str[q]==delimiter; ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } + return false; + } + + //! Remove white spaces on the start and/or end of a C-string. + inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) { + if (!str) return false; + const int l = (int)std::strlen(str); + int p, q; + if (is_symmetric) for (p = 0, q = l - 1; pp && is_blank(str[q]); ) { --q; if (!is_iterative) break; } + } + const int n = q - p + 1; + if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; } + return false; + } + + //! Replace reserved characters (for Windows filename) by another character. + /** + \param[in,out] str C-string to work with (modified at output). + \param[in] c Replacement character. + **/ + inline void strwindows_reserved(char *const str, const char c='_') { + for (char *s = str; *s; ++s) { + const char i = *s; + if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c; + } + } + + //! Replace escape sequences in C-strings by character values. + /** + \param[in,out] str C-string to work with (modified at output). + **/ + inline void strunescape(char *const str) { +#define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break; + + unsigned char val = 0; + for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) { + cimg_strunescape('a','\a'); + cimg_strunescape('b','\b'); + cimg_strunescape('e',0x1B); + cimg_strunescape('f','\f'); + cimg_strunescape('n','\n'); + cimg_strunescape('r','\r'); + cimg_strunescape('t','\t'); + cimg_strunescape('v','\v'); + cimg_strunescape('\\','\\'); + cimg_strunescape('\'','\''); + cimg_strunescape('\"','\"'); + cimg_strunescape('\?','\?'); + case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : + val = (unsigned char)(*(ns++) - '0'); + if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; + if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0'; + *nd = (char)val; + break; + case 'x' : { + char c = lowercase(*(++ns)); + if ((c>='0' && c<='9') || (c>='a' && c<='f')) { + val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10); + c = lowercase(*(++ns)); + if ((c>='0' && c<='9') || (c>='a' && c<='f')) { + (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10); + ++ns; + } + *nd = (char)val; + } else *nd = c; + } break; + case 'u' : { // UTF-8 BMP + char c1, c2, c3, c4; + if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) && + (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) && + (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) && + (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) { + c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10); + c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10); + c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10); + c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10); + const unsigned int ival = + ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4; + if (ival<=0x007f) *nd = (char)ival; + else if (ival<=0x07ff) { + *(nd++) = (char)((ival>>6)|0xc0); + *nd = (char)((ival&0x3f)|0x80); + } else { + *(nd++) = (char)((ival>>12)|0xe0); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } + ns+=5; + } else *nd = *(ns++); + } break; + case 'U' : { // UTF-8 astral planes + char c1, c2, c3, c4, c5, c6, c7, c8; + if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) && + (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) && + (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) && + (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) && + (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) && + (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) && + (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) && + (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) { + c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10); + c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10); + c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10); + c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10); + c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10); + c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10); + c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10); + c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10); + const unsigned int ival = + ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) | + ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8; + if (ival<=0x007f) *nd = (char)ival; + else if (ival<=0x07ff) { + *(nd++) = (char)((ival>>6)|0xc0); + *nd = (char)((ival&0x3f)|0x80); + } else if (ival<=0xffff) { + *(nd++) = (char)((ival>>12)|0xe0); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } else { + *(nd++) = (char)((ival>>18)|0xf0); + *(nd++) = (char)(((ival>>12)&0x3f)|0x80); + *(nd++) = (char)(((ival>>6)&0x3f)|0x80); + *nd = (char)((ival&0x3f)|0x80); + } + ns+=9; + } else *nd = *(ns++); + } break; + default : if (*ns) *nd = *(ns++); + } + else *nd = *(ns++); + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const cimg_ulong size); + + // Return string that identifies the running OS. + inline const char *stros() { +#if defined(linux) || defined(__linux) || defined(__linux__) + static const char *const str = "Linux"; +#elif defined(sun) || defined(__sun) + static const char *const str = "Sun OS"; +#elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__) + static const char *const str = "BSD"; +#elif defined(sgi) || defined(__sgi) + static const char *const str = "Irix"; +#elif defined(__MACOSX__) || defined(__APPLE__) + static const char *const str = "Mac OS"; +#elif defined(unix) || defined(__unix) || defined(__unix__) + static const char *const str = "Generic Unix"; +#elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \ + defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + static const char *const str = "Windows"; +#else + const char + *const _str1 = std::getenv("OSTYPE"), + *const _str2 = _str1?_str1:std::getenv("OS"), + *const str = _str2?_str2:"Unknown OS"; +#endif + return str; + } + + //! Return the basename of a filename. + inline const char* basename(const char *const s, const char separator=cimg_file_separator) { + const char *p = 0, *np = s; + while (np>=s && (p=np)) np = std::strchr(np,separator) + 1; + return p; + } + + // Return a random filename. + inline const char* filenamerand() { + cimg::mutex(6); + static char randomid[9]; + for (unsigned int k = 0; k<8; ++k) { + const int v = (int)cimg::rand(65535)%3; + randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)): + (v==1?('a' + ((int)cimg::rand(65535)%26)): + ('A' + ((int)cimg::rand(65535)%26)))); + } + cimg::mutex(6,0); + return randomid; + } + + // Convert filename as a Windows-style filename (short path name). + inline void winformat_string(char *const str) { + if (str && *str) { +#if cimg_OS==2 + char *const nstr = new char[MAX_PATH]; + if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr); + delete[] nstr; +#endif + } + } + + // Open a file (similar to std:: fopen(), but with wide character support on Windows). + inline std::FILE *std_fopen(const char *const path, const char *const mode); + + + //! Open a file. + /** + \param path Path of the filename to open. + \param mode C-string describing the opening mode. + \return Opened file. + \note Same as std::fopen() but throw a \c CImgIOException when + the specified file cannot be opened, instead of returning \c 0. + **/ + inline std::FILE *fopen(const char *const path, const char *const mode) { + if (!path) + throw CImgArgumentException("cimg::fopen(): Specified file path is (null)."); + if (!mode) + throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).", + path); + std::FILE *res = 0; + if (*path=='-' && (!path[1] || path[1]=='.')) { + res = (*mode=='r')?cimg::_stdin():cimg::_stdout(); +#if cimg_OS==2 + if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode +#ifdef __BORLANDC__ + if (setmode(_fileno(res),0x8000)==-1) res = 0; +#else + if (_setmode(_fileno(res),0x8000)==-1) res = 0; +#endif + } +#endif + } else res = cimg::std_fopen(path,mode); + if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.", + path,mode); + return res; + } + + //! Close a file. + /** + \param file File to close. + \return \c 0 if file has been closed properly, something else otherwise. + \note Same as std::fclose() but display a warning message if + the file has not been closed properly. + **/ + inline int fclose(std::FILE *file) { + if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; } + if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0; + const int errn = std::fclose(file); + if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.", + errn); + return errn; + } + + //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows). + inline int fseek(FILE *stream, cimg_long offset, int origin) { +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + return _fseeki64(stream,(__int64)offset,origin); +#else + return std::fseek(stream,offset,origin); +#endif + } + + //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows). + inline cimg_long ftell(FILE *stream) { +#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) + return (cimg_long)_ftelli64(stream); +#else + return (cimg_long)std::ftell(stream); +#endif + } + + // Get the file or directory attributes with support for UTF-8 paths (Windows only). +#if cimg_OS==2 + inline DWORD win_getfileattributes(const char *const path); +#endif + + //! Check if a path is a directory. + /** + \param path Specified path to test. + **/ + inline bool is_directory(const char *const path) { + if (!path || !*path) return false; +#if cimg_OS==1 + struct stat st_buf; + return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode)); +#elif cimg_OS==2 + const DWORD res = win_getfileattributes(path); + return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY); +#else + return false; +#endif + } + + //! Check if a path is a file. + /** + \param path Specified path to test. + **/ + inline bool is_file(const char *const path) { + if (!path || !*path) return false; +#if cimg_OS==2 + const DWORD res = cimg::win_getfileattributes(path); + return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY); +#else + std::FILE *const file = cimg::std_fopen(path,"rb"); + if (!file) return false; + cimg::fclose(file); + return !is_directory(path); +#endif + } + + //! Get file size. + /** + \param filename Specified filename to get size from. + \return File size or '-1' if file does not exist. + **/ + inline cimg_int64 fsize(const char *const filename) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + if (!file) return (cimg_int64)-1; + std::fseek(file,0,SEEK_END); + const cimg_int64 siz = (cimg_int64)std::ftell(file); + cimg::fclose(file); + return siz; + } + + //! Get last write time of a given file or directory (multiple-attributes version). + /** + \param path Specified path to get attributes from. + \param[in,out] attr Type of requested time attributes. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second } + Replaced by read attributes after return (or -1 if an error occurred). + \param nb_attr Number of attributes to read/write. + \return Latest read attribute. + **/ + template + inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) { +#define _cimg_fdate_err() for (unsigned int i = 0; i + inline int date(T *attr, const unsigned int nb_attr) { + int res = -1; + cimg::mutex(6); +#if cimg_OS==2 + SYSTEMTIME st; + GetLocalTime(&st); + for (unsigned int i = 0; itm_year + 1900: + attr[i]==1?st->tm_mon + 1: + attr[i]==2?st->tm_mday: + attr[i]==3?st->tm_wday: + attr[i]==4?st->tm_hour: + attr[i]==5?st->tm_min: + attr[i]==6?st->tm_sec: + attr[i]==7?_st.tv_usec/1000:-1); + attr[i] = (T)res; + } +#endif + cimg::mutex(6,0); + return res; + } + + //! Get current local time (single-attribute version). + /** + \param attr Type of requested time attribute. + Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second | + 7=millisecond } + \return Specified attribute or -1 if an error occurred. + **/ + inline int date(unsigned int attr) { + int out = (int)attr; + return date(&out,1); + } + + // Get/set path to the \c curl binary. + inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c dcraw binary. + inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the custom's \c custom binary. + inline const char *custom_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the GraphicsMagick's \c gm binary. + inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c gunzip binary. + inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c gzip binary. + inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the ImageMagick's \c convert binary. + inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the Medcon's \c medcon binary. + inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to store temporary files. + inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false); + + // Get/set path to the \c wget binary. + inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false); + + //! Split filename into two C-strings \c body and \c extension. + /** + filename and body must not overlap! + **/ + inline const char *split_filename(const char *const filename, char *const body=0) { + if (!filename) { if (body) *body = 0; return ""; } + const char * p = std::strrchr(filename,'.'); + if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension. + if (body) std::strcpy(body,filename); + return filename + std::strlen(filename); + } + const unsigned int l = (unsigned int)(p - filename); + if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; } + return p + 1; + } + + // Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, + const unsigned int digits, char *const str); + + //! Read data from file. + /** + \param[out] ptr Pointer to memory buffer that will contain the binary data read from file. + \param nmemb Number of elements to read. + \param stream File to read data from. + \return Number of read elements. + \note Same as std::fread() but may display warning message if all elements could not be read. + **/ + template + inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",stream,ptr); + if (!nmemb) return 0; + const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0; + do { + l_to_read = (to_read*sizeof(T))0); + if (to_read>0) + warn("cimg::fread(): Only %lu/%lu elements could be read from file.", + (unsigned long)al_read,(unsigned long)nmemb); + return al_read; + } + + //! Write data to file. + /** + \param ptr Pointer to memory buffer containing the binary data to write on file. + \param nmemb Number of elements to write. + \param[out] stream File to write data on. + \return Number of written elements. + \note Similar to std::fwrite but may display warning messages if all elements could not be written. + **/ + template + inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) { + if (!ptr || !stream) + throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.", + nmemb,cimg::type::string(),nmemb>1?"s":"",ptr,stream); + if (!nmemb) return 0; + const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); + size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0; + do { + l_to_write = (to_write*sizeof(T))0); + if (to_write>0) + warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.", + (unsigned long)al_write,(unsigned long)nmemb); + return al_write; + } + + //! Create an empty file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + **/ + inline void fempty(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::fempty(): Specified filename is (null)."); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (!file) cimg::fclose(nfile); + } + + // Try to guess format from an image file. + inline const char *ftype(std::FILE *const file, const char *const filename); + + // Get or set load from network mode (can be { 0=disabled | 1=enabled }). + inline bool& network_mode(const bool value, const bool is_set) { + static bool mode = true; + if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); } + return mode; + } + + inline bool& network_mode() { + return network_mode(false,false); + } + + // Load file from network as a local temporary file. + inline char *load_network(const char *const url, char *const filename_local, + const unsigned int timeout=0, const bool try_fallback=false, + const char *const referer=0); + + //! Return options specified on the command line. + inline const char* option(const char *const name, const int argc, const char *const *const argv, + const char *const _default, const char *const usage, const bool reset_static) { + static bool first = true, visu = false; + if (reset_static) { first = true; return 0; } + const char *res = 0; + if (first) { + first = false; + visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0; + visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0; + } + if (!name && visu) { + if (usage) { + std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); + std::fprintf(cimg::output(),": %s",usage); + std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time); + } + if (_default) std::fprintf(cimg::output(),"%s\n",_default); + } + if (name) { + if (argc>0) { + int k = 0; + while (k Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n", + cimg::t_bold, + cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"), + cimg::t_normal,cimg::t_green, + cimg_OS, + cimg::t_normal); + + std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n", + cimg::t_bold, + cimg::endianness()?"Big":"Little", + cimg::t_normal); + + std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n", + cimg::t_bold, + cimg_verbosity==0?"Quiet": + cimg_verbosity==1?"Console": + cimg_verbosity==2?"Dialog": + cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings", + cimg::t_normal,cimg::t_green, + cimg_verbosity, + cimg::t_normal); + + std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n", + cimg::t_bold, +#ifdef cimg_strict_warnings + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n", + cimg::t_bold, + cimg_use_cpp11?"Yes":"No", + cimg::t_normal,cimg::t_green, + (int)cimg_use_cpp11, + cimg::t_normal); + + std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_vt100 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n", + cimg::t_bold, + cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown", + cimg::t_normal,cimg::t_green, + (int)cimg_display, + cimg::t_normal); + +#if cimg_display==1 + std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xshm + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_xrandr + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); +#endif + std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n", + cimg::t_bold, +#if cimg_use_openmp!=0 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_png + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_jpeg + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_tiff + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_magick + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_fftw3 + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n", + cimg::t_bold, +#ifdef cimg_use_lapack + "Yes",cimg::t_normal,cimg::t_green,"defined", +#else + "No",cimg::t_normal,cimg::t_green,"undefined", +#endif + cimg::t_normal); + + char *const tmp = new char[1024]; + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path()); + std::fprintf(cimg::output()," > Path of 'curl': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path()); + std::fprintf(cimg::output()," > Path of 'dcraw': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::custom_path()); + std::fprintf(cimg::output()," > Path of 'custom': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path()); + std::fprintf(cimg::output()," > Path of 'graphicsmagick': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path()); + std::fprintf(cimg::output()," > Path of 'gunzip': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path()); + std::fprintf(cimg::output()," > Path of 'gzip': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path()); + std::fprintf(cimg::output()," > Path of 'imagemagick': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path()); + std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path()); + std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path()); + std::fprintf(cimg::output()," > Path of 'wget': %s%-13s%s\n", + cimg::t_bold, + tmp, + cimg::t_normal); + + std::fprintf(cimg::output(),"\n"); + delete[] tmp; + } + + // Declare LAPACK function signatures if LAPACK support is enabled. +#ifdef cimg_use_lapack + template + inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) { + dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) { + sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); + } + + template + inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { + dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { + sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); + } + + template + inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN, + T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) { + dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN, + float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) { + sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); + } + + template + inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) { + int one = 1; + dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) { + int one = 1; + sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); + } + + template + inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { + dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { + ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); + } + + template + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA, + T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) { + dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + + inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA, + float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) { + sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO); + } + +#endif + + } // namespace cimg { ... + + /*------------------------------------------------ + # + # + # Definition of mathematical operators and + # external functions. + # + # + -------------------------------------------------*/ + +#define _cimg_create_operator(typ) \ + template \ + inline CImg::type> operator+(const typ val, const CImg& img) { \ + return img + val; \ + } \ + template \ + inline CImg::type> operator-(const typ val, const CImg& img) { \ + typedef typename cimg::superset::type Tt; \ + return CImg(img._width,img._height,img._depth,img._spectrum,val)-=img; \ + } \ + template \ + inline CImg::type> operator*(const typ val, const CImg& img) { \ + return img*val; \ + } \ + template \ + inline CImg::type> operator/(const typ val, const CImg& img) { \ + return val*img.get_invert(); \ + } \ + template \ + inline CImg::type> operator&(const typ val, const CImg& img) { \ + return img & val; \ + } \ + template \ + inline CImg::type> operator|(const typ val, const CImg& img) { \ + return img | val; \ + } \ + template \ + inline CImg::type> operator^(const typ val, const CImg& img) { \ + return img ^ val; \ + } \ + template \ + inline bool operator==(const typ val, const CImg& img) { \ + return img == val; \ + } \ + template \ + inline bool operator!=(const typ val, const CImg& img) { \ + return img != val; \ + } + + _cimg_create_operator(bool) + _cimg_create_operator(unsigned char) + _cimg_create_operator(char) + _cimg_create_operator(signed char) + _cimg_create_operator(unsigned short) + _cimg_create_operator(short) + _cimg_create_operator(unsigned int) + _cimg_create_operator(int) + _cimg_create_operator(cimg_uint64) + _cimg_create_operator(cimg_int64) + _cimg_create_operator(float) + _cimg_create_operator(double) + _cimg_create_operator(long double) + + template + inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg& img) { + return img + expression; + } + + template + inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg& img) { + return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img; + } + + template + inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg& img) { + return img*expression; + } + + template + inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg& img) { + return expression*img.get_invert(); + } + + template + inline CImg operator&(const char *const expression, const CImg& img) { + return img & expression; + } + + template + inline CImg operator|(const char *const expression, const CImg& img) { + return img | expression; + } + + template + inline CImg operator^(const char *const expression, const CImg& img) { + return img ^ expression; + } + + template + inline bool operator==(const char *const expression, const CImg& img) { + return img==expression; + } + + template + inline bool operator!=(const char *const expression, const CImg& img) { + return img!=expression; + } + + template + inline CImg transpose(const CImg& instance) { + return instance.get_transpose(); + } + + template + inline CImg<_cimg_Tfloat> invert(const CImg& instance, const bool use_LU=true) { + return instance.get_invert(use_LU); + } + + template + inline CImg<_cimg_Tfloat> pseudoinvert(const CImg& instance, const bool use_LU=false) { + return instance.get_pseudoinvert(use_LU); + } + +#define _cimg_create_pointwise_function(name) \ + template \ + inline CImg<_cimg_Tfloat> name(const CImg& instance) { \ + return instance.get_##name(); \ + } + + _cimg_create_pointwise_function(sqr) + _cimg_create_pointwise_function(sqrt) + _cimg_create_pointwise_function(exp) + _cimg_create_pointwise_function(log) + _cimg_create_pointwise_function(log2) + _cimg_create_pointwise_function(log10) + _cimg_create_pointwise_function(abs) + _cimg_create_pointwise_function(sign) + _cimg_create_pointwise_function(cos) + _cimg_create_pointwise_function(sin) + _cimg_create_pointwise_function(sinc) + _cimg_create_pointwise_function(tan) + _cimg_create_pointwise_function(acos) + _cimg_create_pointwise_function(asin) + _cimg_create_pointwise_function(atan) + _cimg_create_pointwise_function(cosh) + _cimg_create_pointwise_function(sinh) + _cimg_create_pointwise_function(tanh) + _cimg_create_pointwise_function(acosh) + _cimg_create_pointwise_function(asinh) + _cimg_create_pointwise_function(atanh) + + /*----------------------------------- + # + # Define the CImgDisplay structure + # + ----------------------------------*/ + //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events). + /** + CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window + (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems). + If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter + a minimal mode where warning messages will be outputted each time the program is trying to call one of the + CImgDisplay method. + + The configuration variable \c cimg_display tells about the graphic library used. + It is set automatically by \CImg when one of these graphic libraries has been detected. + But, you can override its value if necessary. Valid choices are: + - 0: Disable display capabilities. + - 1: Use \b X-Window (X11) library. + - 2: Use \b GDI32 library. + + Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay. + **/ + struct CImgDisplay { + cimg_uint64 _timer, _fps_frames, _fps_timer; + unsigned int _width, _height, _normalization; + float _fps_fps, _min, _max; + bool _is_fullscreen; + char *_title; + unsigned int _window_width, _window_height, _button, *_keys, *_released_keys; + int _window_x, _window_y, _mouse_x, _mouse_y, _wheel; + bool _is_closed, _is_resized, _is_moved, _is_event, + _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7, + _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2, + _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0, + _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE, + _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE, + _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG, + _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX, + _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT, + _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT, + _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3, + _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB, + _is_keyPADMUL, _is_keyPADDIV; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- + +#ifdef cimgdisplay_plugin +#include cimgdisplay_plugin +#endif +#ifdef cimgdisplay_plugin1 +#include cimgdisplay_plugin1 +#endif +#ifdef cimgdisplay_plugin2 +#include cimgdisplay_plugin2 +#endif +#ifdef cimgdisplay_plugin3 +#include cimgdisplay_plugin3 +#endif +#ifdef cimgdisplay_plugin4 +#include cimgdisplay_plugin4 +#endif +#ifdef cimgdisplay_plugin5 +#include cimgdisplay_plugin5 +#endif +#ifdef cimgdisplay_plugin6 +#include cimgdisplay_plugin6 +#endif +#ifdef cimgdisplay_plugin7 +#include cimgdisplay_plugin7 +#endif +#ifdef cimgdisplay_plugin8 +#include cimgdisplay_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + \note If the associated window is visible on the screen, it is closed by the call to the destructor. + **/ + ~CImgDisplay() { + assign(); + delete[] _keys; + delete[] _released_keys; + } + + //! Construct an empty display. + /** + \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until + display of valid data is performed. + \par Example + \code + CImgDisplay disp; // Does actually nothing + ... + disp.display(img); // Construct new window and display image in it + \endcode + **/ + CImgDisplay(): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(); + } + + //! Construct a display with specified dimensions. + /** \param width Window width. + \param height Window height. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note A black background is initially displayed on the associated window. + **/ + CImgDisplay(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(width,height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image. + /** \param img Image used as a model to create the window. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note The pixels of the input image are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(img,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list. + /** \param list The images list to display. + \param title Window title. + \param normalization Normalization type + (0=none, 1=always, 2=once, 3=pixel type-dependent, see normalization()). + \param is_fullscreen Tells if fullscreen mode is enabled. + \param is_closed Tells if associated window is initially visible or not. + \note All images of the list, appended along the X-axis, are initially displayed on the associated window. + **/ + template + explicit CImgDisplay(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(list,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of an existing one. + /** + \param disp Display instance to copy. + \note The pixel buffer of the input window is initially displayed on the associated window. + **/ + CImgDisplay(const CImgDisplay& disp): + _width(0),_height(0),_normalization(0), + _min(0),_max(0), + _is_fullscreen(false), + _title(0), + _window_width(0),_window_height(0),_button(0), + _keys(new unsigned int[128]),_released_keys(new unsigned int[128]), + _window_x(cimg::type::min()),_window_y(cimg::type::min()), + _mouse_x(-1),_mouse_y(-1),_wheel(0), + _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) { + assign(disp); + } + + //! Take a screenshot. + /** + \param[out] img Output screenshot. Can be empty on input + **/ + template + static void screenshot(CImg& img) { + return screenshot(0,0,cimg::type::max(),cimg::type::max(),img); + } + +#if cimg_display==0 + + static void _no_display_exception() { + throw CImgDisplayException("CImgDisplay(): No display available."); + } + + //! Destructor - Empty constructor \inplace. + /** + \note Replace the current instance by an empty display. + **/ + CImgDisplay& assign() { + return flush(); + } + + //! Construct a display with specified dimensions \inplace. + /** + **/ + CImgDisplay& assign(const unsigned int width, const unsigned int height, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + cimg::unused(width,height,title,normalization,is_fullscreen,is_closed); + _no_display_exception(); + return assign(); + } + + //! Construct a display from an image \inplace. + /** + **/ + template + CImgDisplay& assign(const CImg& img, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display from an image list \inplace. + /** + **/ + template + CImgDisplay& assign(const CImgList& list, + const char *const title=0, const unsigned int normalization=3, + const bool is_fullscreen=false, const bool is_closed=false) { + _no_display_exception(); + return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed); + } + + //! Construct a display as a copy of another one \inplace. + /** + **/ + CImgDisplay& assign(const CImgDisplay &disp) { + _no_display_exception(); + return assign(disp._width,disp._height); + } + +#endif + + //! Return a reference to an empty display. + /** + \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&) + must have a default value. + \par Example + \code + void foo(CImgDisplay& disp=CImgDisplay::empty()); + \endcode + **/ + static CImgDisplay& empty() { + static CImgDisplay _empty; + return _empty.assign(); + } + + //! Return a reference to an empty display \const. + static const CImgDisplay& const_empty() { + static const CImgDisplay _empty; + return _empty; + } + +#define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \ + CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true) + static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz, + const int dmin, const int dmax, const bool return_y) { + const int + u = CImgDisplay::screen_width(), + v = CImgDisplay::screen_height(); + const float + mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin, + mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin, + Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax, + Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax; + float + w = (float)std::max(1U,dx), + h = (float)std::max(1U,dy); + if (dz>1) { w+=dz; h+=dz; } + if (wMw) { h = h*Mw/w; w = Mw; } + if (h>Mh) { w = w*Mh/h; h = Mh; } + if (wdisp = img is equivalent to disp.display(img). + **/ + template + CImgDisplay& operator=(const CImg& img) { + return display(img); + } + + //! Display list of images on associated window. + /** + \note disp = list is equivalent to disp.display(list). + **/ + template + CImgDisplay& operator=(const CImgList& list) { + return display(list); + } + + //! Construct a display as a copy of another one \inplace. + /** + \note Equivalent to assign(const CImgDisplay&). + **/ + CImgDisplay& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return \c false if display is empty, \c true otherwise. + /** + \note if (disp) { ... } is equivalent to if (!disp.is_empty()) { ... }. + **/ + operator bool() const { + return !is_empty(); + } + + //@} + //------------------------------------------ + // + //! \name Instance Checking + //@{ + //------------------------------------------ + + //! Return \c true if display is empty, \c false otherwise. + /** + **/ + bool is_empty() const { + return !(_width && _height); + } + + //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise. + /** + \note + - When a user physically closes the associated window, the display is set to closed. + - A closed display is not destroyed. Its associated window can be show again on the screen using show(). + **/ + bool is_closed() const { + return _is_closed; + } + + //! Return \c true if associated window has been resized on the screen, \c false otherwise. + /** + **/ + bool is_resized() const { + return _is_resized; + } + + //! Return \c true if associated window has been moved on the screen, \c false otherwise. + /** + **/ + bool is_moved() const { + return _is_moved; + } + + //! Return \c true if any event has occurred on the associated window, \c false otherwise. + /** + **/ + bool is_event() const { + return _is_event; + } + + //! Return \c true if current display is in fullscreen mode, \c false otherwise. + /** + **/ + bool is_fullscreen() const { + return _is_fullscreen; + } + + //! Return \c true if any key is being pressed on the associated window, \c false otherwise. + /** + \note The methods below do the same only for specific keys. + **/ + bool is_key() const { + return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 || + _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 || + _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 || + _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 || + _is_key3 || _is_key4 || _is_key5 || _is_key6 || + _is_key7 || _is_key8 || _is_key9 || _is_key0 || + _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME || + _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW || + _is_keyE || _is_keyR || _is_keyT || _is_keyY || + _is_keyU || _is_keyI || _is_keyO || _is_keyP || + _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN || + _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD || + _is_keyF || _is_keyG || _is_keyH || _is_keyJ || + _is_keyK || _is_keyL || _is_keyENTER || + _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC || + _is_keyV || _is_keyB || _is_keyN || _is_keyM || + _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT || + _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR || + _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT || + _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT || + _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 || + _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 || + _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 || + _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB || + _is_keyPADMUL || _is_keyPADDIV; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode Keycode to test. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())' + disp.wait(); + } + \endcode + **/ + bool is_key(const unsigned int keycode) const { +#define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k; + _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3); + _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7); + _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11); + _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2); + _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6); + _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0); + _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME); + _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W); + _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y); + _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P); + _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN); + _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D); + _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J); + _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER); + _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C); + _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M); + _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT); + _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR); + _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT); + _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT); + _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2); + _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5); + _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8); + _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB); + _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV); + return false; + } + + //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise. + /** + \param keycode C-string containing the keycode label of the key to test. + \note Use it when the key you want to test can be dynamically set by the user. + \par Example + \code + CImgDisplay disp(400,400); + const char *const keycode = "TAB"; + while (!disp.is_closed()) { + if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())' + disp.wait(); + } + \endcode + **/ + bool& is_key(const char *const keycode) { + static bool f = false; + f = false; +#define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k; + _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3); + _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7); + _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11); + _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2); + _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6); + _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0); + _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME); + _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W); + _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y); + _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P); + _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN); + _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D); + _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J); + _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER); + _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C); + _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M); + _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT); + _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR); + _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT); + _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT); + _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2); + _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5); + _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8); + _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB); + _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV); + return f; + } + + //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise. + /** + \param keycodes_sequence Buffer of keycodes to test. + \param length Number of keys in the \c keycodes_sequence buffer. + \param remove_sequence Tells if the key sequence must be removed from the key history, if found. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + CImgDisplay disp(400,400); + const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD }; + while (!disp.is_closed()) { + if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event + disp.wait(); + } + \endcode + **/ + bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, + const bool remove_sequence=false) { + if (keycodes_sequence && length) { + const unsigned int + *const ps_end = keycodes_sequence + length - 1, + *const pk_end = (unsigned int*)_keys + 1 + 128 - length, + k = *ps_end; + for (unsigned int *pk = (unsigned int*)_keys; pk[0,255]. + If the range of values of the data to display is different, a normalization may be required for displaying + the data in a correct way. The normalization type can be one of: + - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the + CImgDisplay instance have values in range [0,255]. + - \c 1: Value normalization is always performed (this is the default behavior). + Before displaying an input image, its values will be (virtually) stretched + in range [0,255], so that the contrast of the displayed pixels will be maximum. + Use this mode for images whose minimum and maximum values are not prescribed to known values + (e.g. float-valued images). + Note that when normalized versions of images are computed for display purposes, the actual values of these + images are not modified. + - \c 2: Value normalization is performed once (on the first image display), then the same normalization + coefficients are kept for next displayed frames. + - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types, + the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then + for unsigned char). + For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image + data instead. + **/ + unsigned int normalization() const { + return _normalization; + } + + //! Return title of the associated window as a C-string. + /** + \note Window title may be not visible, depending on the used window manager or if the current display is + in fullscreen mode. + **/ + const char *title() const { + return _title?_title:""; + } + + //! Return width of the associated window. + /** + \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual width of the associated window. + **/ + int window_width() const { + return (int)_window_width; + } + + //! Return height of the associated window. + /** + \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance) + may be different from the actual height of the associated window. + **/ + int window_height() const { + return (int)_window_height; + } + + //! Return X-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_x() const { + return _window_x; + } + + //! Return Y-coordinate of the associated window. + /** + \note The returned coordinate corresponds to the location of the upper-left corner of the associated window. + **/ + int window_y() const { + return _window_y; + } + + //! Return X-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,width()-1]. + **/ + int mouse_x() const { + return _mouse_x; + } + + //! Return Y-coordinate of the mouse pointer. + /** + \note + - If the mouse pointer is outside window area, \c -1 is returned. + - Otherwise, the returned value is in the range [0,height()-1]. + **/ + int mouse_y() const { + return _mouse_y; + } + + //! Return current state of the mouse buttons. + /** + \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned + value is set: + - bit \c 0 (value \c 0x1): State of the left mouse button. + - bit \c 1 (value \c 0x2): State of the right mouse button. + - bit \c 2 (value \c 0x4): State of the middle mouse button. + + Several bits can be activated if more than one button are pressed at the same time. + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.button()&1) { // Left button clicked + ... + } + if (disp.button()&2) { // Right button clicked + ... + } + if (disp.button()&4) { // Middle button clicked + ... + } + disp.wait(); + } + \endcode + **/ + unsigned int button() const { + return _button; + } + + //! Return current state of the mouse wheel. + /** + \note + - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled + forward or backward. + - Scrolling the wheel forward add \c 1 to the wheel value. + - Scrolling the wheel backward subtract \c 1 to the wheel value. + - The returned value cumulates the number of forward of backward scrolls since the creation of the display, + or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset + the wheel counter when an action has been performed regarding the current wheel value. + Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done + (as many in forward as in backward directions). + \par Example + \code + CImgDisplay disp(400,400); + while (!disp.is_closed()) { + if (disp.wheel()) { + int counter = disp.wheel(); // Read the state of the mouse wheel + ... // Do what you want with 'counter' + disp.set_wheel(); // Reset the wheel value to 0 + } + disp.wait(); + } + \endcode + **/ + int wheel() const { + return _wheel; + } + + //! Return one entry from the pressed keys history. + /** + \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry). + \return Keycode of a pressed key or \c 0 for a released key. + \note + - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed, + its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead. + This means that up to the 64 last pressed keys may be read from the pressed keys history. + When a new value is stored, the pressed keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int& key(const unsigned int pos=0) const { + static unsigned int key0; + return pos<128?_keys[pos]:(key0 = 0); + + } + + //! Return one entry from the released keys history. + /** + \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry). + \return Keycode of a released key or \c 0 for a pressed key. + \note + - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released, + its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead. + This means that up to the 64 last released keys may be read from the released keys history. + When a new value is stored, the released keys history is shifted so that the latest entry is always + stored at position \c 0. + - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + unsigned int& released_key(const unsigned int pos=0) const { + static unsigned int key0; + return pos<128?_released_keys[pos]:(key0 = 0); + } + + //! Return keycode corresponding to the specified string. + /** + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + \par Example + \code + const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB + \endcode + **/ + static unsigned int keycode(const char *const keycode) { +#define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k; + _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3); + _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7); + _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11); + _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2); + _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6); + _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0); + _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME); + _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W); + _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y); + _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P); + _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN); + _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D); + _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J); + _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER); + _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C); + _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M); + _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT); + _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR); + _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT); + _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT); + _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2); + _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5); + _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8); + _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB); + _cimg_keycode(PADMUL); _cimg_keycode(PADDIV); + return 0; + } + + //! Return the current refresh rate, in frames per second. + /** + \note Returns a significant value when the current instance is used to display successive frames. + It measures the delay between successive calls to frames_per_second(). + **/ + float frames_per_second() { + if (!_fps_timer) _fps_timer = cimg::time(); + const float delta = (float)((cimg::time() - _fps_timer)/1000.f); + ++_fps_frames; + if (delta>=1) { + _fps_fps = _fps_frames/delta; + _fps_frames = 0; + _fps_timer = cimg::time(); + } + return _fps_fps; + } + + // Move current display window so that its content stays inside the current screen. + CImgDisplay& move_inside_screen() { + if (is_empty()) return *this; + const int + x0 = window_x(), + y0 = window_y(), + x1 = x0 + window_width() - 1, + y1 = y0 + window_height() - 1, + sw = CImgDisplay::screen_width(), + sh = CImgDisplay::screen_height(); + if (x0<0 || y0<0 || x1>=sw || y1>=sh) + move(std::max(0,std::min(x0,sw - x1 + x0)), + std::max(0,std::min(y0,sh - y1 + y0))); + return *this; + } + + //@} + //--------------------------------------- + // + //! \name Window Manipulation + //@{ + //--------------------------------------- + +#if cimg_display==0 + + //! Display image on associated window. + /** + \param img Input image to display. + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImg& img) { + return assign(img); + } + +#endif + + //! Display list of images on associated window. + /** + \param list List of images to display. + \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c). + \param align Relative position of aligned images when displaying lists with images of different sizes + (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right). + \note This method returns immediately. + **/ + template + CImgDisplay& display(const CImgList& list, const char axis='x', const float align=0) { + if (list._width==1) { + const CImg& img = list[0]; + if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img); + } + CImgList::ucharT> visu(list._width); + unsigned int dims = 0; + cimglist_for(list,l) { + const CImg& img = list._data[l]; + img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2).move_to(visu[l]); + dims = std::max(dims,visu[l]._spectrum); + } + cimglist_for(list,l) if (visu[l]._spectrumimg.width() become equal, as well as height() and + img.height(). + - The associated window is also resized to specified dimensions. + **/ + template + CImgDisplay& resize(const CImg& img, const bool force_redraw=true) { + return resize(img._width,img._height,force_redraw); + } + + //! Resize display to the size of another CImgDisplay instance. + /** + \param disp Input display to take size from. + \param force_redraw Tells if the previous window content must be resized and updated as well. + \note + - Calling this method ensures that width() and disp.width() become equal, as well as height() and + disp.height(). + - The associated window is also resized to specified dimensions. + **/ + CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) { + return resize(disp.width(),disp.height(),force_redraw); + } + + // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs). + template + static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, + t *ptrd, const unsigned int wd, const unsigned int hd) { + typedef typename cimg::last::type ulongT; + const ulongT one = (ulongT)1; + CImg off_x(wd), off_y(hd + 1); + if (wd==ws) off_x.fill(1); + else { + ulongT *poff_x = off_x._data, curr = 0; + for (unsigned int x = 0; xstd::printf(). + \warning As the first argument is a format string, it is highly recommended to write + \code + disp.set_title("%s",window_title); + \endcode + instead of + \code + disp.set_title(window_title); + \endcode + if \c window_title can be arbitrary, to prevent nasty memory access. + **/ + CImgDisplay& set_title(const char *const format, ...) { + return assign(0,0,format); + } + +#endif + + //! Enable or disable fullscreen mode. + /** + \param is_fullscreen Tells is the fullscreen mode must be activated or not. + \param force_redraw Tells if the previous window content must be displayed as well. + \note + - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the + current display is not modified. + - The screen resolution may be switched to fit the associated window size and ensure it appears the largest + as possible. + For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen + resolution change (requires the X11 extensions to be enabled). + **/ + CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) { + if (is_empty() || _is_fullscreen==is_fullscreen) return *this; + return toggle_fullscreen(force_redraw); + } + +#if cimg_display==0 + + //! Toggle fullscreen mode. + /** + \param force_redraw Tells if the previous window content must be displayed as well. + \note Enable fullscreen mode if it was not enabled, and disable it otherwise. + **/ + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + return assign(_width,_height,0,3,force_redraw); + } + + //! Show mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& show_mouse() { + return assign(); + } + + //! Hide mouse pointer. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& hide_mouse() { + return assign(); + } + + //! Move mouse pointer to a specified location. + /** + \note Depending on the window manager behavior, this method may not succeed + (no exceptions are thrown nevertheless). + **/ + CImgDisplay& set_mouse(const int pos_x, const int pos_y) { + return assign(pos_x,pos_y); + } + +#endif + + //! Simulate a mouse button release event. + /** + \note All mouse buttons are considered released at the same time. + **/ + CImgDisplay& set_button() { + _button = 0; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a mouse button press or release event. + /** + \param button Buttons event code, where each button is associated to a single bit. + \param is_pressed Tells if the mouse button is considered as pressed or released. + **/ + CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) { + const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U; + if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode; + _is_event = buttoncode?true:false; + if (buttoncode) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all mouse wheel events. + /** + \note Make wheel() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_wheel() { + _wheel = 0; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a wheel event. + /** + \param amplitude Amplitude of the wheel scrolling to simulate. + \note Make wheel() to return \c amplitude, if called afterwards. + **/ + CImgDisplay& set_wheel(const int amplitude) { + _wheel+=amplitude; + _is_event = amplitude?true:false; + if (amplitude) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all key events. + /** + \note Make key() to return \c 0, if called afterwards. + **/ + CImgDisplay& set_key() { + std::memset((void*)_keys,0,128*sizeof(unsigned int)); + std::memset((void*)_released_keys,0,128*sizeof(unsigned int)); + _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = + _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = + _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = + _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = + _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = + _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL = + _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN = + _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = + _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = + _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = + _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL = + _is_keyPADDIV = false; + _is_event = true; +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + return *this; + } + + //! Simulate a keyboard press/release event. + /** + \param keycode Keycode of the associated key. + \param is_pressed Tells if the key is considered as pressed or released. + \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure + your code stay portable (see cimg::keyESC). + **/ + CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) { +#define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed; + _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3); + _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7); + _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11); + _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2); + _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6); + _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0); + _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME); + _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W); + _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y); + _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P); + _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN); + _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D); + _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J); + _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER); + _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C); + _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M); + _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT); + _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR); + _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT); + _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT); + _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2); + _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5); + _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8); + _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB); + _cimg_set_key(PADMUL); _cimg_set_key(PADDIV); + if (is_pressed) { + if (*_keys) + std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); + *_keys = keycode; + if (*_released_keys) { + std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); + *_released_keys = 0; + } + } else { + if (*_keys) { + std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int)); + *_keys = 0; + } + if (*_released_keys) + std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int)); + *_released_keys = keycode; + } + _is_event = keycode?true:false; + if (keycode) { +#if cimg_display==1 + pthread_cond_broadcast(&cimg::X11_attr().wait_event); +#elif cimg_display==2 + SetEvent(cimg::Win32_attr().wait_event); +#endif + } + return *this; + } + + //! Flush all display events. + /** + \note Remove all passed events from the current display. + **/ + CImgDisplay& flush() { + set_key().set_button().set_wheel(); + _is_resized = _is_moved = _is_event = false; + _fps_timer = _fps_frames = _timer = 0; + _fps_fps = 0; + return *this; + } + + //! Wait for any user event occurring on the current display. + CImgDisplay& wait() { + wait(*this); + return *this; + } + + //! Wait for a given number of milliseconds since the last call to wait(). + /** + \param milliseconds Number of milliseconds to wait for. + \note Similar to cimg::wait(). + **/ + CImgDisplay& wait(const unsigned int milliseconds) { + cimg::wait(milliseconds,&_timer); + return *this; + } + + //! Wait for any event occurring on the display \c disp1. + static void wait(CImgDisplay& disp1) { + disp1._is_event = false; + while (!disp1._is_closed && !disp1._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1 or \c disp2. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { + disp1._is_event = disp2._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed) && + !disp1._is_event && !disp2._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { + disp1._is_event = disp2._is_event = disp3._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, + CImgDisplay& disp5) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) + wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all(); + } + + //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10. + static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5, + CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, + CImgDisplay& disp10) { + disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = + disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false; + while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed || + !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) && + !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event && + !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) + wait_all(); + } + +#if cimg_display==0 + + //! Wait for any window event occurring in any opened CImgDisplay. + static void wait_all() { + return _no_display_exception(); + } + + //! Render image into internal display buffer. + /** + \param img Input image data to render. + \note + - Convert image data representation into the internal display buffer (architecture-dependent structure). + - The content of the associated window is not modified, until paint() is called. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + template + CImgDisplay& render(const CImg& img) { + return assign(img); + } + + //! Paint internal display buffer on associated window. + /** + \note + - Update the content of the associated window with the internal display buffer, e.g. after a render() call. + - Should not be used for common CImgDisplay uses, since display() is more useful. + **/ + CImgDisplay& paint() { + return assign(); + } + + + //! Take a snapshot of the current screen content. + /** + \param x0 X-coordinate of the upper left corner. + \param y0 Y-coordinate of the upper left corner. + \param x1 X-coordinate of the lower right corner. + \param y1 Y-coordinate of the lower right corner. + \param[out] img Output screenshot. Can be empty on input + **/ + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + cimg::unused(x0,y0,x1,y1,&img); + _no_display_exception(); + } + + //! Take a snapshot of the associated window content. + /** + \param[out] img Output snapshot. Can be empty on input. + **/ + template + const CImgDisplay& snapshot(CImg& img) const { + cimg::unused(img); + _no_display_exception(); + return *this; + } +#endif + + // X11-based implementation + //-------------------------- +#if cimg_display==1 + + Atom _wm_window_atom, _wm_protocol_atom; + Window _window, _background_window; + Colormap _colormap; + XImage *_image; + void *_data; + +#ifdef cimg_use_xshm + XShmSegmentInfo *_shminfo; +#endif + + static int screen_width() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display."); + res = DisplayWidth(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width; + else res = DisplayWidth(dpy,DefaultScreen(dpy)); +#else + res = DisplayWidth(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static int screen_height() { + Display *const dpy = cimg::X11_attr().display; + int res = 0; + if (!dpy) { + Display *const _dpy = XOpenDisplay(0); + if (!_dpy) + throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display."); + res = DisplayHeight(_dpy,DefaultScreen(_dpy)); + XCloseDisplay(_dpy); + } else { + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) + res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height; + else res = DisplayHeight(dpy,DefaultScreen(dpy)); +#else + res = DisplayHeight(dpy,DefaultScreen(dpy)); +#endif + } + return res; + } + + static void wait_all() { + if (!cimg::X11_attr().display) return; + pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex); + pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex); + pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex); + } + + void _handle_events(const XEvent *const pevent) { + Display *const dpy = cimg::X11_attr().display; + XEvent event = *pevent; + switch (event.type) { + case ClientMessage : { + if ((int)event.xclient.message_type==(int)_wm_protocol_atom && + (int)event.xclient.data.l[0]==(int)_wm_window_atom) { + XUnmapWindow(cimg::X11_attr().display,_window); + _is_closed = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + } break; + case ConfigureNotify : { + while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {} + const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height; + const int nx = event.xconfigure.x, ny = event.xconfigure.y; + if (nw && nh && (nw!=_window_width || nh!=_window_height)) { + _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1; + XResizeWindow(dpy,_window,_window_width,_window_height); + _is_resized = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + if (nx!=_window_x || ny!=_window_y) { + _window_x = nx; + _window_y = ny; + _is_moved = _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } + } break; + case Expose : { + while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {} + _paint(false); + if (_is_fullscreen) { + XWindowAttributes attr; + do { + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + XSetInputFocus(dpy,_window,RevertToParent,CurrentTime); + } + } break; + case ButtonPress : { + do { + _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1); break; + case 3 : set_button(2); break; + case 2 : set_button(3); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event)); + } break; + case ButtonRelease : { + do { + _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + switch (event.xbutton.button) { + case 1 : set_button(1,false); break; + case 3 : set_button(2,false); break; + case 2 : set_button(3,false); break; + case 4 : set_wheel(1); break; + case 5 : set_wheel(-1); break; + } + } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event)); + } break; + case KeyPress : { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,true); + } break; + case KeyRelease : { + char keys_return[32]; // Check that the key has been physically unpressed + XQueryKeymap(dpy,keys_return); + const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8; + const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1; + if (!is_key_pressed) { + char tmp = 0; KeySym ksym; + XLookupString(&event.xkey,&tmp,1,&ksym,0); + set_key((unsigned int)ksym,false); + } + } break; + case EnterNotify: { + while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + } break; + case LeaveNotify : { + while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {} + _mouse_x = _mouse_y = -1; _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } break; + case MotionNotify : { + while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {} + _mouse_x = event.xmotion.x; + _mouse_y = event.xmotion.y; + if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1; + _is_event = true; + pthread_cond_broadcast(&cimg::X11_attr().wait_event); + } break; + } + } + + static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows + Display *const dpy = cimg::X11_attr().display; + XEvent event; + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); + if (!arg) for ( ; ; ) { + cimg_lock_display(); + bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event); + if (!event_flag) event_flag = XCheckMaskEvent(dpy, + ExposureMask | StructureNotifyMask | ButtonPressMask | + KeyPressMask | PointerMotionMask | EnterWindowMask | + LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event); + if (event_flag) + for (unsigned int i = 0; i_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) + cimg::X11_attr().wins[i]->_handle_events(&event); + cimg_unlock_display(); + pthread_testcancel(); + cimg::sleep(8); + } + return 0; + } + + void _set_colormap(Colormap& cmap, const unsigned int dim) { + XColor *const colormap = new XColor[256]; + switch (dim) { + case 1 : { // colormap for greyscale images + for (unsigned int index = 0; index<256; ++index) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8); + colormap[index].flags = DoRed | DoGreen | DoBlue; + } + } break; + case 2 : { // colormap for RG images + for (unsigned int index = 0, r = 8; r<256; r+=16) + for (unsigned int g = 8; g<256; g+=16) { + colormap[index].pixel = index; + colormap[index].red = colormap[index].blue = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } break; + default : { // colormap for RGB images + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + colormap[index].pixel = index; + colormap[index].red = (unsigned short)(r<<8); + colormap[index].green = (unsigned short)(g<<8); + colormap[index].blue = (unsigned short)(b<<8); + colormap[index++].flags = DoRed | DoGreen | DoBlue; + } + } + } + XStoreColors(cimg::X11_attr().display,cmap,colormap,256); + delete[] colormap; + } + + void _map_window() { + Display *const dpy = cimg::X11_attr().display; + bool is_exposed = false, is_mapped = false; + XWindowAttributes attr; + XEvent event; + XMapRaised(dpy,_window); + do { // Wait for the window to be mapped + XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event); + switch (event.type) { + case MapNotify : is_mapped = true; break; + case Expose : is_exposed = true; break; + } + } while (!is_exposed || !is_mapped); + do { // Wait for the window to be visible + XGetWindowAttributes(dpy,_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + _window_x = attr.x; + _window_y = attr.y; + } + + void _paint(const bool wait_expose=true) { + if (_is_closed || !_image) return; + Display *const dpy = cimg::X11_attr().display; + if (wait_expose) { // Send an expose event sticked to display window to force repaint + XEvent event; + event.xexpose.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = 1; + event.xexpose.display = dpy; + event.xexpose.window = _window; + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = width(); + event.xexpose.height = height(); + event.xexpose.count = 0; + XSendEvent(dpy,_window,0,0,&event); + } else { // Repaint directly (may be called from the expose event) + GC gc = DefaultGC(dpy,DefaultScreen(dpy)); + +#ifdef cimg_use_xshm + if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1); + else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#else + XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height); +#endif + } + } + + template + void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) { + Display *const dpy = cimg::X11_attr().display; + cimg::unused(pixel_type); + +#ifdef cimg_use_xshm + if (_shminfo) { + XShmSegmentInfo *const nshminfo = new XShmSegmentInfo; + XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); + if (!nimage) { delete nshminfo; return; } + else { + nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777); + if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; } + else { + nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); + if (nshminfo->shmaddr==(char*)-1) { + shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; + } else { + nshminfo->readOnly = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,nshminfo); + XFlush(dpy); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(nshminfo->shmaddr); + shmctl(nshminfo->shmid,IPC_RMID,0); + XDestroyImage(nimage); + delete nshminfo; + return; + } else { + T *const ndata = (T*)nimage->data; + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + XShmDetach(dpy,_shminfo); + XDestroyImage(_image); + shmdt(_shminfo->shmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = nshminfo; + _image = nimage; + _data = (void*)ndata; + } + } + } + } + } else +#endif + { + T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T)); + if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy); + else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); + _data = (void*)ndata; + XDestroyImage(_image); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)), + cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0); + } + } + + void _init_fullscreen() { + if (!_is_fullscreen || _is_closed) return; + Display *const dpy = cimg::X11_attr().display; + _background_window = 0; + +#ifdef cimg_use_xrandr + int foo; + if (XRRQueryExtension(dpy,&foo,&foo)) { + XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation); + if (!cimg::X11_attr().resolutions) { + cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo); + cimg::X11_attr().nb_resolutions = (unsigned int)foo; + } + if (cimg::X11_attr().resolutions) { + cimg::X11_attr().curr_resolution = 0; + for (unsigned int i = 0; i=_width && nh>=_height && + nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) && + nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height)) + cimg::X11_attr().curr_resolution = i; + } + if (cimg::X11_attr().curr_resolution>0) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy), + cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + } + } + } + if (!cimg::X11_attr().resolutions) + cimg::warn(_cimgdisplay_instance + "init_fullscreen(): Xrandr extension not supported by the X server.", + cimgdisplay_instance); +#endif + + const unsigned int sx = screen_width(), sy = screen_height(); + if (sx==_width && sy==_height) return; + XSetWindowAttributes attr_set; + + attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy)); + attr_set.override_redirect = 1; + _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0, + InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set); + XEvent event; + XSelectInput(dpy,_background_window,StructureNotifyMask); + XMapRaised(dpy,_background_window); + do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event); + while (event.type!=MapNotify); + + XWindowAttributes attr; + do { + XGetWindowAttributes(dpy,_background_window,&attr); + if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); } + } while (attr.map_state!=IsViewable); + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + Display *const dpy = cimg::X11_attr().display; + XUngrabKeyboard(dpy,CurrentTime); + +#ifdef cimg_use_xrandr + if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) { + XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy)); + XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime); + XRRFreeScreenConfigInfo(config); + XSync(dpy,0); + cimg::X11_attr().curr_resolution = 0; + } +#endif + if (_background_window) XDestroyWindow(dpy,_background_window); + _background_window = 0; + _is_fullscreen = false; + } + + static int _assign_xshm(Display *dpy, XErrorEvent *error) { + cimg::unused(dpy,error); + cimg::X11_attr().is_shm_enabled = false; + return 0; + } + + void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + cimg::mutex(14); + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous display window if existing + if (!is_empty()) assign(); + + // Open X11 display and retrieve graphical properties. + Display* &dpy = cimg::X11_attr().display; + if (!dpy) { + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Failed to open X11 display.", + cimgdisplay_instance); + + cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy)); + if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && + cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32) + throw CImgDisplayException(_cimgdisplay_instance + "assign(): Invalid %u bits screen mode detected " + "(only 8, 16, 24 and 32 bits modes are managed).", + cimgdisplay_instance, + cimg::X11_attr().nb_bits); + XVisualInfo vtemplate; + vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy))); + int nb_visuals; + XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals); + if (vinfo && vinfo->red_maskblue_mask) cimg::X11_attr().is_blue_first = true; + cimg::X11_attr().byte_order = ImageByteOrder(dpy); + XFree(vinfo); + + cimg_lock_display(); + cimg::X11_attr().events_thread = new pthread_t; + pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0); + } else cimg_lock_display(); + + // Set display variables. + _width = std::min(dimw,(unsigned int)screen_width()); + _height = std::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = cimg::type::min(); + _is_closed = closed_flag; + _title = tmp_title; + flush(); + + // Create X11 window (and LUT, if 8bits display) + if (_is_fullscreen) { + if (!_is_closed) _init_fullscreen(); + const unsigned int sx = screen_width(), sy = screen_height(); + XSetWindowAttributes attr_set; + attr_set.override_redirect = 1; + _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0, + InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set); + } else + _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L); + + XSelectInput(dpy,_window, + ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask | + EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask); + + XStoreName(dpy,_window,_title?_title:" "); + if (cimg::X11_attr().nb_bits==8) { + _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll); + _set_colormap(_colormap,3); + XSetWindowColormap(dpy,_window,_colormap); + } + + static const char *const _window_class = cimg_appname; + XClassHint *const window_class = XAllocClassHint(); + window_class->res_name = (char*)_window_class; + window_class->res_class = (char*)_window_class; + XSetClassHint(dpy,_window,window_class); + XFree(window_class); + + _window_width = _width; + _window_height = _height; + + // Create XImage +#ifdef cimg_use_xshm + _shminfo = 0; + if (XShmQueryExtension(dpy)) { + _shminfo = new XShmSegmentInfo; + _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,_shminfo,_width,_height); + if (!_image) { delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777); + if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; } + else { + _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0)); + if (_shminfo->shmaddr==(char*)-1) { + shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; + } else { + _shminfo->readOnly = 0; + cimg::X11_attr().is_shm_enabled = true; + XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); + XShmAttach(dpy,_shminfo); + XSync(dpy,0); + XSetErrorHandler(oldXErrorHandler); + if (!cimg::X11_attr().is_shm_enabled) { + shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); + delete _shminfo; _shminfo = 0; + } + } + } + } + } + if (!_shminfo) +#endif + { + const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1: + (cimg::X11_attr().nb_bits==16?2:4)); + _data = std::malloc(buf_size); + _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits, + ZPixmap,0,(char*)_data,_width,_height,8,0); + } + + _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0); + _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0); + XSetWMProtocols(dpy,_window,&_wm_window_atom,1); + + if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime); + cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this; + if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type::min(); + cimg_unlock_display(); + cimg::mutex(14,0); + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + + // Remove display window from event thread list. + unsigned int i; + for (i = 0; ishmaddr); + shmctl(_shminfo->shmid,IPC_RMID,0); + delete _shminfo; + _shminfo = 0; + } +#endif + + XDestroyImage(_image); + if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap); + XDestroyWindow(dpy,_window); + XSync(dpy,0); + _window = 0; _colormap = 0; _data = 0; _image = 0; + + // Reset display variables. + delete[] _title; + _width = _height = _normalization = _window_width = _window_height = 0; + _window_x = _window_y = cimg::type::min(); + _is_fullscreen = false; + _is_closed = true; + _min = _max = 0; + _title = 0; + flush(); + + cimg_unlock_display(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + _min = _max = 0; + std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))* + (size_t)_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return render(nimg).paint(); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char): + cimg::X11_attr().nb_bits==16?sizeof(unsigned short): + sizeof(unsigned int))*(size_t)_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign(nwidth,nheight); + Display *const dpy = cimg::X11_attr().display; + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100), + tmpdimy = (nheight>0)?nheight:(-nheight*height()/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + show(); + cimg_lock_display(); + if (_window_width!=dimx || _window_height!=dimy) { + XWindowAttributes attr; + for (unsigned int i = 0; i<10; ++i) { + XResizeWindow(dpy,_window,dimx,dimy); + XGetWindowAttributes(dpy,_window,&attr); + if (attr.width==(int)dimx && attr.height==(int)dimy) break; + cimg::wait(5,&_timer); + } + } + if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) { + case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break; + default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } + } + _window_width = _width = dimx; _window_height = _height = dimy; + cimg_unlock_display(); + } + _is_resized = false; + if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const cimg_ulong buf_size = (cimg_ulong)_width*_height* + (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4)); + void *image_data = std::malloc(buf_size); + std::memcpy(image_data,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,image_data,buf_size); + std::free(image_data); + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + cimg_lock_display(); + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + _map_window(); + cimg_unlock_display(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + if (_is_fullscreen) _desinit_fullscreen(); + XUnmapWindow(dpy,_window); + _window_x = _window_y = cimg::type::min(); + _is_closed = true; + cimg_unlock_display(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + show(); + if (_window_x!=posx || _window_y!=posy) { + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XMoveWindow(dpy,_window,posx,posy); + _window_x = posx; + _window_y = posy; + cimg_unlock_display(); + } + _is_moved = false; + return paint(); + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XUndefineCursor(dpy,_window); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + static const char pix_data[8] = { 0 }; + XColor col; + col.red = col.green = col.blue = 0; + Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8); + Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0); + XFreePixmap(dpy,pix); + XDefineCursor(dpy,_window,cur); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed) return *this; + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy); + _mouse_x = posx; _mouse_y = posy; + _is_moved = false; + XSync(dpy,0); + cimg_unlock_display(); + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char *const tmp = new char[1024]; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,1024,format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + Display *const dpy = cimg::X11_attr().display; + cimg_lock_display(); + XStoreName(dpy,_window,tmp); + cimg_unlock_display(); + delete[] tmp; + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(false); + } + + CImgDisplay& paint(const bool wait_expose=true) { + if (is_empty()) return *this; + cimg_lock_display(); + _paint(wait_expose); + cimg_unlock_display(); + return *this; + } + + template + CImgDisplay& render(const CImg& img, const bool flag8=false) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2)); + if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) + return render(img.get_resize(_width,_height,1,-100,1)); + if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) { + static const CImg::ucharT> default_colormap = CImg::ucharT>::default_LUT256(); + return render(img.get_index(default_colormap,1,false)); + } + + const T + *data1 = img._data, + *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1; + + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + cimg_lock_display(); + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, no normalization + _set_colormap(_colormap,img._spectrum); + unsigned char + *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: + new unsigned char[(size_t)img._width*img._height], + *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + (*ptrd++) = (unsigned char)*(data1++); + break; + case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++), + B = (unsigned char)*(data3++); + (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); + delete[] ndata; + } + } break; + case 16 : { // 16 bits colors, no normalization + unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: + new unsigned short[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + ptrd[0] = (val&M) | (G>>3); + ptrd[1] = (G<<5) | (G>>1); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++), G = val>>2; + ptrd[0] = (G<<5) | (G>>1); + ptrd[1] = (val&M) | (G>>3); + ptrd+=2; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd[1] = (G<<5); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = (G<<5); + ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd+=2; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)*(data2++)>>2; + ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3); + ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3); + ptrd+=2; + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); + delete[] ndata; + } + } break; + default : { // 24 bits colors, no normalization + unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: + new unsigned int[(size_t)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | + (unsigned char)*(data3++); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | + ((unsigned char)*(data1++)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data1++); + ptrd[2] = 0; + ptrd[3] = 0; + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = 0; + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data2++); + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)*(data1++); + ptrd[2] = (unsigned char)*(data2++); + ptrd[3] = (unsigned char)*(data3++); + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = (unsigned char)*(data3++); + ptrd[1] = (unsigned char)*(data2++); + ptrd[2] = (unsigned char)*(data1++); + ptrd[3] = 0; + ptrd+=4; + } + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { _min = (float)cimg::type::min(); _max = (float)cimg::type::max(); } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.f); + switch (cimg::X11_attr().nb_bits) { + case 8 : { // 256 colormap, with normalization + _set_colormap(_colormap,img._spectrum); + unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data: + new unsigned char[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char R = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = R; + } break; + case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + (*ptrd++) = (R&0xf0) | (G>>4); + } break; + default : + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm), + B = (unsigned char)((*(data3++) - _min)*mm); + *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6); + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); + delete[] ndata; + } + } break; + case 16 : { // 16 bits colors, with normalization + unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data: + new unsigned short[(size_t)img._width*img._height]; + unsigned char *ptrd = (unsigned char*)ndata; + const unsigned int M = 248; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; + ptrd[0] = (val&M) | (G>>3); + ptrd[1] = (G<<5) | (val>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2; + ptrd[0] = (G<<5) | (val>>3); + ptrd[1] = (val&M) | (G>>3); + ptrd+=2; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd[1] = (G<<5); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = (G<<5); + ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd+=2; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); + ptrd+=2; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2; + ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3); + ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3); + ptrd+=2; + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); + delete[] ndata; + } + } break; + default : { // 24 bits colors, with normalization + unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data: + new unsigned int[(size_t)img._width*img._height]; + if (sizeof(int)==4) { // 32 bits int uses optimized version + unsigned int *ptrd = ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (val<<16) | (val<<8) | val; + } + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (val<<24) | (val<<16) | (val<<8); + } + break; + case 2 : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); + break; + default : + if (cimg::X11_attr().byte_order==cimg::endianness()) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data1++) - _min)*mm)<<16) | + ((unsigned char)((*(data2++) - _min)*mm)<<8) | + (unsigned char)((*(data3++) - _min)*mm); + else + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) + *(ptrd++) = + ((unsigned char)((*(data3++) - _min)*mm)<<24) | + ((unsigned char)((*(data2++) - _min)*mm)<<16) | + ((unsigned char)((*(data1++) - _min)*mm)<<8); + } + } else { + unsigned char *ptrd = (unsigned char*)ndata; + switch (img._spectrum) { + case 1 : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + ptrd[0] = 0; + ptrd[1] = val; + ptrd[2] = val; + ptrd[3] = val; + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + ptrd[0] = val; + ptrd[1] = val; + ptrd[2] = val; + ptrd[3] = 0; + ptrd+=4; + } + break; + case 2 : + if (cimg::X11_attr().byte_order) cimg::swap(data1,data2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[3] = 0; + ptrd+=4; + } + break; + default : + if (cimg::X11_attr().byte_order) + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = 0; + ptrd[1] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[3] = (unsigned char)((*(data3++) - _min)*mm); + ptrd+=4; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ptrd[0] = (unsigned char)((*(data3++) - _min)*mm); + ptrd[1] = (unsigned char)((*(data2++) - _min)*mm); + ptrd[2] = (unsigned char)((*(data1++) - _min)*mm); + ptrd[3] = 0; + ptrd+=4; + } + } + } + if (ndata!=_data) { + _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); + delete[] ndata; + } + } + } + } + cimg_unlock_display(); + return *this; + } + + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + img.assign(); + Display *dpy = cimg::X11_attr().display; + cimg_lock_display(); + if (!dpy) { + dpy = XOpenDisplay(0); + if (!dpy) + throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display."); + } + Window root = DefaultRootWindow(dpy); + XWindowAttributes gwa; + XGetWindowAttributes(dpy,root,&gwa); + const int width = gwa.width, height = gwa.height; + int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; + if (_x0>_x1) cimg::swap(_x0,_x1); + if (_y0>_y1) cimg::swap(_y0,_y1); + + XImage *image = 0; + if (_x1>=0 && _x0=0 && _y0red_mask, + green_mask = image->green_mask, + blue_mask = image->blue_mask; + img.assign(image->width,image->height,1,3); + T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2); + cimg_forXY(img,x,y) { + const unsigned long pixel = XGetPixel(image,x,y); + *(pR++) = (T)((pixel & red_mask)>>16); + *(pG++) = (T)((pixel & green_mask)>>8); + *(pB++) = (T)(pixel & blue_mask); + } + XDestroyImage(image); + } + } + if (!cimg::X11_attr().display) XCloseDisplay(dpy); + cimg_unlock_display(); + if (img.is_empty()) + throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot " + "with coordinates (%d,%d)-(%d,%d).", + x0,y0,x1,y1); + } + + template + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned char *ptrs = (unsigned char*)_data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3); + switch (cimg::X11_attr().nb_bits) { + case 8 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = *(ptrs++); + *(data1++) = (T)(val&0xe0); + *(data2++) = (T)((val&0x1c)<<3); + *(data3++) = (T)(val<<6); + } + } break; + case 16 : { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + val0 = ptrs[0], + val1 = ptrs[1]; + ptrs+=2; + *(data1++) = (T)(val0&0xf8); + *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5)); + *(data3++) = (T)(val1<<3); + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned short + val0 = ptrs[0], + val1 = ptrs[1]; + ptrs+=2; + *(data1++) = (T)(val1&0xf8); + *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5)); + *(data3++) = (T)(val0<<3); + } + } break; + default : { + if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + ++ptrs; + *(data1++) = (T)ptrs[0]; + *(data2++) = (T)ptrs[1]; + *(data3++) = (T)ptrs[2]; + ptrs+=3; + } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + *(data3++) = (T)ptrs[0]; + *(data2++) = (T)ptrs[1]; + *(data1++) = (T)ptrs[2]; + ptrs+=3; + ++ptrs; + } + } + } + return *this; + } + + // Windows-based implementation. + //------------------------------- +#elif cimg_display==2 + + bool _is_mouse_tracked, _is_cursor_visible; + HANDLE _thread, _is_created, _mutex; + HWND _window, _background_window; + CLIENTCREATESTRUCT _ccs; + unsigned int *_data; + DEVMODE _curr_mode; + BITMAPINFO _bmi; + HDC _hdc; + + static int screen_width() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return (int)mode.dmPelsWidth; + } + + static int screen_height() { + DEVMODE mode; + mode.dmSize = sizeof(DEVMODE); + mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); + return (int)mode.dmPelsHeight; + } + + static void wait_all() { + WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE); + } + + static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) { +#ifdef _WIN64 + CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); +#else + CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); +#endif + MSG st_msg; + switch (msg) { + case WM_CLOSE : + disp->_mouse_x = disp->_mouse_y = -1; + disp->_window_x = disp->_window_y = cimg::type::min(); + disp->set_button().set_key(0).set_key(0,false)._is_closed = true; + ReleaseMutex(disp->_mutex); + ShowWindow(disp->_window,SW_HIDE); + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + return 0; + case WM_SIZE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->_mutex,INFINITE); + const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); + if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) { + disp->_window_width = nw; + disp->_window_height = nh; + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_resized = disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_MOVE : { + while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {} + WaitForSingleObject(disp->_mutex,INFINITE); + const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); + if (nx!=disp->_window_x || ny!=disp->_window_y) { + disp->_window_x = nx; + disp->_window_y = ny; + disp->_is_moved = disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + } + ReleaseMutex(disp->_mutex); + } break; + case WM_PAINT : + disp->paint(); + cimg_lock_display(); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0); + cimg_unlock_display(); + break; + case WM_ERASEBKGND : + // return 0; + break; + case WM_KEYDOWN : + disp->set_key((unsigned int)wParam); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_KEYUP : + disp->set_key((unsigned int)wParam,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MOUSEMOVE : { + while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {} + disp->_mouse_x = LOWORD(lParam); + disp->_mouse_y = HIWORD(lParam); +#if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT) + if (!disp->_is_mouse_tracked) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = disp->_window; + if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true; + } +#endif + if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height()) + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_event = true; + SetEvent(cimg::Win32_attr().wait_event); + cimg_lock_display(); + if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0); + cimg_unlock_display(); + } break; + case WM_MOUSELEAVE : { + disp->_mouse_x = disp->_mouse_y = -1; + disp->_is_mouse_tracked = false; + cimg_lock_display(); + while (ShowCursor(TRUE)<0) {} + cimg_unlock_display(); + } break; + case WM_LBUTTONDOWN : + disp->set_button(1); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONDOWN : + disp->set_button(2); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONDOWN : + disp->set_button(3); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_LBUTTONUP : + disp->set_button(1,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_RBUTTONUP : + disp->set_button(2,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case WM_MBUTTONUP : + disp->set_button(3,false); + SetEvent(cimg::Win32_attr().wait_event); + break; + case 0x020A : // WM_MOUSEWHEEL: + disp->set_wheel((int)((short)HIWORD(wParam))/120); + SetEvent(cimg::Win32_attr().wait_event); + } + return DefWindowProc(window,msg,wParam,lParam); + } + + static DWORD WINAPI _events_thread(void* arg) { + CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]); + const char *const title = (const char*)(((void**)arg)[1]); + MSG msg; + delete[] (void**)arg; + disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + disp->_bmi.bmiHeader.biWidth = disp->width(); + disp->_bmi.bmiHeader.biHeight = -disp->height(); + disp->_bmi.bmiHeader.biPlanes = 1; + disp->_bmi.bmiHeader.biBitCount = 32; + disp->_bmi.bmiHeader.biCompression = BI_RGB; + disp->_bmi.bmiHeader.biSizeImage = 0; + disp->_bmi.bmiHeader.biXPelsPerMeter = 1; + disp->_bmi.bmiHeader.biYPelsPerMeter = 1; + disp->_bmi.bmiHeader.biClrUsed = 0; + disp->_bmi.bmiHeader.biClrImportant = 0; + disp->_data = new unsigned int[(size_t)disp->_width*disp->_height]; + if (!disp->_is_fullscreen) { // Normal window + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int + border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2), + border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1), + ww = disp->width() + 2*border1, + wh = disp->height() + border1 + border2, + sw = CImgDisplay::screen_width(), + sh = CImgDisplay::screen_height(); + int + wx = (int)cimg::round(cimg::rand(0,sw - ww -1)), + wy = (int)cimg::round(cimg::rand(64,sh - wh - 65)); + if (wx + ww>=sw) wx = sw - ww; + if (wy + wh>=sh) wy = sh - wh; + if (wx<0) wx = 0; + if (wy<0) wy = 0; + disp->_window = CreateWindowA("MDICLIENT",title?title:" ", + (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)), + wx,wy,ww,wh,0,0,0,&(disp->_ccs)); + if (!disp->_is_closed) { + GetWindowRect(disp->_window,&rect); + disp->_window_x = rect.left; + disp->_window_y = rect.top; + } else disp->_window_x = disp->_window_y = cimg::type::min(); + } else { // Fullscreen window + const unsigned int + sx = (unsigned int)screen_width(), + sy = (unsigned int)screen_height(); + disp->_window = CreateWindowA("MDICLIENT",title?title:" ", + (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)), + (int)(sx - disp->_width)/2, + (int)(sy - disp->_height)/2, + disp->width(),disp->height(),0,0,0,&(disp->_ccs)); + disp->_window_x = disp->_window_y = 0; + } + SetForegroundWindow(disp->_window); + disp->_hdc = GetDC(disp->_window); + disp->_window_width = disp->_width; + disp->_window_height = disp->_height; + disp->flush(); +#ifdef _WIN64 + SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp); + SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events); +#else + SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp); + SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events); +#endif + SetEvent(disp->_is_created); + while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg); + return 0; + } + + CImgDisplay& _update_window_pos() { + if (_is_closed) _window_x = _window_y = cimg::type::min(); + else { + RECT rect; + rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + GetWindowRect(_window,&rect); + _window_x = rect.left; + _window_y = rect.top; + } + return *this; + } + + void _init_fullscreen() { + _background_window = 0; + if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0; + else { + DEVMODE mode; + unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; + for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { + const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; + if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { + bestbpp = mode.dmBitsPerPel; + ibest = imode; + bw = nw; bh = nh; + } + } + if (bestbpp) { + _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0; + EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode); + EnumDisplaySettings(0,ibest,&mode); + ChangeDisplaySettings(&mode,0); + } else _curr_mode.dmSize = 0; + + const unsigned int + sx = (unsigned int)screen_width(), + sy = (unsigned int)screen_height(); + if (sx!=_width || sy!=_height) { + CLIENTCREATESTRUCT background_ccs = { 0,0 }; + _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, + 0,0,(int)sx,(int)sy,0,0,0,&background_ccs); + SetForegroundWindow(_background_window); + } + } + } + + void _desinit_fullscreen() { + if (!_is_fullscreen) return; + if (_background_window) DestroyWindow(_background_window); + _background_window = 0; + if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0); + _is_fullscreen = false; + } + + CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + + // Allocate space for window title + const char *const nptitle = ptitle?ptitle:""; + const unsigned int s = (unsigned int)std::strlen(nptitle) + 1; + char *const tmp_title = s?new char[s]:0; + if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char)); + + // Destroy previous window if existing + if (!is_empty()) assign(); + + // Set display variables + _width = std::min(dimw,(unsigned int)screen_width()); + _height = std::min(dimh,(unsigned int)screen_height()); + _normalization = normalization_type<4?normalization_type:3; + _is_fullscreen = fullscreen_flag; + _window_x = _window_y = cimg::type::min(); + _is_closed = closed_flag; + _is_cursor_visible = true; + _is_mouse_tracked = false; + _title = tmp_title; + flush(); + if (_is_fullscreen) _init_fullscreen(); + + // Create event thread + void *const arg = (void*)(new void*[2]); + ((void**)arg)[0] = (void*)this; + ((void**)arg)[1] = (void*)_title; + _mutex = CreateMutex(0,FALSE_WIN,0); + _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); + _thread = CreateThread(0,0,_events_thread,arg,0,0); + WaitForSingleObject(_is_created,INFINITE); + return *this; + } + + CImgDisplay& assign() { + if (is_empty()) return flush(); + DestroyWindow(_window); + TerminateThread(_thread,0); + delete[] _data; + delete[] _title; + _data = 0; + _title = 0; + if (_is_fullscreen) _desinit_fullscreen(); + _width = _height = _normalization = _window_width = _window_height = 0; + _window_x = _window_y = cimg::type::min(); + _is_fullscreen = false; + _is_closed = true; + _min = _max = 0; + _title = 0; + flush(); + return *this; + } + + CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!dimw || !dimh) return assign(); + _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag); + _min = _max = 0; + std::memset(_data,0,sizeof(unsigned int)*_width*_height); + return paint(); + } + + template + CImgDisplay& assign(const CImg& img, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!img) return assign(); + CImg tmp; + const CImg& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + template + CImgDisplay& assign(const CImgList& list, const char *const title=0, + const unsigned int normalization_type=3, + const bool fullscreen_flag=false, const bool closed_flag=false) { + if (!list) return assign(); + CImg tmp; + const CImg img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2, + (img._height - 1)/2, + (img._depth - 1)/2)); + _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag); + if (_normalization==2) _min = (float)nimg.min_max(_max); + return display(nimg); + } + + CImgDisplay& assign(const CImgDisplay& disp) { + if (!disp) return assign(); + _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed); + std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height); + return paint(); + } + + CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) { + if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign(); + if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight); + const unsigned int + tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100), + tmpdimy = (nheight>0)?nheight:(-nheight*_height/100), + dimx = tmpdimx?tmpdimx:1, + dimy = tmpdimy?tmpdimy:1; + if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) { + if (_window_width!=dimx || _window_height!=dimy) { + RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1; + AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); + const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1; + SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); + } + if (_width!=dimx || _height!=dimy) { + unsigned int *const ndata = new unsigned int[dimx*dimy]; + if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy); + else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); + delete[] _data; + _data = ndata; + _bmi.bmiHeader.biWidth = (LONG)dimx; + _bmi.bmiHeader.biHeight = -(int)dimy; + _width = dimx; + _height = dimy; + } + _window_width = dimx; _window_height = dimy; + show(); + } + _is_resized = false; + if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2); + if (force_redraw) return paint(); + return *this; + } + + CImgDisplay& toggle_fullscreen(const bool force_redraw=true) { + if (is_empty()) return *this; + if (force_redraw) { + const cimg_ulong buf_size = (cimg_ulong)_width*_height*4; + void *odata = std::malloc(buf_size); + if (odata) { + std::memcpy(odata,_data,buf_size); + assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + std::memcpy(_data,odata,buf_size); + std::free(odata); + } + return paint(); + } + return assign(_width,_height,_title,_normalization,!_is_fullscreen,false); + } + + CImgDisplay& show() { + if (is_empty() || !_is_closed) return *this; + _is_closed = false; + if (_is_fullscreen) _init_fullscreen(); + ShowWindow(_window,SW_SHOW); + _update_window_pos(); + return paint(); + } + + CImgDisplay& close() { + if (is_empty() || _is_closed) return *this; + _is_closed = true; + if (_is_fullscreen) _desinit_fullscreen(); + ShowWindow(_window,SW_HIDE); + _window_x = _window_y = cimg::type::min(); + return *this; + } + + CImgDisplay& move(const int posx, const int posy) { + if (is_empty()) return *this; + if (_window_x!=posx || _window_y!=posy) { + SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); + _window_x = posx; + _window_y = posy; + } + show(); + _is_moved = false; + return *this; + } + + CImgDisplay& show_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = true; + return *this; + } + + CImgDisplay& hide_mouse() { + if (is_empty()) return *this; + _is_cursor_visible = false; + return *this; + } + + CImgDisplay& set_mouse(const int posx, const int posy) { + if (is_empty() || _is_closed || posx<0 || posy<0) return *this; + if (!_is_closed) { + _update_window_pos(); + const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy); + if (res) { _mouse_x = posx; _mouse_y = posy; } + } + return *this; + } + + CImgDisplay& set_title(const char *const format, ...) { + if (is_empty()) return *this; + char *const tmp = new char[1024]; + va_list ap; + va_start(ap, format); + cimg_vsnprintf(tmp,1024,format,ap); + va_end(ap); + if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; } + delete[] _title; + const unsigned int s = (unsigned int)std::strlen(tmp) + 1; + _title = new char[s]; + std::memcpy(_title,tmp,s*sizeof(char)); + SetWindowTextA(_window, tmp); + delete[] tmp; + return *this; + } + + template + CImgDisplay& display(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "display(): Empty specified image.", + cimgdisplay_instance); + if (is_empty()) return assign(img); + return render(img).paint(); + } + + CImgDisplay& paint() { + if (_is_closed) return *this; + WaitForSingleObject(_mutex,INFINITE); + SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS); + ReleaseMutex(_mutex); + return *this; + } + + template + CImgDisplay& render(const CImg& img) { + if (!img) + throw CImgArgumentException(_cimgdisplay_instance + "render(): Empty specified image.", + cimgdisplay_instance); + + if (is_empty()) return *this; + if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2, + (img._depth - 1)/2)); + + const T + *data1 = img._data, + *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1, + *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1; + + WaitForSingleObject(_mutex,INFINITE); + unsigned int + *const ndata = (img._width==_width && img._height==_height)?_data: + new unsigned int[(size_t)img._width*img._height], + *ptrd = ndata; + + if (!_normalization || (_normalization==3 && cimg::type::string()==cimg::type::string())) { + _min = _max = 0; + switch (img._spectrum) { + case 1 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)*(data1++); + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); + } + } break; + case 2 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); + } + } break; + default : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)*(data1++), + G = (unsigned char)*(data2++), + B = (unsigned char)*(data3++); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); + } + } + } + } else { + if (_normalization==3) { + if (cimg::type::is_float()) _min = (float)img.min_max(_max); + else { + _min = (float)cimg::type::min(); + _max = (float)cimg::type::max(); + } + } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max); + const float delta = _max - _min, mm = 255/(delta?delta:1.f); + switch (img._spectrum) { + case 1 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char val = (unsigned char)((*(data1++) - _min)*mm); + *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val); + } + } break; + case 2 : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8)); + } + } break; + default : { + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned char + R = (unsigned char)((*(data1++) - _min)*mm), + G = (unsigned char)((*(data2++) - _min)*mm), + B = (unsigned char)((*(data3++) - _min)*mm); + *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B); + } + } + } + } + if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; } + ReleaseMutex(_mutex); + return *this; + } + + template + static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg& img) { + img.assign(); + HDC hScreen = GetDC(GetDesktopWindow()); + if (hScreen) { + const int + width = GetDeviceCaps(hScreen,HORZRES), + height = GetDeviceCaps(hScreen,VERTRES); + int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1; + if (_x0>_x1) cimg::swap(_x0,_x1); + if (_y0>_y1) cimg::swap(_y0,_y1); + if (_x1>=0 && _x0=0 && _y0 + const CImgDisplay& snapshot(CImg& img) const { + if (is_empty()) { img.assign(); return *this; } + const unsigned int *ptrs = _data; + img.assign(_width,_height,1,3); + T + *data1 = img.data(0,0,0,0), + *data2 = img.data(0,0,0,1), + *data3 = img.data(0,0,0,2); + for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) { + const unsigned int val = *(ptrs++); + *(data1++) = (T)(unsigned char)(val>>16); + *(data2++) = (T)(unsigned char)((val>>8)&0xFF); + *(data3++) = (T)(unsigned char)(val&0xFF); + } + return *this; + } +#endif + + //@} + }; // struct CImgDisplay { ... + + /* + #-------------------------------------- + # + # + # + # Definition of the CImg structure + # + # + # + #-------------------------------------- + */ + + //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. + /** + This is the main class of the %CImg Library. It declares and constructs + an image, allows access to its pixel values, and is able to perform various image operations. + + \par Image representation + + A %CImg image is defined as an instance of the container \c CImg, which contains a regular grid of pixels, + each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth + and number of channels. + Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), + while the number of channels is rather used as a vector-valued dimension + (it may describe the R,G,B color channels for instance). + If you need a fifth dimension, you can use image lists \c CImgList rather than simple images \c CImg. + + Thus, the \c CImg class is able to represent volumetric images of vector-valued pixels, + as well as images with less dimensions (1D scalar signal, 2D color images, ...). + Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. + + Concerning the pixel value type \c T: + fully supported template types are the basic C++ types: unsigned char, char, short, unsigned int, int, + unsigned long, long, float, double, ... . + Typically, fast image display can be done using CImg images, + while complex image processing algorithms may be rather coded using CImg or CImg + images that have floating-point pixel values. The default value for the template T is \c float. + Using your own template types may be possible. However, you will certainly have to define the complete set + of arithmetic and logical operators for your class. + + \par Image structure + + The \c CImg structure contains \e six fields: + - \c _width defines the number of \a columns of the image (size along the X-axis). + - \c _height defines the number of \a rows of the image (size along the Y-axis). + - \c _depth defines the number of \a slices of the image (size along the Z-axis). + - \c _spectrum defines the number of \a channels of the image (size along the C-axis). + - \c _data defines a \a pointer to the \a pixel \a data (of type \c T). + - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with + another image. + + You can access these fields publicly although it is recommended to use the dedicated functions + width(), height(), depth(), spectrum() and ptr() to do so. + Image dimensions are not limited to a specific range (as long as you got enough available memory). + A value of \e 1 usually means that the corresponding dimension is \a flat. + If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. + Empty images should not contain any pixel data and thus, will not be processed by CImg member functions + (a CImgInstanceException will be thrown instead). + Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). + + \par Image declaration and construction + + Declaring an image can be done by using one of the several available constructors. + Here is a list of the most used: + + - Construct images from arbitrary dimensions: + - CImg img; declares an empty image. + - CImg img(128,128); declares a 128x128 greyscale image with + \c unsigned \c char pixel values. + - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. + - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image + (colors are stored as an image with three channels). + - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image + (with \c double pixel values). + - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image + (with \c float pixels, which is the default value of the template parameter \c T). + - \b Note: images pixels are not automatically initialized to 0. You may use the function \c fill() to + do it, or use the specific constructor taking 5 parameters like this: + CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. + + - Construct images from filenames: + - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". + - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the + file "analyze.hdr". + - \b Note: You need to install ImageMagick + to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io). + + - Construct images from C-style arrays: + - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer + \c data_buffer (of size 256x256=65536). + - CImg img(data_buffer,256,256,1,3); constructs a 256x256 color image + from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). + + The complete list of constructors can be found here. + + \par Most useful functions + + The \c CImg class contains a lot of functions that operates on images. + Some of the most useful are: + + - operator()(): Read or write pixel values. + - display(): displays the image in a new window. + **/ + template + struct CImg { + + unsigned int _width, _height, _depth, _spectrum; + bool _is_shared; + T *_data; + + //! Simple iterator type, to loop through each pixel value of an image instance. + /** + \note + - The \c CImg::iterator type is defined to be a T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + CImg img("reference.jpg"); // Load image from file + // Set all pixels to '0', with a CImg iterator. + for (CImg::iterator it = img.begin(), it::const_iterator type is defined to be a \c const \c T*. + - You will seldom have to use iterators in %CImg, most classical operations + being achieved (often in a faster way) using methods of \c CImg. + \par Example + \code + const CImg img("reference.jpg"); // Load image from file + float sum = 0; + // Compute sum of all pixel values, with a CImg iterator. + for (CImg::iterator it = img.begin(), it::value_type type of a \c CImg is defined to be a \c T. + - \c CImg::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs. + static size_t safe_size(const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) { + if (!(dx && dy && dz && dc)) return 0; + size_t siz = (size_t)dx, osiz = siz; + if ((dy==1 || (siz*=dy)>osiz) && + ((osiz = siz), dz==1 || (siz*=dz)>osiz) && + ((osiz = siz), dc==1 || (siz*=dc)>osiz) && + ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) return siz; + throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.", + pixel_type(),dx,dy,dz,dc); + } + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimg_plugin +#include cimg_plugin +#endif +#ifdef cimg_plugin1 +#include cimg_plugin1 +#endif +#ifdef cimg_plugin2 +#include cimg_plugin2 +#endif +#ifdef cimg_plugin3 +#include cimg_plugin3 +#endif +#ifdef cimg_plugin4 +#include cimg_plugin4 +#endif +#ifdef cimg_plugin5 +#include cimg_plugin5 +#endif +#ifdef cimg_plugin6 +#include cimg_plugin6 +#endif +#ifdef cimg_plugin7 +#include cimg_plugin7 +#endif +#ifdef cimg_plugin8 +#include cimg_plugin8 +#endif + + //@} + //--------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //--------------------------------------------------------- + + //! Destroy image. + /** + \note + - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances. + - Destroying an empty or shared image does nothing actually. + \warning + - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image + that shares its buffer with the destroyed instance, in order to avoid further invalid memory access + (to a deallocated buffer). + **/ + ~CImg() { + if (!_is_shared) delete[] _data; + } + + //! Construct empty image. + /** + \note + - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum() + are set to \c 0, as well as its pixel buffer pointer data(). + - An empty image may be re-assigned afterwards, e.g. with the family of + assign(unsigned int,unsigned int,unsigned int,unsigned int) methods, + or by operator=(const CImg&). In all cases, the type of pixels stays \c T. + - An empty image is never shared. + \par Example + \code + CImg img1, img2; // Construct two empty images + img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image + img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1' + img2.assign(); // Re-assign 'img2' to be an empty image again + \endcode + **/ + CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {} + + //! Construct image with specified size. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \note + - It is able to create only \e non-shared images, and allocates thus a pixel buffer data() + for each constructed image instance. + - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of + an \e empty image. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. when requested size is too big for available memory). + \warning + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values during construction (e.g. with \c 0), use constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead. + \par Example + \code + CImg img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values + CImg img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0' + \endcode + **/ + explicit CImg(const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1): + _is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values. + /** + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value Initialization value. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), + but it also fills the pixel buffer with the specified \c value. + \warning + - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels + (e.g. RGB vector, for color images). + For this task, you may use fillC() after construction. + **/ + CImg(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const T& value): + _is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + fill(value); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, + with pixels of type \c T, and initialize pixel + values from the specified sequence of integers \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be an \e integer). + \param value1 Second value of the initialization sequence (must be an \e integer). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \warning + - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence. + Otherwise, the constructor may crash or fill your image pixels with garbage. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image + 0,255,0,255, // Set the 4 values for the red component + 0,0,255,255, // Set the 4 values for the green component + 64,64,64,64); // Set the 4 values for the blue component + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _CImg_stdarg(img,a0,a1,N,t) { \ + size_t _siz = (size_t)N; \ + if (_siz--) { \ + va_list ap; \ + va_start(ap,a1); \ + T *ptrd = (img)._data; \ + *(ptrd++) = (T)a0; \ + if (_siz--) { \ + *(ptrd++) = (T)a1; \ + for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ + } \ + va_end(ap); \ + } \ + } + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int); + } + +#if cimg_use_cpp11==1 + //! Construct image with specified size and initialize pixel values from an initializer list of integers. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, + with pixels of type \c T, and initialize pixel + values from the specified initializer list of integers { \c value0,\c value1,\c ... } + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param { value0, value1, ... } Initialization list + \param repeat_values Tells if the value filling process is repeated over the image. + + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with a sequence of specified integer values. + \par Example + \code + const CImg img(2,2,1,3, // Construct a 2x2 color (RGB) image + { 0,255,0,255, // Set the 4 values for the red component + 0,0,255,255, // Set the 4 values for the green component + 64,64,64,64 }); // Set the 4 values for the blue component + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + template + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { +#define _cimg_constructor_cpp11(repeat_values) \ + auto it = values.begin(); \ + size_t siz = size(); \ + if (repeat_values) for (T *ptrd = _data; siz--; ) { \ + *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \ + else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); } + assign(size_x,size_y,size_z,size_c); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, + std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, const unsigned int size_y, + std::initializer_list values, + const bool repeat_values=true): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y); + _cimg_constructor_cpp11(repeat_values); + } + + template + CImg(const unsigned int size_x, + std::initializer_list values, + const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x); + _cimg_constructor_cpp11(repeat_values); + } + + //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers. + /** + Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1, + with pixels of type \c T, and initialize pixel + values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is + given by the size of the initializer list. + \param { value0, value1, ... } Initialization list + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1, + but it also fills the pixel buffer with a sequence of specified integer values. + \par Example + \code + const CImg img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values + img.resize(150,150).display(); + \endcode + \image html ref_constructor1.jpg + **/ + template + CImg(const std::initializer_list values): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(values.size(),1,1,1); + auto it = values.begin(); + unsigned int siz = _width; + for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); + } + + template + CImg& operator=(std::initializer_list values) { + _cimg_constructor_cpp11(siz>values.size()); + return *this; + } +#endif + + //! Construct image with specified size and initialize pixel values from a sequence of doubles. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ... + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param value0 First value of the initialization sequence (must be a \e double). + \param value1 Second value of the initialization sequence (must be a \e double). + \param ... + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but + takes a sequence of double values instead of integers. + \warning + - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence. + Otherwise, the constructor may crash or fill your image with garbage. + For instance, the code below will probably crash on most platforms: + \code + const CImg img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'! + \endcode + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double); + } + + //! Construct image with specified size and initialize pixel values from a value string. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initializes pixel values from the specified string \c values. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param values Value string describing the way pixel values are set. + \param repeat_values Tells if the value filling process is repeated over the image. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills + the pixel buffer with values described in the value string \c values. + - Value string \c values may describe two different filling processes: + - Either \c values is a sequences of values assigned to the image pixels, as in "1,2,3,7,8,2". + In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence. + - Either, \c values is a formula, as in "cos(x/10)*sin(y/20)". + In this case, parameter \c repeat_values is pointless. + - For both cases, specifying \c repeat_values is mandatory. + It disambiguates the possible overloading of constructor + CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a const char*. + - A \c CImgArgumentException is thrown when an invalid value string \c values is specified. + \par Example + \code + const CImg img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence + img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula + (img1,img2).display(); + \endcode + \image html ref_constructor2.jpg + **/ + CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values):_is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + fill(values,repeat_values); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer. + /** + Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T, + and initializes pixel values from the specified \c t* memory buffer. + \param values Pointer to the input memory buffer. + \param size_x Image width(). + \param size_y Image height(). + \param size_z Image depth(). + \param size_c Image spectrum() (number of channels). + \param is_shared Tells if input memory buffer must be shared by the current instance. + \note + - If \c is_shared is \c false, the image instance allocates its own pixel buffer, + and values from the specified input buffer are copied to the instance buffer. + If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy. + - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its + own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared + image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. when requested size is too big for available memory). + \warning + - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data() + (e.g. already deallocated). + \par Example + \code + unsigned char tab[256*256] = { 0 }; + CImg img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab' + img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab' + tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1' + \endcode + **/ + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance " + "from a (%s*) buffer (pixel types are different).", + cimg_instance, + size_x,size_y,size_z,size_c,CImg::pixel_type()); + } + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + + } + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared; + if (_is_shared) _data = const_cast(values); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + std::memcpy(_data,values,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from memory buffer with specified size and pixel ordering scheme. + template + CImg(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const axes_order):_data(0),_is_shared(false) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (values && siz) { + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } + else { ++n_code[c%=4]; s_code[l] = c; } + } + if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { + const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); + int s0 = 0, s1 = 0, s2 = 0, s3 = 0; + const char *inv_order = 0; + switch (code) { + case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc + case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz + case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc + case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy + case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz + case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy + case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc + case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz + case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc + case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx + case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz + case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx + case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc + case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy + case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc + case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx + case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy + case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx + case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz + case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy + case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz + case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx + case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy + case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx + } + CImg(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this); + } else { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image from reading an image file. + /** + Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from + an image file. + \param filename Filename, as a C-string. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image + dimensions and pixel values from the specified image file. + - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system + and on the external libraries you used to link your code against. + - Considered pixel type \c T should better fit the file format specification, or data loss may occur during + file load (e.g. constructing a \c CImg from a float-valued image file). + - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not + recognized. + \par Example + \code + const CImg img("reference.jpg"); + img.display(); + \endcode + \image html ref_image.jpg + **/ + explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(filename); + } + + //! Construct image copy. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance. + \param img Input image to copy. + \note + - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the + input image \c img. + - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also + \e shared, and shares its pixel buffer with \c img. + Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img. + This behavior is needful to allow functions to return shared images. + - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input + image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and + \c t are different. + - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than + with different types. + - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated + (e.g. not enough available memory). + **/ + template + CImg(const CImg& img):_is_shared(false) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Construct image copy \specialization. + CImg(const CImg& img) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = img._is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Advanced copy constructor. + /** + Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg instance, + while forcing the shared state of the constructed copy. + \param img Input image to copy. + \param is_shared Tells about the shared state of the constructed copy. + \note + - Similar to CImg(const CImg&), except that it allows to decide the shared state of + the constructed image, which does not depend anymore on the shared state of the input image \c img: + - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img. + For that case, the pixel types \c T and \c t \e must be the same. + - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input + image \c img is shared or not. + - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t. + **/ + template + CImg(const CImg& img, const bool is_shared):_is_shared(false) { + if (is_shared) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgArgumentException(_cimg_instance + "CImg(): Invalid construction request of a shared instance from a " + "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).", + cimg_instance, + CImg::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data); + } + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + } else { _width = _height = _depth = _spectrum = 0; _data = 0; } + } + + //! Advanced copy constructor \specialization. + CImg(const CImg& img, const bool is_shared) { + const size_t siz = (size_t)img.size(); + if (img._data && siz) { + _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; + _is_shared = is_shared; + if (_is_shared) _data = const_cast(img._data); + else { + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum), + img._width,img._height,img._depth,img._spectrum); + } + std::memcpy(_data,img._data,siz*sizeof(T)); + } + } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; } + } + + //! Construct image with dimensions borrowed from another image. + /** + Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing + \c CImg instance. + \param img Input image from which dimensions are borrowed. + \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions. + \note + - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions + (\e not its pixel values) from an existing \c CImg instance. + - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values. + In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg&,const char*,T) + instead. + \par Example + \code + const CImg img1(256,128,1,3), // 'img1' is a 256x128x1x3 image + img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image + img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image + img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0') + \endcode + **/ + template + CImg(const CImg& img, const char *const dimensions): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values. + /** + Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing + \c CImg instance, and set all pixel values to specified \c value. + \param img Input image from which dimensions are borrowed. + \param dimensions String describing the image size along the X,Y,Z and V-dimensions. + \param value Value used for initialization. + \note + - Similar to CImg(const CImg&,const char*), but it also fills the pixel buffer with the specified \c value. + **/ + template + CImg(const CImg& img, const char *const dimensions, const T& value): + _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + assign(img,dimensions).fill(value); + } + + //! Construct image from a display window. + /** + Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance. + \param disp Input display window. + \note + - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay. + - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3 + (i.e. a 2D color image). + - The image pixels are read as 8-bits RGB values. + **/ + explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + disp.snapshot(*this); + } + + // Constructor and assignment operator for rvalue references (c++11). + // This avoids an additional image copy for methods returning new images. Can save RAM for big images ! +#if cimg_use_cpp11==1 + CImg(CImg&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) { + swap(img); + } + + CImg& operator=(CImg&& img) { + if (_is_shared) return assign(img); + return img.swap(*this); + } +#endif + + //! Construct empty image \inplace. + /** + In-place version of the default constructor CImg(). It simply resets the instance to an empty image. + **/ + CImg& assign() { + if (!_is_shared) delete[] _data; + _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; + return *this; + } + + //! Construct image with specified size \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!siz) return assign(); + const size_t curr_siz = (size_t)size(); + if (siz!=curr_siz) { + if (_is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from specified " + "image (%u,%u,%u,%u).", + cimg_instance, + size_x,size_y,size_z,size_c); + else { + delete[] _data; + try { _data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + } + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + return *this; + } + + //! Construct image with specified size and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const T& value) { + return assign(size_x,size_y,size_z,size_c).fill(value); + } + + //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const int value0, const int value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const double value0, const double value1, ...) { + assign(size_x,size_y,size_z,size_c); + _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a value string \inplace. + /** + In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool). + **/ + CImg& assign(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const values, const bool repeat_values) { + return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \inplace. + /** + In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int). + **/ + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + assign(size_x,size_y,size_z,size_c); + const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++); + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \specialization. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + const size_t curr_siz = (size_t)size(); + if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c); + if (_is_shared || values + siz<_data || values>=_data + size()) { + assign(size_x,size_y,size_z,size_c); + if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T)); + else std::memcpy((void*)_data,(void*)values,siz*sizeof(T)); + } else { + T *new_data = 0; + try { new_data = new T[siz]; } catch (...) { + _width = _height = _depth = _spectrum = 0; _data = 0; + throw CImgInstanceException(_cimg_instance + "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).", + cimg_instance, + cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c), + size_x,size_y,size_z,size_c); + } + std::memcpy((void*)new_data,(void*)values,siz*sizeof(T)); + delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; + } + return *this; + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + if (is_shared) + throw CImgArgumentException(_cimg_instance + "assign(): Invalid assignment request of shared instance from (%s*) buffer" + "(pixel types are different).", + cimg_instance, + CImg::pixel_type()); + return assign(values,size_x,size_y,size_z,size_c); + } + + //! Construct image with specified size and initialize pixel values from a memory buffer \overloading. + CImg& assign(const T *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, const bool is_shared) { + const size_t siz = safe_size(size_x,size_y,size_z,size_c); + if (!values || !siz) return assign(); + if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); } + else { + if (!_is_shared) { + if (values + siz<_data || values>=_data + size()) assign(); + else cimg::warn(_cimg_instance + "assign(): Shared image instance has overlapping memory.", + cimg_instance); + } + _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true; + _data = const_cast(values); + } + return *this; + } + + //! Construct image from memory buffer with specified size and pixel ordering scheme. + template + CImg& assign(const t *const values, const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const char *const axes_order) { + CImg(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this); + } + + //! Construct image from reading an image file \inplace. + /** + In-place version of the constructor CImg(const char*). + **/ + CImg& assign(const char *const filename) { + return load(filename); + } + + //! Construct image copy \inplace. + /** + In-place version of the constructor CImg(const CImg&). + **/ + template + CImg& assign(const CImg& img) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum); + } + + //! In-place version of the advanced copy constructor. + /** + In-place version of the constructor CImg(const CImg&,bool). + **/ + template + CImg& assign(const CImg& img, const bool is_shared) { + return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared); + } + + //! Construct image with dimensions borrowed from another image \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions) { + if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum); + unsigned int siz[4] = { 0,1,1,1 }, k = 0; + CImg item(256); + for (const char *s = dimensions; *s && k<4; ++k) { + if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item); + if (*s) { + unsigned int val = 0; char sep = 0; + if (cimg_sscanf(s,"%u%c",&val,&sep)>0) { + if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100; + else siz[k] = val; + while (*s>='0' && *s<='9') ++s; + if (sep=='%') ++s; + } else switch (cimg::lowercase(*s)) { + case 'x' : case 'w' : siz[k] = img._width; ++s; break; + case 'y' : case 'h' : siz[k] = img._height; ++s; break; + case 'z' : case 'd' : siz[k] = img._depth; ++s; break; + case 'c' : case 's' : siz[k] = img._spectrum; ++s; break; + default : + throw CImgArgumentException(_cimg_instance + "assign(): Invalid character '%c' detected in specified dimension string '%s'.", + cimg_instance, + *s,dimensions); + } + } + } + return assign(siz[0],siz[1],siz[2],siz[3]); + } + + //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace. + /** + In-place version of the constructor CImg(const CImg&,const char*,T). + **/ + template + CImg& assign(const CImg& img, const char *const dimensions, const T& value) { + return assign(img,dimensions).fill(value); + } + + //! Construct image from a display window \inplace. + /** + In-place version of the constructor CImg(const CImgDisplay&). + **/ + CImg& assign(const CImgDisplay &disp) { + disp.snapshot(*this); + return *this; + } + + //! Construct empty image \inplace. + /** + Equivalent to assign(). + \note + - It has been defined for compatibility with STL naming conventions. + **/ + CImg& clear() { + return assign(); + } + + //! Transfer content of an image instance into another one. + /** + Transfer the dimensions and the pixel buffer content of an image instance into another one, + and replace instance by an empty image. It avoids the copy of the pixel buffer + when possible. + \param img Destination image. + \note + - Pixel types \c T and \c t of source and destination images can be different, though the process is + designed to be instantaneous when \c T and \c t are the same. + \par Example + \code + CImg src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0' + dest(16,16); // Construct a 16x16x1x1 (scalar) image + src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image + \endcode + **/ + template + CImg& move_to(CImg& img) { + img.assign(*this); + assign(); + return img; + } + + //! Transfer content of an image instance into another one \specialization. + CImg& move_to(CImg& img) { + if (_is_shared || img._is_shared) img.assign(*this); + else swap(img); + assign(); + return img; + } + + //! Transfer content of an image instance into a new image in an image list. + /** + Transfer the dimensions and the pixel buffer content of an image instance + into a newly inserted image at position \c pos in specified \c CImgList instance. + \param list Destination list. + \param pos Position of the newly inserted image in the list. + \note + - When optional parameter \c pos is omitted, the image instance is transferred as a new + image at the end of the specified \c list. + - It is convenient to sequentially insert new images into image lists, with no + additional copies of memory buffer. + \par Example + \code + CImgList list; // Construct an empty image list + CImg img("reference.jpg"); // Read image from filename + img.move_to(list); // Transfer image content as a new item in the list (no buffer copy) + \endcode + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos=~0U) { + const unsigned int npos = pos>list._width?list._width:pos; + move_to(list.insert(1,npos)[npos]); + return list; + } + + //! Swap fields of two image instances. + /** + \param img Image to swap fields with. + \note + - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing + with algorithms requiring two swapping buffers. + \par Example + \code + CImg img1("lena.jpg"), + img2("milla.jpg"); + img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena' + \endcode + **/ + CImg& swap(CImg& img) { + cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum); + cimg::swap(_data,img._data); + cimg::swap(_is_shared,img._is_shared); + return img; + } + + //! Return a reference to an empty image. + /** + \note + This function is useful mainly to declare optional parameters having type \c CImg in functions prototypes, + e.g. + \code + void f(const int x=0, const int y=0, const CImg& img=CImg::empty()); + \endcode + **/ + static CImg& empty() { + static CImg _empty; + return _empty.assign(); + } + + //! Return a reference to an empty image \const. + static const CImg& const_empty() { + static const CImg _empty; + return _empty; + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Access to a pixel value. + /** + Return a reference to a located pixel value of the image instance, + being possibly \e const, whether the image instance is \e const or not. + This is the standard method to get/set pixel values in \c CImg images. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Range of pixel coordinates start from (0,0,0,0) to + (width() - 1,height() - 1,depth() - 1,spectrum() - 1). + - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the + corresponding dimension is equal to \c 1. + For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by img(x,y,c) instead of + img(x,y,0,c). + \warning + - There is \e no boundary checking done in this operator, to make it as fast as possible. + You \e must take care of out-of-bounds access by yourself, if necessary. + For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary + checking operations in this operator. In that case, warning messages will be printed on the error output + when accessing out-of-bounds pixels. + \par Example + \code + CImg img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0' + const float + valR = img(10,10,0,0), // Read red value at coordinates (10,10) + valG = img(10,10,0,1), // Read green value at coordinates (10,10) + valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted) + avg = (valR + valG + valB)/3; // Compute average pixel value + img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value + \endcode + **/ +#if cimg_verbosity>=3 + T& operator()(const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + const ulongT off = (ulongT)offset(x,y,z,c); + if (!_data || off>=size()) { + cimg::warn(_cimg_instance + "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].", + cimg_instance, + (int)x,(int)y,(int)z,(int)c,off); + return *_data; + } + else return _data[off]; + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->operator()(x,y,z,c); + } + + //! Access to a pixel value. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param wh Precomputed offset, must be equal to width()*\ref height(). + \param whd Precomputed offset, must be equal to width()*\ref height()*\ref depth(). + \note + - Similar to (but faster than) operator()(). + It uses precomputed offsets to optimize memory access. You may use it to optimize + the reading/writing of several pixel values in the same image (e.g. in a loop). + **/ + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd=0) { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } + + //! Access to a pixel value \const. + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd=0) const { + cimg::unused(wh,whd); + return (*this)(x,y,z,c); + } +#else + T& operator()(const unsigned int x) { + return _data[x]; + } + + const T& operator()(const unsigned int x) const { + return _data[x]; + } + + T& operator()(const unsigned int x, const unsigned int y) { + return _data[x + y*_width]; + } + + const T& operator()(const unsigned int x, const unsigned int y) const { + return _data[x + y*_width]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const { + return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const ulongT wh) { + return _data[x + y*_width + z*wh]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int, + const ulongT wh) const { + return _data[x + y*_width + z*wh]; + } + + T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd) { + return _data[x + y*_width + z*wh + c*whd]; + } + + const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c, + const ulongT wh, const ulongT whd) const { + return _data[x + y*_width + z*wh + c*whd]; + } +#endif + + //! Implicitly cast an image into a \c T*. + /** + Implicitly cast a \c CImg instance into a \c T* or \c const \c T* pointer, whether the image instance + is \e const or not. The returned pointer points on the first value of the image pixel buffer. + \note + - It simply returns the pointer data() to the pixel buffer. + - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g. + \code + CImg img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image + if (img1) { // Test succeeds, 'img1' is not an empty image + if (!img2) { // Test succeeds, 'img2' is an empty image + std::printf("'img1' is not empty, 'img2' is empty."); + } + } + \endcode + - It also allows to use brackets to access pixel values, without need for a \c CImg::operator[](), e.g. + \code + CImg img(100,100); + const float value = img[99]; // Access to value of the last pixel on the first row + img[510] = 255; // Set pixel value at (10,5) + \endcode + **/ + operator T*() { + return _data; + } + + //! Implicitly cast an image into a \c T* \const. + operator const T*() const { + return _data; + } + + //! Assign a value to all image pixels. + /** + Assign specified \c value to each pixel value of the image instance. + \param value Value that will be assigned to image pixels. + \note + - The image size is never modified. + - The \c value may be casted to pixel type \c T if necessary. + \par Example + \code + CImg img(100,100); // Declare image (with garbage values) + img = 0; // Set all pixel values to '0' + img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char') + \endcode + **/ + CImg& operator=(const T& value) { + return fill(value); + } + + //! Assign pixels values from a specified expression. + /** + Initialize all pixel values from the specified string \c expression. + \param expression Value string describing the way pixel values are set. + \note + - String parameter \c expression may describe different things: + - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"), + the pixel values are set from specified \c expression and the image size is not modified. + - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and + replace the image instance. The image size is modified if necessary. + \par Example + \code + CImg img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values + img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence + img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula + img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified) + (img1,img2,img3).display(); + \endcode + \image html ref_operator_eq.jpg + **/ + CImg& operator=(const char *const expression) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + _fill(expression,true,1,0,0,"operator=",0); + } catch (CImgException&) { + cimg::exception_mode(omode); + load(expression); + } + cimg::exception_mode(omode); + return *this; + } + + //! Copy an image into the current image instance. + /** + Similar to the in-place copy constructor assign(const CImg&). + **/ + template + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy an image into the current image instance \specialization. + CImg& operator=(const CImg& img) { + return assign(img); + } + + //! Copy the content of a display window to the current image instance. + /** + Similar to assign(const CImgDisplay&). + **/ + CImg& operator=(const CImgDisplay& disp) { + disp.snapshot(*this); + return *this; + } + + //! In-place addition operator. + /** + Add specified \c value to all pixels of an image instance. + \param value Value to add. + \note + - Resulting pixel values are casted to fit the pixel type \c T. + For instance, adding \c 0.2 to a \c CImg is possible but does nothing indeed. + - Overflow values are treated as with standard C++ numeric types. For instance, + \code + CImg img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255' + img+=1; // Add '1' to each pixels -> Overflow + // here all pixels of image 'img' are equal to '0'. + \endcode + - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double, + and use cut() after addition. + \par Example + \code + CImg img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255]) + CImg img2(img1); // Construct a float-valued copy of 'img1' + img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats + img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint + img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1' + const CImg img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way + (img1,img2,img3).display(); + \endcode + \image html ref_operator_plus.jpg + **/ + template + CImg& operator+=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr + value,524288); + return *this; + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the specified string \c expression. + \param expression Value string describing the way pixel values are added. + \note + - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance, + instead of assigning them. + **/ + CImg& operator+=(const char *const expression) { + return *this+=(+*this)._fill(expression,true,1,0,0,"operator+=",this); + } + + //! In-place addition operator. + /** + Add values to image pixels, according to the values of the input image \c img. + \param img Input image to add. + \note + - The size of the image instance is never modified. + - It is not mandatory that input image \c img has the same size as the image instance. + If less values are available in \c img, then the values are added periodically. For instance, adding one + WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3) + means each color channel will be incremented with the same values at the same locations. + \par Example + \code + CImg img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3) + // Construct a scalar shading (img2.spectrum()==1). + const CImg img2(img1.width(),img.height(),1,1,"255*(x/w)^2"); + img1+=img2; // Add shading to each channel of 'img1' + img1.cut(0,255); // Prevent [0,255] overflow + (img2,img1).display(); + \endcode + \image html ref_operator_plus1.jpg + **/ + template + CImg& operator+=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this+=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator++() { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr + 1,524288); + return *this; + } + + //! In-place increment operator (postfix). + /** + Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance. + \note + - Use the prefixed version operator++() if you don't need a copy of the initial + (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage. + **/ + CImg operator++(int) { + const CImg copy(*this,false); + ++*this; + return copy; + } + + //! Return a non-shared copy of the image instance. + /** + \note + - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T. + Indeed, the usual copy constructor CImg(const CImg&) returns a shared copy of a shared input image, + and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no + information about the shared state of the input image. + - Writing \c (+img) is equivalent to \c CImg(img,false). + **/ + CImg operator+() const { + return CImg(*this,false); + } + + //! Addition operator. + /** + Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const t value) const { + return CImg<_cimg_Tt>(*this,false)+=value; + } + + //! Addition operator. + /** + Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator+(const char *const expression) const { + return CImg(*this,false)+=expression; + } + + //! Addition operator. + /** + Similar to operator+=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator+(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)+=img; + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const t), except that it performs a subtraction instead of an addition. + **/ + template + CImg& operator-=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr - value,524288); + return *this; + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const char*), except that it performs a subtraction instead of an addition. + **/ + CImg& operator-=(const char *const expression) { + return *this-=(+*this)._fill(expression,true,1,0,0,"operator-=",this); + } + + //! In-place subtraction operator. + /** + Similar to operator+=(const CImg&), except that it performs a subtraction instead of an addition. + **/ + template + CImg& operator-=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this-=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs& operator--() { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr - 1,524288); + return *this; + } + + //! In-place decrement operator (postfix). + /** + Similar to operator++(int), except that it performs a decrement instead of an increment. + **/ + CImg operator--(int) { + const CImg copy(*this,false); + --*this; + return copy; + } + + //! Replace each pixel by its opposite value. + /** + \note + - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types. + For instance, the \c unsigned \c char opposite of \c 1 is \c 255. + \par Example + \code + const CImg + img1("reference.jpg"), // Load a RGB color image + img2 = -img1; // Compute its opposite (in 'unsigned char') + (img1,img2).display(); + \endcode + \image html ref_operator_minus.jpg + **/ + CImg operator-() const { + return CImg(_width,_height,_depth,_spectrum,(T)0)-=*this; + } + + //! Subtraction operator. + /** + Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const t value) const { + return CImg<_cimg_Tt>(*this,false)-=value; + } + + //! Subtraction operator. + /** + Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator-(const char *const expression) const { + return CImg(*this,false)-=expression; + } + + //! Subtraction operator. + /** + Similar to operator-=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator-(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)-=img; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const t), except that it performs a multiplication instead of an addition. + **/ + template + CImg& operator*=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr * value,262144); + return *this; + } + + //! In-place multiplication operator. + /** + Similar to operator+=(const char*), except that it performs a multiplication instead of an addition. + **/ + CImg& operator*=(const char *const expression) { + return mul((+*this)._fill(expression,true,1,0,0,"operator*=",this)); + } + + //! In-place multiplication operator. + /** + Replace the image instance by the matrix multiplication between the image instance and the specified matrix + \c img. + \param img Second operand of the matrix multiplication. + \note + - It does \e not compute a pointwise multiplication between two images. For this purpose, use + mul(const CImg&) instead. + - The size of the image instance can be modified by this operator. + \par Example + \code + CImg A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4] + const CImg X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2] + A*=X; // Assign matrix multiplication A*X to 'A' + // 'A' is now a 1x2 vector whose values are [5;11]. + \endcode + **/ + template + CImg& operator*=(const CImg& img) { + return ((*this)*img).move_to(*this); + } + + //! Multiplication operator. + /** + Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const t value) const { + return CImg<_cimg_Tt>(*this,false)*=value; + } + + //! Multiplication operator. + /** + Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator*(const char *const expression) const { + return CImg(*this,false)*=expression; + } + + //! Multiplication operator. + /** + Similar to operator*=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator*(const CImg& img) const { + typedef _cimg_Ttdouble Ttdouble; + typedef _cimg_Tt Tt; + if (_width!=img._height || _depth!=1 || _spectrum!=1) + throw CImgArgumentException(_cimg_instance + "operator*(): Invalid multiplication of instance by specified " + "matrix (%u,%u,%u,%u,%p).", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + CImg res(img._width,_height); + + // Check for common cases to optimize. + if (img._width==1) { // Matrix * Vector + if (_height==1) switch (_width) { // Vector^T * Vector + case 1 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0]); + return res; + case 2 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); + return res; + case 3 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2]); + return res; + case 4 : + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); + return res; + default : { + Ttdouble val = 0; + cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096)) + cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i]; + res[0] = val; + return res; + } + } else if (_height==_width) switch (_width) { // Square_matrix * Vector + case 2 : // 2x2_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]); + res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]); + return res; + case 3 : // 3x3_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2]); + res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] + + (Ttdouble)_data[5]*img[2]); + res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] + + (Ttdouble)_data[8]*img[2]); + return res; + case 4 : // 4x4_matrix * Vector + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] + + (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]); + res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] + + (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]); + res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] + + (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]); + res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] + + (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]); + return res; + } + } else if (_height==_width) { + if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix + case 2 : // 2x2_matrix * 2x2_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]); + res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]); + res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]); + return res; + case 3 : // 3x3_matrix * 3x3_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] + + (Ttdouble)_data[2]*img[6]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] + + (Ttdouble)_data[2]*img[7]); + res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] + + (Ttdouble)_data[2]*img[8]); + res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] + + (Ttdouble)_data[5]*img[6]); + res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] + + (Ttdouble)_data[5]*img[7]); + res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] + + (Ttdouble)_data[5]*img[8]); + res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] + + (Ttdouble)_data[8]*img[6]); + res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] + + (Ttdouble)_data[8]*img[7]); + res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] + + (Ttdouble)_data[8]*img[8]); + return res; + case 4 : // 4x4_matrix * 4x4_matrix + res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] + + (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]); + res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] + + (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]); + res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] + + (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]); + res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] + + (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]); + res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] + + (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]); + res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] + + (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]); + res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] + + (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]); + res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] + + (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]); + res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] + + (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]); + res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] + + (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]); + res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] + + (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]); + res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] + + (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]); + res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] + + (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]); + res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] + + (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]); + res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] + + (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]); + res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] + + (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]); + return res; + } else switch (_width) { // Square_matrix * Matrix + case 2 : { // 2x2_matrix * Matrix + const t *const ps0 = img.data(), *const ps1 = img.data(0,1); + Tt *const pd0 = res.data(), *const pd1 = res.data(0,1); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], + a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i]; + pd0[i] = (Tt)(a0*x + a1*y); + pd1[i] = (Tt)(a2*x + a3*y); + } + return res; + } + case 3 : { // 3x3_matrix * Matrix + const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2); + Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], + a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], + a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i]; + pd0[i] = (Tt)(a0*x + a1*y + a2*z); + pd1[i] = (Tt)(a3*x + a4*y + a5*z); + pd2[i] = (Tt)(a6*x + a7*y + a8*z); + } + return res; + } + case 4 : { // 4x4_matrix * Matrix + const t + *const ps0 = img.data(), *const ps1 = img.data(0,1), + *const ps2 = img.data(0,2), *const ps3 = img.data(0,3); + Tt + *const pd0 = res.data(), *const pd1 = res.data(0,1), + *const pd2 = res.data(0,2), *const pd3 = res.data(0,3); + const Ttdouble + a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3], + a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], + a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11], + a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14], + a15 = (Ttdouble)_data[15]; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512)) + cimg_forX(img,i) { + const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i]; + pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c); + pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c); + pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c); + pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c); + } + return res; + } + } + } + + // Fallback to generic version. +#if cimg_use_openmp!=0 + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 && + img.size()>(cimg_openmp_sizefactor)*1024)) + cimg_forXY(res,i,j) { + Ttdouble value = 0; + cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); + res(i,j) = (Tt)value; + } +#else + Tt *ptrd = res._data; + cimg_forXY(res,i,j) { + Ttdouble value = 0; + cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); + *(ptrd++) = (Tt)value; + } +#endif + return res; + } + + //! In-place division operator. + /** + Similar to operator+=(const t), except that it performs a division instead of an addition. + **/ + template + CImg& operator/=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,*ptr / value,32768); + return *this; + } + + //! In-place division operator. + /** + Similar to operator+=(const char*), except that it performs a division instead of an addition. + **/ + CImg& operator/=(const char *const expression) { + return div((+*this)._fill(expression,true,1,0,0,"operator/=",this)); + } + + //! In-place division operator. + /** + Replace the image instance by the (right) matrix division between the image instance and the specified + matrix \c img. + \param img Second operand of the matrix division. + \note + - It does \e not compute a pointwise division between two images. For this purpose, use + div(const CImg&) instead. + - It returns the matrix operation \c A*inverse(img). + - The size of the image instance can be modified by this operator. + **/ + template + CImg& operator/=(const CImg& img) { + return (*this*img.get_invert()).move_to(*this); + } + + //! Division operator. + /** + Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const t value) const { + return CImg<_cimg_Tt>(*this,false)/=value; + } + + //! Division operator. + /** + Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator/(const char *const expression) const { + return CImg(*this,false)/=expression; + } + + //! Division operator. + /** + Similar to operator/=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator/(const CImg& img) const { + return (*this)*img.get_invert(); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const t), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384); + return *this; + } + + //! In-place modulo operator. + /** + Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition. + **/ + CImg& operator%=(const char *const expression) { + return *this%=(+*this)._fill(expression,true,1,0,0,"operator%=",this); + } + + //! In-place modulo operator. + /** + Similar to operator+=(const CImg&), except that it performs a modulo operation instead of an addition. + **/ + template + CImg& operator%=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this%=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> operator%(const t value) const { + return CImg<_cimg_Tt>(*this,false)%=value; + } + + //! Modulo operator. + /** + Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + CImg operator%(const char *const expression) const { + return CImg(*this,false)%=expression; + } + + //! Modulo operator. + /** + Similar to operator%=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary. + **/ + template + CImg<_cimg_Tt> operator%(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false)%=img; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr & (ulongT)value,32768); + return *this; + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition. + **/ + CImg& operator&=(const char *const expression) { + return *this&=(+*this)._fill(expression,true,1,0,0,"operator&=",this); + } + + //! In-place bitwise AND operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise AND operation instead of an addition. + **/ + template + CImg& operator&=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this&=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator&(const t value) const { + return (+*this)&=value; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator&(const char *const expression) const { + return (+*this)&=expression; + } + + //! Bitwise AND operator. + /** + Similar to operator&=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator&(const CImg& img) const { + return (+*this)&=img; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr | (ulongT)value,32768); + return *this; + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition. + **/ + CImg& operator|=(const char *const expression) { + return *this|=(+*this)._fill(expression,true,1,0,0,"operator|=",this); + } + + //! In-place bitwise OR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise OR operation instead of an addition. + **/ + template + CImg& operator|=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this|=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator|(const t value) const { + return (+*this)|=value; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator|(const char *const expression) const { + return (+*this)|=expression; + } + + //! Bitwise OR operator. + /** + Similar to operator|=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator|(const CImg& img) const { + return (+*this)|=img; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead. + **/ + template + CImg& operator^=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,(ulongT)*ptr ^ (ulongT)value,32768); + return *this; + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead. + **/ + CImg& operator^=(const char *const expression) { + return *this^=(+*this)._fill(expression,true,1,0,0,"operator^=",this); + } + + //! In-place bitwise XOR operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise XOR operation instead of an addition. + \warning + - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg&) instead. + **/ + template + CImg& operator^=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator^(const t value) const { + return (+*this)^=value; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator^(const char *const expression) const { + return (+*this)^=expression; + } + + //! Bitwise XOR operator. + /** + Similar to operator^=(const CImg&), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator^(const CImg& img) const { + return (+*this)^=img; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536); + return *this; + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition. + **/ + CImg& operator<<=(const char *const expression) { + return *this<<=(+*this)._fill(expression,true,1,0,0,"operator<<=",this); + } + + //! In-place bitwise left shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise left shift instead of an addition. + **/ + template + CImg& operator<<=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg operator<<(const t value) const { + return (+*this)<<=value; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator<<(const char *const expression) const { + return (+*this)<<=expression; + } + + //! Bitwise left shift operator. + /** + Similar to operator<<=(const CImg&), except that it returns a new image instance instead of + operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator<<(const CImg& img) const { + return (+*this)<<=img; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const t value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536); + return *this; + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition. + **/ + CImg& operator>>=(const char *const expression) { + return *this>>=(+*this)._fill(expression,true,1,0,0,"operator>>=",this); + } + + //! In-place bitwise right shift operator. + /** + Similar to operator+=(const CImg&), except that it performs a bitwise right shift instead of an addition. + **/ + template + CImg& operator>>=(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return *this^=+img; + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs> (int)*(ptrs++)); + for (const t *ptrs = img._data; ptrd> (int)*(ptrs++)); + } + return *this; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const t value) const { + return (+*this)>>=value; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place. + The pixel type of the returned image is \c T. + **/ + CImg operator>>(const char *const expression) const { + return (+*this)>>=expression; + } + + //! Bitwise right shift operator. + /** + Similar to operator>>=(const CImg&), except that it returns a new image instance instead of + operating in-place. + The pixel type of the returned image is \c T. + **/ + template + CImg operator>>(const CImg& img) const { + return (+*this)>>=img; + } + + //! Bitwise inversion operator. + /** + Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value. + **/ + CImg operator~() const { + CImg res(_width,_height,_depth,_spectrum); + const T *ptrs = _data; + cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; } + return res; + } + + //! Test if all pixels of an image have the same value. + /** + Return \c true is all pixels of the image instance are equal to the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator==(const t value) const { + if (is_empty()) return false; + typedef _cimg_Tt Tt; + bool is_equal = true; + for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {} + return is_equal; + } + + //! Test if all pixel values of an image follow a specified expression. + /** + Return \c true is all pixels of the image instance are equal to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator==(const char *const expression) const { + return *this==(+*this)._fill(expression,true,1,0,0,"operator==",this); + } + + //! Test if two images have the same size and values. + /** + Return \c true if the image instance and the input image \c img have the same pixel values, + even if the dimensions of the two images do not match. It returns \c false otherwise. + \param img Input image to compare with. + \note + - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==() + to return \c true. + Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different + pixel types \c T and \c t. + \par Example + \code + const CImg img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values) + const CImg img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values) + if (img1==img2) { // Test succeeds, image dimensions and values are the same + std::printf("'img1' and 'img2' have same dimensions and values."); + } + \endcode + **/ + template + bool operator==(const CImg& img) const { + typedef _cimg_Tt Tt; + const ulongT siz = size(); + bool is_equal = true; + if (siz!=img.size()) return false; + t *ptrs = img._data + siz; + for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {} + return is_equal; + } + + //! Test if pixels of an image are all different from a value. + /** + Return \c true is all pixels of the image instance are different than the specified \c value. + \param value Reference value to compare with. + **/ + template + bool operator!=(const t value) const { + return !((*this)==value); + } + + //! Test if all pixel values of an image are different from a specified expression. + /** + Return \c true is all pixels of the image instance are different to the specified \c expression. + \param expression Value string describing the way pixel values are compared. + **/ + bool operator!=(const char *const expression) const { + return !((*this)==expression); + } + + //! Test if two images have different sizes or values. + /** + Return \c true if the image instance and the input image \c img have different dimensions or pixel values, + and \c false otherwise. + \param img Input image to compare with. + \note + - Writing \c img1!=img2 is equivalent to \c !(img1==img2). + **/ + template + bool operator!=(const CImg& img) const { + return !((*this)==img); + } + + //! Construct an image list from two images. + /** + Return a new list of image (\c CImgList instance) containing exactly two elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image \c img, at position [\c 1]. + + \param img Input image that will be the second image of the resulting list. + \note + - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow + in practice (see warning below). + - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are + inserted as new non-shared copies in the resulting list. + - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary. + \warning + - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list. + This may become very expensive in terms of speed and used memory. You should avoid using this technique to + build a new CImgList instance from several images, if you are seeking for performance. + Fast insertions of images in an image list are possible with + CImgList::insert(const CImg&,unsigned int,bool) or move_to(CImgList&,unsigned int). + \par Example + \code + const CImg + img1("reference.jpg"), + img2 = img1.get_mirror('x'), + img3 = img2.get_blur(5); + const CImgList list = (img1,img2); // Create list of two elements from 'img1' and 'img2' + (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3' + \endcode + \image html ref_operator_comma.jpg + **/ + template + CImgList<_cimg_Tt> operator,(const CImg& img) const { + return CImgList<_cimg_Tt>(*this,img); + } + + //! Construct an image list from image instance and an input image list. + /** + Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements: + - A copy of the image instance, at position [\c 0]. + - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()]. + + \param list Input image list that will be appended to the image instance. + \note + - Similar to operator,(const CImg&) const, except that it takes an image list as an argument. + **/ + template + CImgList<_cimg_Tt> operator,(const CImgList& list) const { + return CImgList<_cimg_Tt>(list,false).insert(*this,0); + } + + //! Split image along specified axis. + /** + Return a new list of images (\c CImgList instance) containing the split components + of the instance image along the specified axis. + \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c') + \note + - Similar to get_split(char,int) const, with default second argument. + \par Example + \code + const CImg img("reference.jpg"); // Load a RGB color image + const CImgList list = (img<'c'); // Get a list of its three R,G,B channels + (img,list).display(); + \endcode + \image html ref_operator_less.jpg + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the number of image columns. + /** + Return the image width, i.e. the image dimension along the X-axis. + \note + - The width() of an empty image is equal to \c 0. + - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations. + - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._width. + **/ + int width() const { + return (int)_width; + } + + //! Return the number of image rows. + /** + Return the image height, i.e. the image dimension along the Y-axis. + \note + - The height() of an empty image is equal to \c 0. + - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._height. + **/ + int height() const { + return (int)_height; + } + + //! Return the number of image slices. + /** + Return the image depth, i.e. the image dimension along the Z-axis. + \note + - The depth() of an empty image is equal to \c 0. + - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image + is said to be \e volumetric. + - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._depth. + **/ + int depth() const { + return (int)_depth; + } + + //! Return the number of image channels. + /** + Return the number of image channels, i.e. the image dimension along the C-axis. + \note + - The spectrum() of an empty image is equal to \c 0. + - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3 + for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel). + The number of channels of an image instance is not limited. The meaning of the pixel values is not linked + up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image). + - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int. + Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving + \c unsigned \c int variables. + Access to the initial \c unsigned \c int variable is possible (though not recommended) by + (*this)._spectrum. + **/ + int spectrum() const { + return (int)_spectrum; + } + + //! Return the total number of pixel values. + /** + Return width()*\ref height()*\ref depth()*\ref spectrum(), + i.e. the total number of values of type \c T in the pixel buffer of the image instance. + \note + - The size() of an empty image is equal to \c 0. + - The allocated memory size for a pixel buffer of a non-shared \c CImg instance is equal to + size()*sizeof(T). + \par Example + \code + const CImg img(100,100,1,3); // Construct new 100x100 color image + if (img.size()==30000) // Test succeeds + std::printf("Pixel buffer uses %lu bytes", + img.size()*sizeof(float)); + \endcode + **/ + ulongT size() const { + return (ulongT)_width*_height*_depth*_spectrum; + } + + //! Return a pointer to the first pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance, + whether the instance is \c const or not. + \note + - The data() of an empty image is equal to \c 0 (null pointer). + - The allocated pixel buffer for the image instance starts from \c data() + and goes to data()+\ref size() - 1 (included). + - To get the pointer to one particular location of the pixel buffer, use + data(unsigned int,unsigned int,unsigned int,unsigned int) instead. + **/ + T* data() { + return _data; + } + + //! Return a pointer to the first pixel value \const. + const T* data() const { + return _data; + } + + //! Return a pointer to a located pixel value. + /** + Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer + of the image instance, + whether the instance is \c const or not. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)). Thus, this method has the same + properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + **/ +#if cimg_verbosity>=3 + T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + const ulongT off = (ulongT)offset(x,y,z,c); + if (off>=size()) + cimg::warn(_cimg_instance + "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].", + cimg_instance, + x,y,z,c,off); + return _data + off; + } + + //! Return a pointer to a located pixel value \const. + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return const_cast*>(this)->data(x,y,z,c); + } +#else + T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) { + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; + } + + const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const { + return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth; + } +#endif + + //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Writing \c img.data(x,y,z,c) is equivalent to &(img(x,y,z,c)) - img.data(). + Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int). + \par Example + \code + const CImg img(100,100,1,3); // Define a 100x100 RGB-color image + const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10) + const float val = img[off]; // Get the blue value of this pixel + \endcode + **/ + longT offset(const int x, const int y=0, const int z=0, const int c=0) const { + return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth; + } + + //! Return a CImg::iterator pointing to the first pixel value. + /** + \note + - Equivalent to data(). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + iterator begin() { + return _data; + } + + //! Return a CImg::iterator pointing to the first value of the pixel buffer \const. + const_iterator begin() const { + return _data; + } + + //! Return a CImg::iterator pointing next to the last pixel value. + /** + \note + - Writing \c img.end() is equivalent to img.data() + img.size(). + - It has been mainly defined for compatibility with STL naming conventions. + \warning + - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer. + Trying to read or write the content of the returned iterator will probably result in a crash. + Use it mainly as a strict upper bound for a CImg::iterator. + \par Example + \code + CImg img(100,100,1,3); // Define a 100x100 RGB color image + // 'img.end()' used below as an upper bound for the iterator. + for (CImg::iterator it = img.begin(); it::iterator pointing next to the last pixel value \const. + const_iterator end() const { + return _data + size(); + } + + //! Return a reference to the first pixel value. + /** + \note + - Writing \c img.front() is equivalent to img[0], or img(0,0,0,0). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& front() { + return *_data; + } + + //! Return a reference to the first pixel value \const. + const T& front() const { + return *_data; + } + + //! Return a reference to the last pixel value. + /** + \note + - Writing \c img.back() is equivalent to img[img.size() - 1], or + img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1). + - It has been mainly defined for compatibility with STL naming conventions. + **/ + T& back() { + return *(_data + size() - 1); + } + + //! Return a reference to the last pixel value \const. + const T& back() const { + return *(_data + size() - 1); + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to a specified default value in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note + - Writing \c img.at(offset,out_value) is similar to img[offset], except that if \c offset + is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value + is safely returned instead. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + **/ + T& at(const int offset, const T& out_value) { + return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const. + T at(const int offset, const T& out_value) const { + return (offset<0 || offset>=(int)size())?out_value:(*this)[offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions. + /** + Return a reference to the pixel value of the image instance located at a specified \c offset, + or to the nearest pixel location in the image instance in case of out-of-bounds access. + \param offset Offset to the desired pixel value. + \note + - Similar to at(int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified offset, i.e. + - If \c offset<0, then \c img[0] is returned. + - If \c offset>=img.size(), then \c img[img.size() - 1] is returned. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel offset. + - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int). + **/ + T& at(const int offset) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + T& _at(const int offset) { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; + } + + //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const. + const T& at(const int offset) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "at(): Empty instance.", + cimg_instance); + return _at(offset); + } + + const T& _at(const int offset) const { + const unsigned int siz = (unsigned int)size(); + return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset]; + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to a specified default value in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value + \c out_value. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const. + T atX(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || x>=width())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate. + /** + Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c), + or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the + nearest pixel in the image instance, regarding the specified X-coordinate. + - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when + you are \e not sure about the validity of the specified pixel coordinates. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _at(int,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + T& atX(const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + T& _atX(const int x, const int y=0, const int z=0, const int c=0) { + return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const. + const T& atX(const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atX(): Empty instance.", + cimg_instance); + return _atX(x,y,z,c); + } + + const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const { + return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates. + **/ + T& atXY(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const. + T atXY(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXY(int,int,int,int). + **/ + T& atXY(const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + T& _atXY(const int x, const int y, const int z=0, const int c=0) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1),z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const. + const T& atXY(const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXY(): Empty instance.", + cimg_instance); + return _atXY(x,y,z,c); + } + + const T& _atXY(const int x, const int y, const int z=0, const int c=0) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1),z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on + X,Y and Z-coordinates. + **/ + T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const. + T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXYZ(int,int,int,int). + **/ + T& atXYZ(const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + T& _atXYZ(const int x, const int y, const int z, const int c=0) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1),c); + } + + //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const. + const T& atXYZ(const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZ(): Empty instance.", + cimg_instance); + return _atXYZ(x,y,z,c); + } + + const T& _atXYZ(const int x, const int y, const int z, const int c=0) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1),c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions. + /** + Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all + X,Y,Z and C-coordinates. + **/ + T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())? + (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c); + } + + //! Access to a pixel value, using Dirichlet boundary conditions \const. + T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const { + return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value: + (*this)(x,y,z,c); + } + + //! Access to a pixel value, using Neumann boundary conditions. + /** + Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _atXYZC(int,int,int,int). + **/ + T& atXYZC(const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + T& _atXYZC(const int x, const int y, const int z, const int c) { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1), + cimg::cut(c,0,spectrum() - 1)); + } + + //! Access to a pixel value, using Neumann boundary conditions \const. + const T& atXYZC(const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "atXYZC(): Empty instance.", + cimg_instance); + return _atXYZC(x,y,z,c); + } + + const T& _atXYZC(const int x, const int y, const int z, const int c) const { + return (*this)(cimg::cut(x,0,width() - 1), + cimg::cut(y,0,height() - 1), + cimg::cut(z,0,depth() - 1), + cimg::cut(c,0,spectrum() - 1)); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by + a linear interpolation along the X-axis, if corresponding coordinates are not integers. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1; + const float + dx = fx - x; + const Tfloat + Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access along + the X-axis. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns + the value of the nearest pixel in the image instance, regarding the specified X-coordinate. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX(): Empty instance.", + cimg_instance); + + return _linear_atX(fx,y,z,c); + } + + Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx - x; + const unsigned int + nx = dx>0?x + 1:x; + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate. + Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atX_p(): Empty instance.", + cimg_instance); + + return _linear_atX_p(fx,y,z,c); + } + + Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f); + const unsigned int + x = (unsigned int)nfx; + const float + dx = nfx - x; + const unsigned int + nx = cimg::mod(x + 1,_width); + const Tfloat + Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c); + return Ic + dx*(In - Ic); + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved both for X and Y-coordinates. + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1; + const float + dx = fx - x, + dy = fy - y; + const Tfloat + Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), + Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXY(float,float,int,int). + **/ + Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY(): Empty instance.", + cimg_instance); + + return _linear_atXY(fx,fy,z,c); + } + + Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx - x, + dy = nfy - y; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y; + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates. + Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXY_p(): Empty instance.", + cimg_instance); + + return _linear_atXY_p(fx,fy,z,c); + } + + Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy; + const float + dx = nfx - x, + dy = nfy - y; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height); + const Tfloat + Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c); + return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy; + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved both for X,Y and Z-coordinates. + **/ + Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z; + const Tfloat + Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), + Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), + Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), + Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXYZ(float,float,float,int). + **/ + Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ(): Empty instance.", + cimg_instance); + + return _linear_atXYZ(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = cimg::cut(fz,0,depth() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y, + nz = dz>0?z + 1:z; + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates. + Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZ_p(): Empty instance.", + cimg_instance); + + return _linear_atXYZ_p(fx,fy,fz,c); + } + + Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f), + nfz = cimg::mod(fz,_depth - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height), + nz = cimg::mod(z + 1,_depth); + const Tfloat + Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c), + Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c), + Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c), + Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c); + return Iccc + + (Incc - Iccc + + (Iccc + Innc - Icnc - Incc + + (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy + + (Iccc + Incn - Iccn - Incc)*dz)*dx + + (Icnc - Iccc + + (Iccc + Icnn - Iccn - Icnc)*dz)*dy + + (Iccn - Iccc)*dz; + } + + //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates. + /** + Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the + boundary checking are achieved for all X,Y,Z and C-coordinates. + **/ + Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1, + c = (int)fc - (fc>=0?0:1), nc = c + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z, + dc = fc - c; + const Tfloat + Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value), + Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value), + Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value), + Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value), + Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value), + Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value), + Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value), + Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn -Icccc); + } + + //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates. + /** + Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking + are achieved for all X,Y,Z and C-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _linear_atXYZC(float,float,float,float). + **/ + Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC(): Empty instance.", + cimg_instance); + + return _linear_atXYZC(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + const float + nfx = cimg::cut(fx,0,width() - 1), + nfy = cimg::cut(fy,0,height() - 1), + nfz = cimg::cut(fz,0,depth() - 1), + nfc = cimg::cut(fc,0,spectrum() - 1); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = dx>0?x + 1:x, + ny = dy>0?y + 1:y, + nz = dz>0?z + 1:z, + nc = dc>0?c + 1:c; + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn - Icccc); + } + + //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates. + Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "linear_atXYZC_p(): Empty instance.", + cimg_instance); + + return _linear_atXYZC_p(fx,fy,fz,fc); + } + + Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const { + const float + nfx = cimg::mod(fx,_width - 0.5f), + nfy = cimg::mod(fy,_height - 0.5f), + nfz = cimg::mod(fz,_depth - 0.5f), + nfc = cimg::mod(fc,_spectrum - 0.5f); + const unsigned int + x = (unsigned int)nfx, + y = (unsigned int)nfy, + z = (unsigned int)nfz, + c = (unsigned int)nfc; + const float + dx = nfx - x, + dy = nfy - y, + dz = nfz - z, + dc = nfc - c; + const unsigned int + nx = cimg::mod(x + 1,_width), + ny = cimg::mod(y + 1,_height), + nz = cimg::mod(z + 1,_depth), + nc = cimg::mod(c + 1,_spectrum); + const Tfloat + Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c), + Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c), + Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c), + Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c), + Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc), + Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc), + Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc), + Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc); + return Icccc + + dx*(Inccc - Icccc + + dy*(Icccc + Inncc - Icncc - Inccc + + dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc + + dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc - + Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) + + dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) + + dz*(Icccc + Incnc - Iccnc - Inccc + + dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) + + dc*(Icccc + Inccn - Inccc - Icccn)) + + dy*(Icncc - Icccc + + dz*(Icccc + Icnnc - Iccnc - Icncc + + dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) + + dc*(Icccc + Icncn - Icncc - Icccn)) + + dz*(Iccnc - Icccc + + dc*(Icccc + Iccnn - Iccnc - Icccn)) + + dc*(Icccn - Icccc); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or a specified default value in case of out-of-bounds access along the X-axis. + The cubic interpolation uses Hermite splines. + \param fx d X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds. + \note + - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is + approximated by a \e cubic interpolation along the X-axis. + - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued. + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2; + const float + dx = fx - x; + const Tfloat + Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value), + In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atX(fx,y,z,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c), + or the value of the nearest pixel location in the image instance in case of out-of-bounds access + along the X-axis. The cubic interpolation uses Hermite splines. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is + approximated by a cubic interpolation along the X-axis. + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atX(float,int,int,int). + \warning + - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds. + **/ + Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX(): Empty instance.", + cimg_instance); + return _cubic_atX(fx,y,z,c); + } + + Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1); + const int + x = (int)nfx; + const float + dx = nfx - x; + const int + px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2; + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate. + /** + Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atX_c(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX(fx,y,z,c)); + } + + T _cubic_atX_c(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX(fx,y,z,c)); + } + + //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate. + Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atX_p(): Empty instance.", + cimg_instance); + return _cubic_atX_p(fx,y,z,c); + } + + Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f); + const int + x = (int)nfx; + const float + dx = nfx - x; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()); + const Tfloat + Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c), + In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c); + return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atX_pc(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(cubic_atX_p(fx,y,z,c)); + } + + T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const { + return cimg::type::cut(_cubic_atX_p(fx,y,z,c)); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X and Y-coordinates. + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, + y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2; + const float dx = fx - x, dy = fy - y; + const Tfloat + Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), + Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value), + Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), + Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), + Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved for both X and Y-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXY(float,float,int,int). + **/ + Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY(): Empty instance.", + cimg_instance); + return _cubic_atXY(fx,fy,z,c); + } + + Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), + nfy = cimg::type::is_nan(fy)?0:cimg::cut(fy,0,height() - 1); + const int x = (int)nfx, y = (int)nfy; + const float dx = nfx - x, dy = nfy - y; + const int + px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2, + py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2; + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), + Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), + Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), + Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates. + /** + Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY(fx,fy,z,c)); + } + + T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY(fx,fy,z,c)); + } + + //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates. + Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXY_p(): Empty instance.", + cimg_instance); + return _cubic_atXY_p(fx,fy,z,c); + } + + Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f), + nfy = cimg::type::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f); + const int x = (int)nfx, y = (int)nfy; + const float dx = nfx - x, dy = nfy - y; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()), + py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()); + const Tfloat + Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), + Iap = (Tfloat)(*this)(ax,py,z,c), + Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), + Iac = (Tfloat)(*this)(ax,y,z,c), + Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), + Ian = (Tfloat)(*this)(ax,ny,z,c), + In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), + Iaa = (Tfloat)(*this)(ax,ay,z,c), + Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(cubic_atXY_p(fx,fy,z,c)); + } + + T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const { + return cimg::type::cut(_cubic_atXY_p(fx,fy,z,c)); + } + + //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + const int + x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2, + y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2, + z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2; + const float dx = fx - x, dy = fy - y, dz = fz - z; + const Tfloat + Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value), + Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value), + Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value), + Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value), + Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value), + Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value), + Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), + Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value), + Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value), + Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value), + Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), + Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value), + Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value), + Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value), + Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value), + Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value), + Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay + in the min/max range of the datatype \c T. + **/ + T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c,out_value)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ(): Empty instance.", + cimg_instance); + return _cubic_atXYZ(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::cut(fx,0,width() - 1), + nfy = cimg::type::is_nan(fy)?0:cimg::cut(fy,0,height() - 1), + nfz = cimg::type::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1); + const int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2, + py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2, + pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2; + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates. + /** + Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the + min/max range of the datatype \c T. + **/ + T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ(fx,fy,fz,c)); + } + + T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ(fx,fy,fz,c)); + } + + //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates. + /** + Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking + are achieved both for X,Y and Z-coordinates. + \note + - If you know your image instance is \e not empty, you may rather use the slightly faster method + \c _cubic_atXYZ(float,float,float,int). + **/ + Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "cubic_atXYZ_p(): Empty instance.", + cimg_instance); + return _cubic_atXYZ_p(fx,fy,fz,c); + } + + Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const { + const float + nfx = cimg::type::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f), + nfy = cimg::type::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f), + nfz = cimg::type::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f); + const int x = (int)nfx, y = (int)nfy, z = (int)nfz; + const float dx = nfx - x, dy = nfy - y, dz = nfz - z; + const int + px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()), + py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()), + pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth()); + const Tfloat + Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c), + Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c), + Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) + + dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)), + Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c), + Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c), + Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) + + dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)), + Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c), + Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c), + Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) + + dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)), + Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c), + Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c), + Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) + + dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)), + Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) + + dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)), + Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c), + Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c), + Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) + + dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)), + Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c), + Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c), + Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) + + dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)), + Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c), + Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c), + Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) + + dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)), + Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c), + Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c), + Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) + + dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)), + Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) + + dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)), + Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c), + Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c), + Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) + + dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)), + Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c), + Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c), + Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) + + dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)), + Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c), + Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c), + Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) + + dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)), + Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c), + Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c), + Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) + + dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)), + In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) + + dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)), + Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c), + Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c), + Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) + + dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)), + Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c), + Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c), + Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) + + dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)), + Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c), + Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c), + Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) + + dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)), + Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c), + Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c), + Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) + + dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)), + Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) + + dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa)); + return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia)); + } + + T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(cubic_atXYZ_p(fx,fy,fz,c)); + } + + T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const { + return cimg::type::cut(_cubic_atXYZ_p(fx,fy,fz,c)); + } + + //! Set pixel value, using linear interpolation for the X-coordinates. + /** + Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that + the value is spread amongst several neighbors if the pixel coordinates are float-valued. + \param value Pixel value to set. + \param fx X-coordinate of the pixel value (float-valued). + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image + pixel(s). + \return A reference to the current image instance. + \note + - Calling this method with out-of-bounds coordinates does nothing. + **/ + CImg& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1; + const float + dx = fx - x; + if (y>=0 && y=0 && z=0 && c=0 && x=0 && nx& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1; + const float + dx = fx - x, + dy = fy - y; + if (z>=0 && z=0 && c=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0, + const bool is_added=false) { + const int + x = (int)fx - (fx>=0?0:1), nx = x + 1, + y = (int)fy - (fy>=0?0:1), ny = y + 1, + z = (int)fz - (fz>=0?0:1), nz = z + 1; + const float + dx = fx - x, + dy = fy - y, + dz = fz - z; + if (c>=0 && c=0 && z=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx=0 && nz=0 && y=0 && x=0 && nx=0 && ny=0 && x=0 && nx image whose buffer data() is a \c char* string describing the list of all pixel values + of the image instance (written in base 10), separated by specified \c separator character. + \param separator A \c char character which specifies the separator between values in the returned C-string. + \param max_size Maximum size of the returned image (or \c 0 if no limits are set). + \param format For float/double-values, tell the printf format used to generate the text representation + of the numbers (or \c 0 for default representation). + \note + - The returned image is never empty. + - For an empty image instance, the returned string is "". + - If \c max_size is equal to \c 0, there are no limits on the size of the returned string. + - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off + and terminated by character \c '\0'. In that case, the returned image size is max_size + 1. + **/ + CImg value_string(const char separator=',', const unsigned int max_size=0, + const char *const format=0) const { + if (is_empty() || max_size==1) return CImg(1,1,1,1,0); + CImgList items; + CImg s_item(256); *s_item = 0; + const T *ptrs = _data; + unsigned int string_size = 0; + const char *const _format = format?format:cimg::type::format(); + for (ulongT off = 0, siz = size(); off::format(*(ptrs++))); + CImg item(s_item._data,printed_size); + item[printed_size - 1] = separator; + item.move_to(items); + if (max_size) string_size+=printed_size; + } + CImg res; + (items>'x').move_to(res); + if (max_size && res._width>=max_size) res.crop(0,max_size - 1); + res.back() = 0; + return res; + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Test shared state of the pixel buffer. + /** + Return \c true if image instance has a shared memory buffer, and \c false otherwise. + \note + - A shared image do not own his pixel buffer data() and will not deallocate it on destruction. + - Most of the time, a \c CImg image instance will \e not be shared. + - A shared image can only be obtained by a limited set of constructors and methods (see list below). + **/ + bool is_shared() const { + return _is_shared; + } + + //! Test if image instance is empty. + /** + Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions + \c 0 x \c 0 x \c 0 x \c 0 and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise. + **/ + bool is_empty() const { + return !(_data && _width && _height && _depth && _spectrum); + } + + //! Test if image instance contains a 'inf' value. + /** + Return \c true, if image instance contains a 'inf' value, and \c false otherwise. + **/ + bool is_inf() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_inf((float)*p)) return true; + return false; + } + + //! Test if image instance contains a NaN value. + /** + Return \c true, if image instance contains a NaN value, and \c false otherwise. + **/ + bool is_nan() const { + if (cimg::type::is_float()) cimg_for(*this,p,T) if (cimg::type::is_nan((float)*p)) return true; + return false; + } + + //! Test if image width is equal to specified value. + bool is_sameX(const unsigned int size_x) const { + return _width==size_x; + } + + //! Test if image width is equal to specified value. + template + bool is_sameX(const CImg& img) const { + return is_sameX(img._width); + } + + //! Test if image width is equal to specified value. + bool is_sameX(const CImgDisplay& disp) const { + return is_sameX(disp._width); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const unsigned int size_y) const { + return _height==size_y; + } + + //! Test if image height is equal to specified value. + template + bool is_sameY(const CImg& img) const { + return is_sameY(img._height); + } + + //! Test if image height is equal to specified value. + bool is_sameY(const CImgDisplay& disp) const { + return is_sameY(disp._height); + } + + //! Test if image depth is equal to specified value. + bool is_sameZ(const unsigned int size_z) const { + return _depth==size_z; + } + + //! Test if image depth is equal to specified value. + template + bool is_sameZ(const CImg& img) const { + return is_sameZ(img._depth); + } + + //! Test if image spectrum is equal to specified value. + bool is_sameC(const unsigned int size_c) const { + return _spectrum==size_c; + } + + //! Test if image spectrum is equal to specified value. + template + bool is_sameC(const CImg& img) const { + return is_sameC(img._spectrum); + } + + //! Test if image width and height are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified. + **/ + bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const { + return _width==size_x && _height==size_y; + } + + //! Test if image width and height are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameY(const CImg&) const are both verified. + **/ + template + bool is_sameXY(const CImg& img) const { + return is_sameXY(img._width,img._height); + } + + //! Test if image width and height are the same as that of an existing display window. + /** + Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified. + **/ + bool is_sameXY(const CImgDisplay& disp) const { + return is_sameXY(disp._width,disp._height); + } + + //! Test if image width and depth are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const { + return _width==size_x && _depth==size_z; + } + + //! Test if image width and depth are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXZ(const CImg& img) const { + return is_sameXZ(img._width,img._depth); + } + + //! Test if image width and spectrum are equal to specified values. + /** + Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const { + return _width==size_x && _spectrum==size_c; + } + + //! Test if image width and spectrum are the same as that of another image. + /** + Test if is_sameX(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXC(const CImg& img) const { + return is_sameXC(img._width,img._spectrum); + } + + //! Test if image height and depth are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const { + return _height==size_y && _depth==size_z; + } + + //! Test if image height and depth are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameYZ(const CImg& img) const { + return is_sameYZ(img._height,img._depth); + } + + //! Test if image height and spectrum are equal to specified values. + /** + Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const { + return _height==size_y && _spectrum==size_c; + } + + //! Test if image height and spectrum are the same as that of another image. + /** + Test if is_sameY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYC(const CImg& img) const { + return is_sameYC(img._height,img._spectrum); + } + + //! Test if image depth and spectrum are equal to specified values. + /** + Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const { + return _depth==size_z && _spectrum==size_c; + } + + //! Test if image depth and spectrum are the same as that of another image. + /** + Test if is_sameZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameZC(const CImg& img) const { + return is_sameZC(img._depth,img._spectrum); + } + + //! Test if image width, height and depth are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified. + **/ + bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const { + return is_sameXY(size_x,size_y) && _depth==size_z; + } + + //! Test if image width, height and depth are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameZ(const CImg&) const are both verified. + **/ + template + bool is_sameXYZ(const CImg& img) const { + return is_sameXYZ(img._width,img._height,img._depth); + } + + //! Test if image width, height and spectrum are equal to specified values. + /** + Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const { + return is_sameXY(size_x,size_y) && _spectrum==size_c; + } + + //! Test if image width, height and spectrum are the same as that of another image. + /** + Test if is_sameXY(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYC(const CImg& img) const { + return is_sameXYC(img._width,img._height,img._spectrum); + } + + //! Test if image width, depth and spectrum are equal to specified values. + /** + Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const { + return is_sameXZ(size_x,size_z) && _spectrum==size_c; + } + + //! Test if image width, depth and spectrum are the same as that of another image. + /** + Test if is_sameXZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXZC(const CImg& img) const { + return is_sameXZC(img._width,img._depth,img._spectrum); + } + + //! Test if image height, depth and spectrum are equal to specified values. + /** + Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified. + **/ + bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const { + return is_sameYZ(size_y,size_z) && _spectrum==size_c; + } + + //! Test if image height, depth and spectrum are the same as that of another image. + /** + Test if is_sameYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameYZC(const CImg& img) const { + return is_sameYZC(img._height,img._depth,img._spectrum); + } + + //! Test if image width, height, depth and spectrum are equal to specified values. + /** + Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both + verified. + **/ + bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c) const { + return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c; + } + + //! Test if image width, height, depth and spectrum are the same as that of another image. + /** + Test if is_sameXYZ(const CImg&) const and is_sameC(const CImg&) const are both verified. + **/ + template + bool is_sameXYZC(const CImg& img) const { + return is_sameXYZC(img._width,img._height,img._depth,img._spectrum); + } + + //! Test if specified coordinates are inside image bounds. + /** + Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance, + and \c false otherwise. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note + - Return \c true only if all these conditions are verified: + - The image instance is \e not empty. + - 0<=x<=\ref width() - 1. + - 0<=y<=\ref height() - 1. + - 0<=z<=\ref depth() - 1. + - 0<=c<=\ref spectrum() - 1. + **/ + bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const { + return !is_empty() && x>=0 && x=0 && y=0 && z=0 && c img(100,100,1,3); // Construct a 100x100 RGB color image + const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0) + unsigned int x,y,z,c; + if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates + std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n", + offset,x,y,z,c); + } + \endcode + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z, t& c) const { + const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = (ulongT)(ppixel - _data); + const ulongT nc = off/whd; + off%=whd; + const ulongT nz = off/wh; + off%=wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc; + return true; + } + + //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y, t& z) const { + const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((ulongT)(ppixel - _data))%whd; + const ulongT nz = off/wh; + off%=wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; z = (t)nz; + return true; + } + + //! Test if pixel value is inside image bounds and get its X and Y-coordinates. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set. + **/ + template + bool contains(const T& pixel, t& x, t& y) const { + const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum; + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false; + ulongT off = ((unsigned int)(ppixel - _data))%wh; + const ulongT ny = off/_width, nx = off%_width; + x = (t)nx; y = (t)ny; + return true; + } + + //! Test if pixel value is inside image bounds and get its X-coordinate. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set. + **/ + template + bool contains(const T& pixel, t& x) const { + const T *const ppixel = &pixel; + if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false; + x = (t)(((ulongT)(ppixel - _data))%_width); + return true; + } + + //! Test if pixel value is inside image bounds. + /** + Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set. + **/ + bool contains(const T& pixel) const { + const T *const ppixel = &pixel; + return !is_empty() && ppixel>=_data && ppixel<_data + size(); + } + + //! Test if pixel buffers of instance and input images overlap. + /** + Return \c true, if pixel buffers attached to image instance and input image \c img overlap, + and \c false otherwise. + \param img Input image to compare with. + \note + - Buffer overlapping may happen when manipulating \e shared images. + - If two image buffers overlap, operating on one of the image will probably modify the other one. + - Most of the time, \c CImg instances are \e non-shared and do not overlap between each others. + \par Example + \code + const CImg + img1("reference.jpg"), // Load RGB-color image + img2 = img1.get_shared_channel(1); // Get shared version of the green channel + if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps + std::printf("Buffers overlap!\n"); + } + \endcode + **/ + template + bool is_overlapped(const CImg& img) const { + const ulongT csiz = size(), isiz = img.size(); + return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz)); + } + + //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object. + /** + Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a + valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image. + \param primitives List of primitives of the 3D object. + \param colors List of colors of the 3D object. + \param opacities List (or image) of opacities of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of + each 3D object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + template + bool is_object3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true, + char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check consistency for the particular case of an empty 3D object. + if (is_empty()) { + if (primitives || colors || opacities) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines no vertices but %u primitives, " + "%u colors and %lu opacities", + _width,primitives._width,primitives._width, + colors._width,(unsigned long)opacities.size()); + return false; + } + return true; + } + + // Check consistency of vertices. + if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)", + _width,primitives._width,_width,_height,_depth,_spectrum); + return false; + } + if (colors._width>primitives._width + 1) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines %u colors", + _width,primitives._width,colors._width); + return false; + } + if (opacities.size()>primitives._width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines %lu opacities", + _width,primitives._width,(unsigned long)opacities.size()); + return false; + } + if (!full_check) return true; + + // Check consistency of primitives. + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + const unsigned int psiz = (unsigned int)primitive.size(); + switch (psiz) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)primitive(0); + if (i0>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex index %u in " + "point primitive [%u]", + _width,primitives._width,i0,l); + return false; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + if (i0>=_width || i1>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + _width,primitives._width,i0,i1,l); + return false; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + if (i0>=_width || i1>=_width || i2>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + _width,primitives._width,i0,i1,i2,l); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = (unsigned int)primitive(3); + if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + _width,primitives._width,i0,i1,i2,i3,l); + return false; + } + } break; + default : + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines an invalid primitive [%u] of size %u", + _width,primitives._width,l,(unsigned int)psiz); + return false; + } + } + + // Check consistency of colors. + cimglist_for(colors,c) { + const CImg& color = colors[c]; + if (!color) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines no color for primitive [%u]", + _width,primitives._width,c); + return false; + } + } + + // Check consistency of light texture. + if (colors._width>primitives._width) { + const CImg &light = colors.back(); + if (!light || light._depth>1) { + if (error_message) cimg_sprintf(error_message, + "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)", + _width,primitives._width,light._width, + light._height,light._depth,light._spectrum); + return false; + } + } + + return true; + } + + //! Test if image instance represents a valid serialization of a 3D object. + /** + Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise. + \param full_check Tells if full checking of the instance must be performed. + \param[out] error_message C-string to contain the error message, if the test does not succeed. + \note + - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of + each 3D object component is checked. + - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message. + **/ + bool is_CImg3d(const bool full_check=true, char *const error_message=0) const { + if (error_message) *error_message = 0; + + // Check instance dimension and header. + if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) { + if (error_message) cimg_sprintf(error_message, + "CImg3d has invalid dimensions (%u,%u,%u,%u)", + _width,_height,_depth,_spectrum); + return false; + } + const T *ptrs = _data, *const ptre = end(); + if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') || + !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) { + if (error_message) cimg_sprintf(error_message, + "CImg3d header not found"); + return false; + } + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + + // Check consistency of number of vertices / primitives. + if (!full_check) { + const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives; + if (_data + minimal_size>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has only %lu values, while at least %lu values were expected", + nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size); + return false; + } + } + + // Check consistency of vertex data. + if (!nb_points) { + if (nb_primitives) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no vertices but %u primitives", + nb_points,nb_primitives,nb_primitives); + return false; + } + if (ptrs!=ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) is an empty object but contains %u value%s " + "more than expected", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":""); + return false; + } + return true; + } + if (ptrs + 3*nb_points>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines only %u vertices data", + nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3); + return false; + } + ptrs+=3*nb_points; + + // Check consistency of primitive data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines %u vertices but no primitive", + nb_points,nb_primitives,nb_points); + return false; + } + + if (!full_check) return true; + + for (unsigned int p = 0; p=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]", + nb_points,nb_primitives,i0,p); + return false; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + ptrs+=3; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "sphere primitive [%u]", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==6) ptrs+=4; + if (i0>=nb_points || i1>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in " + "segment primitive [%u]", + nb_points,nb_primitives,i0,i1,p); + return false; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==9) ptrs+=6; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in " + "triangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,p); + return false; + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = cimg::float2uint((float)*(ptrs++)), + i1 = cimg::float2uint((float)*(ptrs++)), + i2 = cimg::float2uint((float)*(ptrs++)), + i3 = cimg::float2uint((float)*(ptrs++)); + if (nb_inds==12) ptrs+=8; + if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in " + "quadrangle primitive [%u]", + nb_points,nb_primitives,i0,i1,i2,i3,p); + return false; + } + } break; + default : + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u", + nb_points,nb_primitives,p,nb_inds); + return false; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre)); + return false; + } + } + + // Check consistency of color data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no color/texture data", + nb_points,nb_primitives); + return false; + } + for (unsigned int c = 0; c=c) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,c); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], " + "%u values missing", + nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre)); + return false; + } + } + + // Check consistency of opacity data. + if (ptrs==ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) defines no opacity data", + nb_points,nb_primitives); + return false; + } + for (unsigned int o = 0; o=o) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) refers to invalid shared opacity index %u " + "for primitive [%u]", + nb_points,nb_primitives,w,o); + return false; + } + } else ptrs+=w*h*s; + } + if (ptrs>ptre) { + if (error_message) cimg_sprintf(error_message, + "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]", + nb_points,nb_primitives,o); + return false; + } + } + + // Check end of data. + if (ptrs1?"s":""); + return false; + } + return true; + } + + static bool _is_CImg3d(const T val, const char c) { + return val>=(T)c && val<(T)(c + 1); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + // Define the math formula parser/compiler and expression evaluator. + struct _cimg_math_parser { + CImg mem; + CImg memtype, memmerge; + CImgList _code, &code, code_begin, code_end, + _code_begin_t, &code_begin_t, _code_end_t, &code_end_t; + CImg opcode; + const CImg *p_code_end, *p_code; + const CImg *const p_break; + + CImg expr, pexpr; + const CImg& imgin; + const CImgList& listin; + CImg &imgout; + CImgList& listout; + + CImg _img_stats, &img_stats, constcache_vals; + CImgList _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm; + CImg mem_img_stats, constcache_inds; + + CImg level, variable_pos, reserved_label; + CImgList variable_def, macro_def, macro_body; + CImgList macro_body_is_string; + char *user_macro; + + unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent, result_dim, break_type, + constcache_size; + bool is_parallelizable, is_end_code, is_fill, return_new_comp, need_input_copy; + double *result; + cimg_uint64 rng; + const char *const calling_function, *s_op, *ss_op; + typedef double (*mp_func)(_cimg_math_parser&); + +#define _cimg_mp_is_constant(arg) (memtype[arg]==1) // Is constant value? +#define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value? +#define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value? +#define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)? +#define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector? +#define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN) +#define _cimg_mp_calling_function s_calling_function()._data +#define _cimg_mp_op(s) s_op = s; ss_op = ss +#define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char) +#define _cimg_mp_check_constant(arg,n_arg,mode) check_constant(arg,n_arg,mode,ss,se,saved_char) +#define _cimg_mp_check_constant_index(arg) check_constant_index(arg,ss,se,saved_char) +#define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char) +#define _cimg_mp_check_list(is_out) check_list(is_out,ss,se,saved_char) +#define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp) +#define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; } +#define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan) +#define _cimg_mp_constant(val) _cimg_mp_return(constant((double)(val))) +#define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op)) +#define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1)) +#define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2)) +#define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3)) +#define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4)) +#define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5)) +#define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6)) +#define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7)) +#define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1)) +#define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2)) +#define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2)) +#define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2)) +#define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3)) +#define _cimg_mp_strerr \ + *se = saved_char; \ + for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \ + if (*s0==';') ++s0; \ + while (cimg::is_blank(*s0)) ++s0; \ + cimg::strellipsize(s0,64) + + // Constructors / Destructors. + ~_cimg_math_parser() { + cimg::srand(rng); + } + + _cimg_math_parser(const char *const expression, const char *const funcname=0, + const CImg& img_input=CImg::const_empty(), CImg *const img_output=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0, + const bool _is_fill=false): + code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), + p_break((CImg*)(cimg_ulong)-2), + imgin(img_input),listin(list_inputs?*list_inputs:CImgList::const_empty()), + imgout(img_output?*img_output:CImg::empty()),listout(list_outputs?*list_outputs:CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0), + mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),break_type(0), + constcache_size(0),is_parallelizable(true),is_fill(_is_fill),need_input_copy(false), + rng((cimg::_rand(),cimg::rng())),calling_function(funcname?funcname:"cimg_math_parser") { + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + if (!expression || !*expression) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Empty expression.", + pixel_type(),_cimg_mp_calling_function); + const char *_expression = expression; + while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression; + CImg::string(_expression).move_to(expr); + char *ps = &expr.back() - 1; + while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps; + *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1); + + // Ease the retrieval of previous non-space characters afterwards. + pexpr.assign(expr._width); + char c, *pe = pexpr._data; + for (ps = expr._data, c = ' '; *ps; ++ps) { + if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' '; + *(pe++) = c; + } + *pe = 0; + level = get_level(expr); + + // Init constant values. +#define _cimg_mp_interpolation (reserved_label[30]!=~0U?reserved_label[30]:0) +#define _cimg_mp_boundary (reserved_label[31]!=~0U?reserved_label[31]:0) +#define _cimg_mp_slot_t 17 +#define _cimg_mp_slot_nan 29 +#define _cimg_mp_slot_x 30 +#define _cimg_mp_slot_y 31 +#define _cimg_mp_slot_z 32 +#define _cimg_mp_slot_c 33 + + mem.assign(96); + for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10 + for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5 + mem[16] = 0.5; + mem[_cimg_mp_slot_t] = 0; // thread_id + mem[18] = (double)imgin._width; // w + mem[19] = (double)imgin._height; // h + mem[20] = (double)imgin._depth; // d + mem[21] = (double)imgin._spectrum; // s + mem[22] = (double)imgin._is_shared; // r + mem[23] = (double)imgin._width*imgin._height; // wh + mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd + mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds + mem[26] = (double)listin._width; // l + mem[27] = std::exp(1.); // e + mem[28] = cimg::PI; // pi + mem[_cimg_mp_slot_nan] = cimg::type::nan(); // nan + + // Set value property : + // { -1 = reserved (e.g. variable) | 0 = computation value | + // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }. + memtype.assign(mem._width,1,1,1,0); + for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1; + memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] = + memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1; + mempos = _cimg_mp_slot_c + 1; + variable_pos.assign(8); + + reserved_label.assign(128,1,1,1,~0U); + // reserved_label[0-31] are used to store the memory index of these variables: + // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, + // [8] = is, [9] = ip, [10] = ic, [11] = in, [12] = xm, [13] = ym, [14] = zm, [15] = cm, [16] = xM, + // [17] = yM, [18] = zM, [19] = cM, [20] = i0...[29] = i9, [30] = interpolation, [31] = boundary + + // Compile expression into a sequence of opcodes. + s_op = ""; ss_op = expr._data; + const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,false); + if (!_cimg_mp_is_constant(ind_result)) { + if (_cimg_mp_is_vector(ind_result)) + CImg(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true). + fill(cimg::type::nan()); + else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type::nan(); + } + + // Free resources used for compiling expression and prepare evaluation. + result_dim = _cimg_mp_size(ind_result); + if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1); + result = mem._data + ind_result; + memtype.assign(); + constcache_vals.assign(); + constcache_inds.assign(); + level.assign(); + variable_pos.assign(); + reserved_label.assign(); + expr.assign(); + pexpr.assign(); + opcode.assign(); + opcode._is_shared = true; + + // Execute begin() bloc if any specified. + if (code_begin) { + mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_begin.end(); + for (p_code = code_begin; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + p_code_end = code.end(); + } + + _cimg_math_parser(): + code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t), + p_code_end(0),p_break((CImg*)(cimg_ulong)-2), + imgin(CImg::const_empty()),listin(CImgList::const_empty()), + imgout(CImg::empty()),listout(CImgList::empty()), + img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0), + result_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),is_fill(false), + need_input_copy(false),rng(0),calling_function(0) { + mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()() + result = mem._data; + } + + _cimg_math_parser(const _cimg_math_parser& mp): + mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t), + p_code_end(mp.p_code_end),p_break(mp.p_break), + imgin(mp.imgin),listin(mp.listin),imgout(mp.imgout),listout(mp.listout), + img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm), + debug_indent(0),result_dim(mp.result_dim),break_type(0),constcache_size(0), + is_parallelizable(mp.is_parallelizable),is_fill(mp.is_fill), + need_input_copy(mp.need_input_copy),result(mem._data + (mp.result - mp.mem._data)), + rng((cimg::_rand(),cimg::rng())),calling_function(0) { + +#if cimg_use_openmp!=0 + mem[_cimg_mp_slot_t] = omp_get_thread_num(); + rng+=omp_get_thread_num(); +#endif + opcode.assign(); + opcode._is_shared = true; + } + + // Compilation procedure. + unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref, + const bool is_critical) { + if (depth>256) { + cimg::strellipsize(expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Call stack overflow (infinite recursion?), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + (ss - 4)>expr._data?"...":"", + (ss - 4)>expr._data?ss - 4:expr._data, + se<&expr.back()?"...":""); + } + char c1, c2; + + // Simplify expression when possible. + do { + c2 = 0; + if (ssss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; + } + while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { + ++ss; --se; c2 = 1; + } + } while (c2 && ss::%s: %s%s Missing %s, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + *s_op=='F'?"argument":"item", + (ss_op - 4)>expr._data?"...":"", + (ss_op - 4)>expr._data?ss_op - 4:expr._data, + ss_op + std::strlen(ss_op)<&expr.back()?"...":""); + } + + static const size_t siz_ref = 7*sizeof(unsigned int); + const char *const previous_s_op = s_op, *const previous_ss_op = ss_op; + const unsigned int depth1 = depth + 1; + unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6; + char + *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3, + *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4, + *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8, + *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0; + double val = 0, val1, val2; + mp_func op; + return_new_comp = false; + + // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value + // linked to the returned memory slot (reference that cannot be determined at compile time). + // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) | + // 3 = image value (coordinates) | 4 = image value as a vector (offsets) | + // 5 = image value as a vector (coordinates) }. + // Depending on p_ref[0], the remaining p_ref[k] have the following meaning: + // When p_ref[0]==0, p_ref is actually unlinked. + // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ]. + // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ]. + // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ]. + // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ]. + if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; } + + const char saved_char = *se; *se = 0; + const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1; + bool is_sth, is_relative; + CImg ref; + CImg variable_name; + CImgList l_opcode; + + // Look for a single value or a pre-defined variable. + int nb = 0; + s = ss + (*ss=='+' || *ss=='-'?1:0); + if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf + is_sth = *ss=='-'; + if (!cimg::strcasecmp(s,"inf")) { val = cimg::type::inf(); nb = 1; } + else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type::nan(); nb = 1; } + if (nb==1 && is_sth) val = -val; + } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number + is_sth = *ss=='-'; + if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) { + nb = 1; + val = (double)arg1; + if (is_sth) val = -val; + } + } + if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0)); + if (nb==1) _cimg_mp_constant(val); + if (nb==2 && sep=='%') _cimg_mp_constant(val/100); + + if (ss1==se) switch (*ss) { // One-char reserved variable + case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c); + case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20); + case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27); + case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19); + case 'k' : + if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']); + pos = get_mem_img_index(); + if (pos!=~0U) _cimg_mp_return(pos); + _cimg_mp_return_nan(); + case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26); + case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22); + case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21); + case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t); + case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18); + case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x); + case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y); + case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z); + case 'u' : + if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']); + _cimg_mp_scalar2(mp_u,0,1); + case 'g' : + if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']); + _cimg_mp_scalar0(mp_g); + case 'i' : + if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']); + _cimg_mp_scalar0(mp_i); + case 'I' : + _cimg_mp_op("Variable 'I'"); + if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']); + if (!imgin._spectrum) _cimg_mp_return(0); + need_input_copy = true; + pos = vector(imgin._spectrum); + CImg::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + case 'R' : + if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0); + case 'G' : + if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0); + case 'B' : + if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0); + case 'A' : + if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0); + } + else if (ss2==se) { // Two-chars reserved variable + arg1 = arg2 = ~0U; + if (*ss=='w' && *ss1=='h') // wh + _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23); + if (*ss=='p' && *ss1=='i') // pi + _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28); + if (*ss=='i') { + if (*ss1>='0' && *ss1<='9') { // i0...i9 + pos = 20 + *ss1 - '0'; + if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]); + need_input_copy = true; + _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 20,0,0); + } + switch (*ss1) { + case 'm' : arg1 = 4; arg2 = 0; break; // im + case 'M' : arg1 = 5; arg2 = 1; break; // iM + case 'a' : arg1 = 6; arg2 = 2; break; // ia + case 'v' : arg1 = 7; arg2 = 3; break; // iv + case 's' : arg1 = 8; arg2 = 12; break; // is + case 'p' : arg1 = 9; arg2 = 13; break; // ip + case 'c' : // ic + if (reserved_label[10]!=~0U) _cimg_mp_return(reserved_label[10]); + if (mem_img_median==~0U) mem_img_median = imgin?constant(imgin.median()):0; + _cimg_mp_return(mem_img_median); + break; + case 'n' : // in + if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]); + if (mem_img_norm==~0U) mem_img_norm = imgin?constant(imgin.magnitude()):0; + _cimg_mp_return(mem_img_norm); + } + } + else if (*ss1=='m') switch (*ss) { + case 'x' : arg1 = 12; arg2 = 4; break; // xm + case 'y' : arg1 = 13; arg2 = 5; break; // ym + case 'z' : arg1 = 14; arg2 = 6; break; // zm + case 'c' : arg1 = 15; arg2 = 7; break; // cm + } + else if (*ss1=='M') switch (*ss) { + case 'x' : arg1 = 16; arg2 = 8; break; // xM + case 'y' : arg1 = 17; arg2 = 9; break; // yM + case 'z' : arg1 = 18; arg2 = 10; break; // zM + case 'c' : arg1 = 19; arg2 = 11; break; // cM + } + if (arg1!=~0U) { + if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]); + if (!img_stats) { + img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false); + mem_img_stats.assign(1,14,1,1,~0U); + } + if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = constant(img_stats[arg2]); + _cimg_mp_return(mem_img_stats[arg2]); + } + } else if (ss3==se) { // Three-chars reserved variable + if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd + _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24); + } else if (ss4==se) { // Four-chars reserved variable + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds + _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25); + } + + pos = ~0U; + is_sth = false; + for (s0 = ss, s = ss1; s='i'?1:3,0); + if (_cimg_mp_is_vector(arg2)) { + if (p1!=~0U) { + _cimg_mp_check_constant_index(p1); + p3 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + p2 = listin[p3]._spectrum; + } else p2 = imgin._spectrum; + if (!p2) _cimg_mp_return(0); + _cimg_mp_check_type(arg2,2,2,p2); + } else p2 = 0; + + if (p_ref) { + *p_ref = _cimg_mp_is_vector(arg2)?4:2; + p_ref[1] = p1; + p_ref[2] = (unsigned int)is_relative; + p_ref[3] = arg1; + if (_cimg_mp_is_vector(arg2)) + set_reserved_vector(arg2); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1; + } + + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg1).move_to(code); + else if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg1).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg1,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value + if (!is_critical) is_parallelizable = false; + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0='i'?1:3,0); + if (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + } else if (s1='i') + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg5,p1,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg5,p1,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg5); + if (*ss>='i') + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg5,arg1,arg2,arg3,arg4).move_to(code); + else if (_cimg_mp_is_scalar(arg5)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg5,arg1,arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code); + } + _cimg_mp_return(arg5); + } + } + + // Assign vector value (direct). + if (l_variable_name>3 && *ve1==']' && *ss!='[') { + s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss && is_varname(ss,s0 - ss)) { + variable_name[s0 - ss] = 0; // Remove brackets in variable name + get_variable_pos(variable_name,arg1,arg2); + arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot + if (arg1==~0U || _cimg_mp_is_scalar(arg1)) + compile(ss,s0,depth1,0,is_critical); // Variable does not exist or is not a vector -> error + + arg2 = compile(++s0,ve1,depth1,0,is_critical); // Index + arg3 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + _cimg_mp_check_type(arg3,2,1,0); + + if (_cimg_mp_is_constant(arg2)) { // Constant index -> return corresponding variable slot directly + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) { + arg1+=nb + 1; + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + _cimg_mp_return(arg1); + } + compile(ss,s,depth1,0,is_critical); // Out-of-bounds reference -> error + } + + // Case of non-constant index -> return assigned value + linked reference + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; + } + CImg::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg3); + } + } + + // Assign user-defined macro. + if (l_variable_name>2 && *ve1==')' && *ss!='(') { + s0 = ve1; while (s0>ss && *s0!='(') --s0; + if (is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) && + std::strncmp(variable_name,"print(",6)) { // Valid macro name + s0 = variable_name._data + (s0 - ss); + *s0 = 0; + s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis + CImg(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0); + ++s; while (*s && cimg::is_blank(*s)) ++s; + CImg(s,(unsigned int)(se - s + 1)).move_to(macro_body,0); + + p1 = 1; // Index of current parsed argument + for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments + if (p1>24) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too much specified arguments (>24) in macro " + "definition '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && p1==1) break; // Function has no arguments + + s2 = s; // Start of the argument name + is_sth = true; // is_valid_argument_name? + if (*s>='0' && *s<='9') is_sth = false; + else for (ns = s; ns::%s: %s: %s name specified for argument %u when defining " + "macro '%s()', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + is_sth?"Empty":"Invalid",p1, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (ns==s1 || *ns==',') { // New argument found + *s3 = 0; + p2 = (unsigned int)(s3 - s2); // Argument length + for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number + if (!((ps>macro_body[0]._data && is_varchar(*(ps - 1))) || + (ps + p2macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign + *(ps - 1) = (char)p1; + if (ps + p26 && !std::strncmp(variable_name,"const ",6); + s0 = variable_name._data; + if (is_const) { + s0+=6; while (cimg::is_blank(*s0)) ++s0; + variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1); + } + if (is_varname(variable_name)) { // Valid variable name + + // Assign variable (direct). + get_variable_pos(variable_name,arg1,arg2); + arg3 = compile(s + 1,se,depth1,0,is_critical); + is_sth = return_new_comp; // is arg3 a new blank object? + if (is_const) _cimg_mp_check_constant(arg3,2,0); + arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; + + if (arg1==~0U) { // Create new variable + if (_cimg_mp_is_vector(arg3)) { // Vector variable + arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3); + set_reserved_vector(arg1); // Prevent from being used in further optimization + } else { // Scalar variable + if (is_const) arg1 = arg3; + else { + arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3); + memtype[arg1] = -1; + } + } + + if (arg2!=~0U) reserved_label[arg2] = arg1; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg1; + variable_name.move_to(variable_def); + } + + } else { // Variable already exists -> assign a new value + if (is_const || _cimg_mp_is_constant(arg1)) { + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"already-defined ":"non-", + variable_name._data, + !_cimg_mp_is_constant(arg1) && is_const?" as a new const variable":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1)) { // Vector + if (_cimg_mp_is_vector(arg3)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3). + move_to(code); + } else // Scalar + CImg::vector((ulongT)mp_copy,arg1,arg3).move_to(code); + } + return_new_comp = false; + _cimg_mp_return(arg1); + } + + // Assign lvalue (variable name was not valid for a direct assignment). + arg1 = ~0U; + is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator? + if (is_sth) break; // Do nothing and make ternary operator prioritary over assignment + + if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) { + ref.assign(7); + arg1 = compile(ss,s,depth1,ref,is_critical); // Lvalue slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to assign + + if (*ref==1) { // Vector value (scalar): V[k] = scalar + _cimg_mp_check_type(arg2,2,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(arg2); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg2,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg2,arg3).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg2,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg2); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg2,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg2,p1,arg3).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code); + } + + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg2,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg2,arg3,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg2,p1,arg3,arg4,arg5).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); + } + + } else { + if (!imgout) _cimg_mp_return(arg2); + if (_cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg2,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code); + } + _cimg_mp_return(arg2); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg2)) // From vector + CImg::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)). + move_to(code); + else // From scalar + CImg::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2). + move_to(code); + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar + _cimg_mp_check_type(arg2,2,1,0); + CImg::vector((ulongT)mp_copy,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + } + + // No assignment expressions match -> error + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg1!=~0U && _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++. + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps && + level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=) + _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='"); + + ref.assign(7); + arg1 = compile(ss,ns,depth1,ref,is_critical); // Vector slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Right operand + _cimg_mp_check_type(arg1,1,2,2); + _cimg_mp_check_type(arg2,2,3,2); + if (_cimg_mp_is_vector(arg2)) { // Complex **= complex + if (*ps=='*') + CImg::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code); + else if (*ps=='/') + CImg::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code); + } else { // Complex **= scalar + if (*ps=='*') { + if (arg2==1) _cimg_mp_return(arg1); + self_vector_s(arg1,mp_self_mul,arg2); + } else if (*ps=='/') { + if (arg2==1) _cimg_mp_return(arg1); + self_vector_s(arg1,mp_self_div,arg2); + } else { + if (arg2==1) _cimg_mp_return(arg1); + CImg::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code); + } + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + + } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + + _cimg_mp_return(arg1); + } + + for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1 + if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || *ps=='%' || + *ps=='&' || *ps=='^' || *ps=='|' || + (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) && + level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=) + switch (*ps) { + case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break; + case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break; + case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break; + case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break; + case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break; + case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break; + case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break; + case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break; + case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break; + default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break; + } + s1 = *ps=='>' || *ps=='<'?ns:ps; + + ref.assign(7); + arg1 = compile(ss,s1,depth1,ref,is_critical); // Variable slot + arg2 = compile(s + 1,se,depth1,0,is_critical); // Value to apply + + // Check for particular case to be simplified. + if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1); + if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1); + + // Apply operator on a copy to prevent modifying a constant or a variable. + if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k] += scalar + _cimg_mp_check_type(arg2,2,1,0); + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(arg1); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,1,0); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value + if (!is_critical) is_parallelizable = false; + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(arg1); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector + else self_vector_s(arg1,op,arg2); // Vector += scalar + _cimg_mp_return(arg1); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar + _cimg_mp_check_type(arg2,2,1,0); + CImg::vector((ulongT)op,arg1,arg2).move_to(code); + _cimg_mp_return(arg1); + } + + variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0; + cimg::strpare(variable_name,false,true); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + for (s = ss1; s::vector((ulongT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||') + _cimg_mp_op("Operator '||'"); + arg1 = compile(ss,s,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,1,0); + if (arg1>0 && arg1<=16) _cimg_mp_return(1); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] || mem[arg2]); + if (!arg1) _cimg_mp_return(arg2); + pos = scalar(); + CImg::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&') + _cimg_mp_op("Operator '&&'"); + arg1 = compile(ss,s,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,1,0); + if (!arg1) _cimg_mp_return(0); + p2 = code._width; + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,1,0); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(mem[arg1] && mem[arg2]); + if (arg1>0 && arg1<=16) _cimg_mp_return(arg2); + pos = scalar(); + CImg::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2). + move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se2; s>ss; --s) + if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|') + _cimg_mp_op("Operator '|'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + if (!arg1) _cimg_mp_return(arg2); + _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1] | (longT)mem[arg2]); + if (!arg2) _cimg_mp_return(arg1); + if (!arg1) _cimg_mp_return(arg2); + _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2); + } + + for (s = se2; s>ss; --s) + if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&') + _cimg_mp_op("Operator '&'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1] & (longT)mem[arg2]); + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=') + _cimg_mp_op("Operator '!='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + if (arg1==arg2) _cimg_mp_return(0); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); + if (p1 || p2) { + if (p1 && p2 && p1!=p2) _cimg_mp_return(1); + pos = scalar(); + CImg::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]!=mem[arg2]); + _cimg_mp_scalar2(mp_neq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==') + _cimg_mp_op("Operator '=='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + if (arg1==arg2) _cimg_mp_return(1); + p1 = _cimg_mp_size(arg1); + p2 = _cimg_mp_size(arg2); + if (p1 || p2) { + if (p1 && p2 && p1!=p2) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]==mem[arg2]); + _cimg_mp_scalar2(mp_eq,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=') + _cimg_mp_op("Operator '<='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]<=mem[arg2]); + if (arg1==arg2) _cimg_mp_return(1); + _cimg_mp_scalar2(mp_lte,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=') + _cimg_mp_op("Operator '>='"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>=mem[arg2]); + if (arg1==arg2) _cimg_mp_return(1); + _cimg_mp_scalar2(mp_gte,arg1,arg2); + } + + for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps) + if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<') + _cimg_mp_op("Operator '<'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]ss; --s, --ns, --ps) + if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>') + _cimg_mp_op("Operator '>'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]>mem[arg2]); + if (arg1==arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_gt,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<') + _cimg_mp_op("Operator '<<'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1]<<(unsigned int)mem[arg2]); + if (!arg1) _cimg_mp_return(0); + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>') + _cimg_mp_op("Operator '>>'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) { + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2); + } + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant((longT)mem[arg1]>>(unsigned int)mem[arg2]); + if (!arg1) _cimg_mp_return(0); + if (!arg2) _cimg_mp_return(arg1); + _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Addition ('+') + _cimg_mp_op("Operator '+'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (!arg2) _cimg_mp_return(arg1); + if (!arg1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] + mem[arg2]); + if (code) { // Try to spot linear case 'a*b + c' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); + _cimg_mp_return(arg3); + } + } + if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1); + if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2); + _cimg_mp_scalar2(mp_add,arg1,arg2); + } + + for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps) + if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && + *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' && + (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' && + *(ps - 1)<='9')))) && + level[s - expr._data]==clevel) { // Subtraction ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (!arg2) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2); + _cimg_mp_vector2_sv(mp_sub,arg1,arg2); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1] - mem[arg2]); + if (!arg1) _cimg_mp_scalar1(mp_minus,arg2); + if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right), + arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code); + _cimg_mp_return(arg3); + } + } + if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1); + _cimg_mp_scalar2(mp_sub,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**') + _cimg_mp_op("Operator '**'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + if (arg1==1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//') + _cimg_mp_op("Operator '//'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) { + pos = vector(2); + CImg::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (!arg1) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*') + _cimg_mp_op("Operator '*'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + p2 = _cimg_mp_size(arg2); + if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication + pos = vector(p2); + CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (arg1==1) _cimg_mp_return(arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]*mem[arg2]); + + if (code) { // Try to spot double multiplication 'a*b*c' + CImg &pop = code.back(); + if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) { + arg3 = (unsigned int)pop[1]; + arg4 = (unsigned int)pop[2]; + arg5 = (unsigned int)pop[3]; + code.remove(); + CImg::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code); + _cimg_mp_return(arg3); + } + } + if (!arg1 || !arg2) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_mul,arg1,arg2); + } + + for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/') + _cimg_mp_op("Operator '/'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) _cimg_mp_constant(mem[arg1]/mem[arg2]); + if (!arg1) _cimg_mp_return(0); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + for (s = se2, ns = se1; s>ss; --s, --ns) + if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%') + _cimg_mp_op("Operator '%'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(cimg::mod(mem[arg1],mem[arg2])); + _cimg_mp_scalar2(mp_modulo,arg1,arg2); + } + + if (se1>ss) { + if (*ss=='+' && (*ss1!='+' || (ss2='0' && *ss2<='9'))) { // Unary plus ('+') + _cimg_mp_op("Operator '+'"); + _cimg_mp_return(compile(ss1,se,depth1,0,is_critical)); + } + + if (*ss=='-' && (*ss1!='-' || (ss2='0' && *ss2<='9'))) { // Unary minus ('-') + _cimg_mp_op("Operator '-'"); + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(-mem[arg1]); + _cimg_mp_scalar1(mp_minus,arg1); + } + + if (*ss=='!') { // Logical not ('!') + _cimg_mp_op("Operator '!'"); + if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)' + arg1 = compile(ss2,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + _cimg_mp_scalar1(mp_bool,arg1); + } + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(!mem[arg1]); + _cimg_mp_scalar1(mp_logical_not,arg1); + } + + if (*ss=='~') { // Bitwise not ('~') + _cimg_mp_op("Operator '~'"); + arg1 = compile(ss1,se,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(~(unsigned int)mem[arg1]); + _cimg_mp_scalar1(mp_bitwise_not,arg1); + } + } + + for (s = se3, ns = se2; s>ss; --s, --ns) + if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^') + _cimg_mp_op("Operator '^^'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 2,se,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,3,2); + _cimg_mp_check_type(arg2,2,3,2); + if (arg2==1) _cimg_mp_return(arg1); + pos = vector(2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) + CImg::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code); + else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) + CImg::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code); + else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) + CImg::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code); + else + CImg::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + for (s = se2; s>ss; --s) + if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^') + _cimg_mp_op("Operator '^'"); + arg1 = compile(ss,s,depth1,0,is_critical); + arg2 = compile(s + 1,se,depth1,0,is_critical); + _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1)); + if (arg2==1) _cimg_mp_return(arg1); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2); + if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2); + if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2); + if (_cimg_mp_is_constant(arg1) && _cimg_mp_is_constant(arg2)) + _cimg_mp_constant(std::pow(mem[arg1],mem[arg2])); + switch (arg2) { + case 0 : _cimg_mp_return(1); + case 2 : _cimg_mp_scalar1(mp_sqr,arg1); + case 3 : _cimg_mp_scalar1(mp_pow3,arg1); + case 4 : _cimg_mp_scalar1(mp_pow4,arg1); + default : + if (_cimg_mp_is_constant(arg2)) { + if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); } + else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); } + } + _cimg_mp_scalar2(mp_pow,arg1,arg2); + } + } + + // Percentage computation. + if (*se1=='%') { + arg1 = compile(ss,se1,depth1,0,is_critical); + arg2 = _cimg_mp_is_constant(arg1)?0:constant(100); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(mem[arg1]/100); + _cimg_mp_scalar2(mp_div,arg1,arg2); + } + + is_sth = ss1ss && (*se1=='+' || *se1=='-') && *se2==*se1)) { // Pre/post-decrement and increment + if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) { + _cimg_mp_op("Operator '++'"); + op = mp_self_increment; + } else { + _cimg_mp_op("Operator '--'"); + op = mp_self_decrement; + } + ref.assign(7); + arg1 = is_sth?compile(ss2,se,depth1,ref,is_critical): + compile(ss,se2,depth1,ref,is_critical); // Variable slot + + // Apply operator on a copy to prevent modifying a constant or a variable. + if (*ref && (_cimg_mp_is_constant(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) { + if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1); + else arg1 = scalar1(mp_copy,arg1); + } + + if (is_sth) pos = arg1; // Determine return index, depending on pre/post action + else { + if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1); + else pos = scalar1(mp_copy,arg1); + } + + if (*ref==1) { // Vector value (scalar): V[k]++ + arg3 = ref[1]; // Vector slot + arg4 = ref[2]; // Index + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1,1).move_to(code); + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + _cimg_mp_return(pos); + } + + if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + arg6 = ref[6]; // C + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + CImg::vector((ulongT)op,arg1).move_to(code); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==4) { // Image value (vector): I/J[_#ind,off]++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // Offset + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++ + if (!is_critical) is_parallelizable = false; + p1 = ref[1]; // Index + is_relative = (bool)ref[2]; + arg3 = ref[3]; // X + arg4 = ref[4]; // Y + arg5 = ref[5]; // Z + if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int)); + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + if (p1!=~0U) { + if (!listout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } else { + if (!imgout) _cimg_mp_return(pos); + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + _cimg_mp_return(pos); + } + + if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++ + self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1); + _cimg_mp_return(pos); + } + + if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++ + CImg::vector((ulongT)op,arg1).move_to(code); + _cimg_mp_return(pos); + } + + if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1)); + else variable_name.assign(ss,(unsigned int)(se1 - ss)); + variable_name.back() = 0; + cimg::strpare(variable_name,false,true); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid %slvalue '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"const ":"", + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'. + if (*se1==']') { + _cimg_mp_op("Value accessor '[]'"); + + // Find opening bracket for the offset. + s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } + is_sth=s0>ss && *(s0-1)==']'; // Particular case s.a. '..[..][..]' ? + is_relative = *ss=='j' || *ss=='J'; + + if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' && + (reserved_label[(int)*ss]==~0U || + !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff), + pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); + } else { + need_input_copy = true; + CImg::vector((ulongT)(is_relative?mp_Joff:mp_Ioff), + pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' && + (reserved_label[(int)*ss]==~0U || + !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s0ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0; + if (s0>ss) { // Vector element + arg1 = compile(ss,s0,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) { + variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + + } + s1 = s0 + 1; while (s1 sub-vector extraction + p1 = _cimg_mp_size(arg1); + arg2 = compile(++s0,s1,depth1,0,is_critical); // Starting index + s0 = ++s1; while (s0::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // One argument -> vector value reference + arg2 = compile(++s0,se1,depth1,0,is_critical); + if (_cimg_mp_is_constant(arg2)) { // Constant index + nb = (int)mem[arg2]; + if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb); + variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' " + "(vector '%s' has dimension %u), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data,nb, + variable_name._data,_cimg_mp_size(arg1), + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (p_ref) { + *p_ref = 1; + p_ref[1] = arg1; + p_ref[2] = arg2; + if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization + } + pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2); + memtype[pos] = -1; // Prevent from being used in further optimization + _cimg_mp_return(pos); + } + } + + // Look for a function call, an access to image value, or a parenthesis. + if (*se1==')') { + if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,is_critical)); // Simple parentheses + _cimg_mp_op("Value accessor '()'"); + is_relative = *ss=='j' || *ss=='J'; + s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); } + + // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions) + if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + if (s1::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz), + pos,p1,arg1,arg2,arg3, + arg4==~0U?_cimg_mp_interpolation:arg4, + arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); + else { + need_input_copy = true; + CImg::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz), + pos,arg1,arg2,arg3, + arg4==~0U?_cimg_mp_interpolation:arg4, + arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions) + if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar + if (*ss2=='#') { // Index specified + s0 = ss3; while (s01) { + arg2 = arg1 + 1; + if (p2>2) { + arg3 = arg2 + 1; + if (p2>3) arg4 = arg3 + 1; + } + } + if (s1::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode); + for (s = ++s2; s::vector(arg3).move_to(l_opcode); + ++p3; + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (_cimg_mp_is_constant(arg1)) { + p3-=1; // Number of args + if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1); + else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]); + if (arg1::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test) + _cimg_mp_op("Function 'breakpoint()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"bool(",5)) { // Boolean cast + _cimg_mp_op("Function 'bool()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((bool)mem[arg1]); + _cimg_mp_scalar1(mp_bool,arg1); + } + + if (!std::strncmp(ss,"begin(",6)) { // Begin + _cimg_mp_op("Function 'begin()'"); + code.swap(code_begin); + arg1 = compile(ss6,se1,depth1,p_ref,true); + code.swap(code_begin); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread + _cimg_mp_op("Function 'begin_t()'"); + code.swap(code_begin_t); + arg1 = compile(ss8,se1,depth1,p_ref,true); + code.swap(code_begin_t); + _cimg_mp_return(arg1); + } + break; + + case 'c' : + if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value + _cimg_mp_op("Function 'cabs()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0); + _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2); + } + + if (!std::strncmp(ss,"carg(",5)) { // Complex argument + _cimg_mp_op("Function 'carg()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1); + _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1); + } + + if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root + _cimg_mp_op("Function 'cbrt()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::cbrt(mem[arg1])); + _cimg_mp_scalar1(mp_cbrt,arg1); + } + + if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate + _cimg_mp_op("Function 'cconj()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ceil(",5)) { // Ceil + _cimg_mp_op("Function 'ceil()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::ceil(mem[arg1])); + _cimg_mp_scalar1(mp_ceil,arg1); + } + + if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential + _cimg_mp_op("Function 'cexp()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm + _cimg_mp_op("Function 'clog()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine + _cimg_mp_op("Function 'ccos()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"csin(",5)) { // Complex sine + _cimg_mp_op("Function 'csin()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent + _cimg_mp_op("Function 'ctan()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine + _cimg_mp_op("Function 'ccosh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine + _cimg_mp_op("Function 'csinh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent + _cimg_mp_op("Function 'ctanh()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,0,3,2); + pos = vector(2); + if (_cimg_mp_is_scalar(arg1)) CImg::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code); + else CImg::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"continue(",9)) { // Continue loop + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"copy(",5)) { // Memory copy + _cimg_mp_op("Function 'copy()'"); + ref.assign(14); + s1 = ss5; while (s1=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]); + } + if (_cimg_mp_is_vector(arg2)) { + if (arg3==~0U) arg3 = constant(_cimg_mp_size(arg2)); + if (!ref[7]) ++arg2; + if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]); + } + if (arg3==~0U) arg3 = 1; + if (arg4==~0U) arg4 = 1; + if (arg5==~0U) arg5 = 1; + _cimg_mp_check_type(arg3,3,1,0); + _cimg_mp_check_type(arg4,4,1,0); + _cimg_mp_check_type(arg5,5,1,0); + _cimg_mp_check_type(arg6,5,1,0); + CImg(1,22).move_to(code); + code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6); + code.back().get_shared_rows(8,21).fill(ref); + _cimg_mp_return(p1); + } + + if (!std::strncmp(ss,"cos(",4)) { // Cosine + _cimg_mp_op("Function 'cos()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cos(mem[arg1])); + _cimg_mp_scalar1(mp_cos,arg1); + } + + if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine + _cimg_mp_op("Function 'cosh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::cosh(mem[arg1])); + _cimg_mp_scalar1(mp_cosh,arg1); + } + + if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time) + _cimg_mp_op("Function 'critical()'"); + p1 = code._width; + arg1 = compile(ss + 9,se1,depth1,p_ref,true); + CImg::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"crop(",5)) { // Image crop + _cimg_mp_op("Function 'crop()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)); + opcode.resize(1,std::min(opcode._height,4U),1,1,0).move_to(l_opcode); + is_sth = true; + } else { + _cimg_mp_check_type(arg1,pos + 1,1,0); + CImg::vector(arg1).move_to(l_opcode); + } + s = ns; + } + (l_opcode>'y').move_to(opcode); + + arg1 = 0; arg2 = (p1!=~0U); + switch (opcode._height) { + case 0 : case 1 : + CImg::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode); + break; + case 2 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + 2; + break; + case 3 : + CImg::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode); + arg1 = arg2 + 2; + break; + case 4 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary). + move_to(opcode); + arg1 = arg2 + (is_sth?2:3); + break; + case 5 : + CImg::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]). + move_to(opcode); + arg1 = arg2 + (is_sth?2:3); + break; + case 6 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + _cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + (is_sth?2:4); + break; + case 7 : + CImg::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U, + opcode[6]).move_to(opcode); + arg1 = arg2 + (is_sth?2:4); + break; + case 8 : + CImg::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6], + opcode[7],_cimg_mp_boundary).move_to(opcode); + arg1 = arg2 + (is_sth?2:5); + break; + case 9 : + arg1 = arg2 + (is_sth?2:5); + break; + default : // Error -> too much arguments + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Too much arguments specified, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + _cimg_mp_check_type((unsigned int)*opcode,arg2 + 1,1,0); + _cimg_mp_check_type((unsigned int)opcode[1],arg2 + 1 + (is_sth?0:1),1,0); + _cimg_mp_check_type((unsigned int)opcode[2],arg2 + 1 + (is_sth?0:2),1,0); + _cimg_mp_check_type((unsigned int)opcode[3],arg2 + 1 + (is_sth?0:3),1,0); + if (opcode[4]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[4],arg1,3); + opcode[4] = (ulongT)mem[opcode[4]]; + } + if (opcode[5]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[5],arg1 + 1,3); + opcode[5] = (ulongT)mem[opcode[5]]; + } + if (opcode[6]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[6],arg1 + 2,3); + opcode[6] = (ulongT)mem[opcode[6]]; + } + if (opcode[7]!=(ulongT)~0U) { + _cimg_mp_check_constant((unsigned int)opcode[7],arg1 + 3,3); + opcode[7] = (ulongT)mem[opcode[7]]; + } + _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0); + + if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U || + opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) { + p2 = 0; + if (p1!=~0U) { + _cimg_mp_check_constant(p1,1,1); + p2 = (unsigned int)cimg::mod((int)mem[p1],listin.width()); + } + const CImg &img = p1!=~0U?listin[p2]:imgin; + if (!img) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Cannot crop empty image when " + "some xyzc-coordinates are unspecified, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width; + if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height; + if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth; + if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum; + } + + pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7])); + CImg::vector((ulongT)mp_crop, + pos,p1, + *opcode,opcode[1],opcode[2],opcode[3], + opcode[4],opcode[5],opcode[6],opcode[7], + opcode[8]).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cross(",6)) { // Cross product + _cimg_mp_op("Function 'cross()'"); + s1 = ss6; while (s1::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"cut(",4)) { // Cut + _cimg_mp_op("Function 'cut()'"); + s1 = ss4; while (s1val2?val2:val); + } + _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3); + } + + if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate + is_sth = *ss2=='n'; // is_convolve? + _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'"); + op = is_sth?mp_convolve:mp_correlate; + const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector + 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA + 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM + 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode + 11,11,11, // [15]=xcenter, [16]=ycenter, [17]=zcenter (default value:-1) + 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart + 11,11,11, // [21]=xend, [22]=yend, [23]=zend (default value: -1) + 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride + 1,1,1 }; // [27]=xdilation, [28]=ydilation, [29]=zdilation + + l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'! + CImg(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode); + + arg1 = 2; + for (s = std::strchr(ss,'(') + 1; s=opcode._height) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: %s arguments provided, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + arg1<12?"Not enough":"Too much", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + _cimg_mp_check_type(opcode[2],1,2,0); // A + _cimg_mp_check_constant(opcode[3],2,3); // wA + _cimg_mp_check_constant(opcode[4],3,3); // hA + _cimg_mp_check_constant(opcode[5],4,3); // dA + _cimg_mp_check_constant(opcode[6],5,3); // sA + _cimg_mp_check_type(opcode[7],6,2,0); // M + _cimg_mp_check_constant(opcode[8],7,3); // wM + _cimg_mp_check_constant(opcode[9],8,3); // hM + _cimg_mp_check_constant(opcode[10],9,3); // dM + _cimg_mp_check_constant(opcode[11],10,3); // sM + _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions + _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized + _cimg_mp_check_constant(opcode[14],13,1); // channel_mode + _cimg_mp_check_type(opcode[15],14,1,0); // xcenter + _cimg_mp_check_type(opcode[16],15,1,0); // ycenter + _cimg_mp_check_type(opcode[17],16,1,0); // zcenter + _cimg_mp_check_constant(opcode[18],17,1); // xstart + _cimg_mp_check_constant(opcode[19],18,1); // ystart + _cimg_mp_check_constant(opcode[20],19,1); // zstart + _cimg_mp_check_constant(opcode[21],20,1); // xend + _cimg_mp_check_constant(opcode[22],21,1); // yend + _cimg_mp_check_constant(opcode[23],22,1); // zend + _cimg_mp_check_constant(opcode[24],23,3); // xstride + _cimg_mp_check_constant(opcode[25],24,3); // ystride + _cimg_mp_check_constant(opcode[26],25,3); // zstride + _cimg_mp_check_type(opcode[27],26,1,0); // xdilation + _cimg_mp_check_type(opcode[28],27,1,0); // ydilation + _cimg_mp_check_type(opcode[29],28,1,0); // zdilation + + const unsigned int + wA = (unsigned int)mem[opcode[3]], + hA = (unsigned int)mem[opcode[4]], + dA = (unsigned int)mem[opcode[5]], + sA = (unsigned int)mem[opcode[6]], + wM = (unsigned int)mem[opcode[8]], + hM = (unsigned int)mem[opcode[9]], + dM = (unsigned int)mem[opcode[10]], + sM = (unsigned int)mem[opcode[11]], + channel_mode = (unsigned int)mem[opcode[14]], + xstart = std::min((unsigned int)mem[opcode[18]],wA - 1), + ystart = std::min((unsigned int)mem[opcode[19]],hA - 1), + zstart = std::min((unsigned int)mem[opcode[20]],dA - 1), + xend = std::min((unsigned int)mem[opcode[21]],wA - 1), + yend = std::min((unsigned int)mem[opcode[22]],hA - 1), + zend = std::min((unsigned int)mem[opcode[23]],dA - 1); + + if (xstart>xend || ystart>yend || zstart>zend) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid xyz-start/end arguments " + "(start = (%u,%u,%u), end = (%u,%u,%u)), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + xstart,ystart,zstart,xend,yend,zend, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + const float + xstride = (float)mem[opcode[24]], + ystride = (float)mem[opcode[25]], + zstride = (float)mem[opcode[26]]; + + if (xstride<=0 || ystride<=0 || zstride<=0) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + xstride,ystride,zstride, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + arg2 = 1 + (unsigned int)std::floor((xend - xstart)/xstride); + arg3 = 1 + (unsigned int)std::floor((yend - ystart)/ystride); + arg4 = 1 + (unsigned int)std::floor((zend + zstart)/zstride); + arg5 = channel_mode==0?sM:channel_mode==1?std::max(sA,sM):sA*sM; + + opcode[1] = pos = vector(arg2*arg3*arg4*arg5); + opcode[3] = (ulongT)wA; + opcode[4] = (ulongT)hA; + opcode[5] = (ulongT)dA; + opcode[6] = (ulongT)sA; + opcode[8] = (ulongT)wM; + opcode[9] = (ulongT)hM; + opcode[10] = (ulongT)dM; + opcode[11] = (ulongT)sM; + opcode[14] = (ulongT)channel_mode; + opcode[18] = (ulongT)xstart; + opcode[19] = (ulongT)ystart; + opcode[20] = (ulongT)zstart; + opcode[21] = (ulongT)xend; + opcode[22] = (ulongT)yend; + opcode[23] = (ulongT)zend; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'd' : + if (*ss1=='(') { // Image depth + _cimg_mp_op("Function 'd()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_d,p1); + } + + if (!std::strncmp(ss,"date(",5)) { // Current date or file date + _cimg_mp_op("Function 'date()'"); + s1 = ss5; while (s1::vector((ulongT)mp_date,pos,_cimg_mp_size(pos), + arg1,arg1==~0U?~0U:_cimg_mp_size(arg1), + arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"debug(",6)) { // Print debug info + _cimg_mp_op("Function 'debug()'"); + p1 = code._width; + arg1 = compile(ss6,se1,depth1,p_ref,is_critical); + *se1 = 0; + variable_name.assign(CImg::string(ss6,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + ((CImg::vector((ulongT)mp_debug,arg1,0,code._width - p1), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code,p1); + *se1 = ')'; + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image + _cimg_mp_op("Function 'display()'"); + if (pexpr[se2 - expr._data]=='(') { // no arguments? + CImg::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code); + _cimg_mp_return_nan(); + } + if (*ss8!='#') { // Vector + s1 = ss8; while (s1::string(ss8,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(arg1)) + ((CImg::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0), + variable_name)>'y').move_to(opcode); + else + ((CImg::vector((ulongT)mp_print,arg1,0,0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + + ((CImg::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1), + arg2,arg3,arg4,arg5), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *s1 = c1; + _cimg_mp_return(arg1); + + } else { // Image + p1 = compile(ss8 + 1,se1,depth1,0,is_critical); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"det(",4)) { // Matrix determinant + _cimg_mp_op("Function 'det()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + _cimg_mp_scalar2(mp_det,arg1,p1); + } + + if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix + _cimg_mp_op("Function 'diag()'"); + CImg::vector((ulongT)mp_diag,0,0).move_to(l_opcode); + for (s = ss5; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + arg1 = opcode._height - 3; + pos = vector(arg1*arg1); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"dot(",4)) { // Dot product + _cimg_mp_op("Function 'dot()'"); + s1 = ss4; while (s1::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1), + p1>=arg6 && !_cimg_mp_is_constant(p1), + p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + _cimg_mp_return(p1); + } + + if (!std::strncmp(ss,"draw(",5)) { // Draw image + if (!is_critical) is_parallelizable = false; + _cimg_mp_op("Function 'draw()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s01) { + arg3 = arg2 + 1; + if (p2>2) { + arg4 = arg3 + 1; + if (p2>3) arg5 = arg4 + 1; + } + } + ++s0; + is_sth = true; + } else { + if (s0::vector((ulongT)mp_draw,arg1,(ulongT)_cimg_mp_size(arg1),p1,arg2,arg3,arg4,arg5, + 0,0,0,0,1,(ulongT)~0U,0,1).move_to(l_opcode); + + arg2 = arg3 = arg4 = arg5 = ~0U; + p2 = p1!=~0U?0:1; + if (s0::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode); + for (s = ss5; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector + _cimg_mp_op("Function 'eig()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + pos = vector((p1 + 1)*p1); + CImg::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing + if (!is_critical) is_parallelizable = false; + _cimg_mp_op("Function 'ellipse()'"); + if (*ss8=='#') { // Index specified + s0 = ss + 9; while (s0::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"erf(",4)) { // Error function + _cimg_mp_op("Function 'erf()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::erf(mem[arg1])); + _cimg_mp_scalar1(mp_erf,arg1); + } + + if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function + _cimg_mp_op("Function 'erfinv()'"); + arg1 = compile(ss7,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::erfinv(mem[arg1])); + _cimg_mp_scalar1(mp_erfinv,arg1); + } + + if (!std::strncmp(ss,"exp(",4)) { // Exponential + _cimg_mp_op("Function 'exp()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::exp(mem[arg1])); + _cimg_mp_scalar1(mp_exp,arg1); + } + + if (!std::strncmp(ss,"expr(",5)) { // Vector from expression + _cimg_mp_op("Function 'expr()'"); + s1 = ss5; while (s1::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"eye(",4)) { // Identity matrix + _cimg_mp_op("Function 'eye()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_constant(arg1,1,3); + p1 = (unsigned int)mem[arg1]; + pos = vector(p1*p1); + CImg::vector((ulongT)mp_eye,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"end(",4)) { // End + _cimg_mp_op("Function 'end()'"); + code.swap(code_end); + compile(ss4,se1,depth1,p_ref,true); + code.swap(code_end); + is_end_code = true; + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"end_t(",6)) { // End thread + _cimg_mp_op("Function 'end_t()'"); + code.swap(code_end_t); + compile(ss6,se1,depth1,p_ref,true); + code.swap(code_end_t); + is_end_code = true; + _cimg_mp_return_nan(); + } + break; + + case 'f' : + if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion + _cimg_mp_op("Function 'f2ui()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::float2uint((float)mem[arg1])); + _cimg_mp_scalar1(mp_f2ui,arg1); + } + + if (!std::strncmp(ss,"fact(",5)) { // Factorial + _cimg_mp_op("Function 'fact()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::factorial((int)mem[arg1])); + _cimg_mp_scalar1(mp_factorial,arg1); + } + + if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci + _cimg_mp_op("Function 'fibo()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::fibonacci((int)mem[arg1])); + _cimg_mp_scalar1(mp_fibonacci,arg1); + } + + if (!std::strncmp(ss,"fill(",5)) { // Fill + _cimg_mp_op("Function 'fill()'"); + s0 = ss5; while (s0::%s: %s: Target scalar is constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + ss>expr._data?"...":"",ss,se<&expr.back()?"...":""); + s1 = ++s0; while (s1::%s: %s: Invalid loop variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg3,3,1,0); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,is_critical); + } + // arg2 = variable slot, arg3 = fill expression. + _cimg_mp_check_type(arg3,3,1,0); + CImg::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1). + move_to(code,p1); + _cimg_mp_return(arg1); + } + + if (!std::strncmp(ss,"find(",5)) { // Find + _cimg_mp_op("Function 'find()'"); + + // First argument: data to look at. + s0 = ss5; while (s01) + _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4); + _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4); + } + if (_cimg_mp_size(arg2)>1) + _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4); + _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4); + } + + if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop + _cimg_mp_op("Function 'for()'"); + s1 = ss4; while (s1::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2, + arg4 - arg3,code._width - arg4, + p3>=arg6 && !_cimg_mp_is_constant(p3), + p2>=arg6 && !_cimg_mp_is_constant(p2)).move_to(code,arg1); + _cimg_mp_return(p3); + } + + if (!std::strncmp(ss,"floor(",6)) { // Floor + _cimg_mp_op("Function 'floor()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::floor(mem[arg1])); + _cimg_mp_scalar1(mp_floor,arg1); + } + + if (!std::strncmp(ss,"fsize(",6)) { // File size + _cimg_mp_op("Function 'fsize()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_type(arg1,1,2,0); + pos = scalar(); + CImg::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'g' : + if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function + _cimg_mp_op("Function 'gauss()'"); + s1 = ss6; while (s1::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + break; + + case 'h' : + if (*ss1=='(') { // Image height + _cimg_mp_op("Function 'h()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_h,p1); + } + break; + + case 'i' : + if (*ss1=='c' && *ss2=='(') { // Image median + _cimg_mp_op("Function 'ic()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_median,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (*ss1=='n' && *ss2=='(') { // Image norm + _cimg_mp_op("Function 'in()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + pos = scalar(); + CImg::vector((ulongT)mp_image_norm,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (*ss1=='f' && *ss2=='(') { // If..then[..else.] + _cimg_mp_op("Function 'if()'"); + s1 = ss3; while (s1::vector((ulongT)mp_if,pos,arg1,arg2,arg3, + p3 - p2,code._width - p3,arg4).move_to(code,p2); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"inrange(",8)) { // Check value range + _cimg_mp_op("Function 'inrange()'"); + s1 = ss8; while (s1=val1) + is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"int(",4)) { // Integer cast + _cimg_mp_op("Function 'int()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((longT)mem[arg1]); + _cimg_mp_scalar1(mp_int,arg1); + } + + if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inversion + _cimg_mp_op("Function 'invert()'"); + s1 = ss7; while (s1::vector((ulongT)mp_matrix_invert,pos,arg1,p1,arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(1/mem[arg1]); + _cimg_mp_scalar2(mp_div,1,arg1); + } + + if (*ss1=='s') { // Family of 'is_?()' functions + + if (!std::strncmp(ss,"isbool(",7)) { // Is boolean? + _cimg_mp_op("Function 'isbool()'"); + if (ss7==se1) _cimg_mp_return(0); + try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + catch(CImgException&) { _cimg_mp_return(0); } + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.); + _cimg_mp_scalar1(mp_isbool,arg1); + } + + if (!std::strncmp(ss,"isdir(",6)) { // Is directory? + _cimg_mp_op("Function 'isdir()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isfile(",7)) { // Is file? + _cimg_mp_op("Function 'isfile()'"); + arg1 = compile(ss7,se1,depth1,0,is_critical); + if (_cimg_mp_is_scalar(arg1)) _cimg_mp_return(0); + pos = scalar(); + CImg::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector? + if (ss5>=se1) _cimg_mp_return(0); + _cimg_mp_op("Function 'isin()'"); + pos = scalar(); + CImg::vector((ulongT)mp_isin,pos,0).move_to(l_opcode); + for (s = ss5; s::sequence(_cimg_mp_size(arg1),arg1 + 1, + arg1 + (ulongT)_cimg_mp_size(arg1)). + move_to(l_opcode); + else CImg::vector(arg1).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"isinf(",6)) { // Is infinite? + _cimg_mp_op("Function 'isinf()'"); + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_inf(mem[arg1])); + _cimg_mp_scalar1(mp_isinf,arg1); + } + + if (!std::strncmp(ss,"isint(",6)) { // Is integer? + _cimg_mp_op("Function 'isint()'"); + if (ss6==se1) _cimg_mp_return(0); + try { arg1 = compile(ss6,se1,depth1,0,is_critical); } + catch(CImgException&) { _cimg_mp_return(0); } + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1])); + _cimg_mp_scalar1(mp_isint,arg1); + } + + if (!std::strncmp(ss,"isnan(",6)) { // Is NaN? + _cimg_mp_op("Function 'isnan()'"); + if (ss6==se1) _cimg_mp_return(0); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_return((unsigned int)cimg::type::is_nan(mem[arg1])); + _cimg_mp_scalar1(mp_isnan,arg1); + } + + if (!std::strncmp(ss,"isnum(",6)) { // Is number? + _cimg_mp_op("Function 'isnum()'"); + val = 0; + if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1); + _cimg_mp_return(0); + } + + if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression? + _cimg_mp_op("Function 'isexpr()'"); + if (ss7==se1) _cimg_mp_return(0); + try { arg1 = compile(ss7,se1,depth1,0,is_critical); } + catch (CImgException&) { _cimg_mp_return(0); } + _cimg_mp_return(1); + } + } + break; + + case 'l' : + if (*ss1=='(') { // Size of image list + _cimg_mp_op("Function 'l()'"); + if (ss2!=se1) break; + _cimg_mp_scalar0(mp_list_l); + } + + if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation + _cimg_mp_op("Function 'lerp()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"log(",4)) { // Natural logarithm + _cimg_mp_op("Function 'log()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log(mem[arg1])); + _cimg_mp_scalar1(mp_log,arg1); + } + + if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm + _cimg_mp_op("Function 'log2()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::log2(mem[arg1])); + _cimg_mp_scalar1(mp_log2,arg1); + } + + if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm + _cimg_mp_op("Function 'log10()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::log10(mem[arg1])); + _cimg_mp_scalar1(mp_log10,arg1); + } + + if (!std::strncmp(ss,"lowercase(",10)) { // Lower case + _cimg_mp_op("Function 'lowercase()'"); + arg1 = compile(ss + 10,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::lowercase(mem[arg1])); + _cimg_mp_scalar1(mp_lowercase,arg1); + } + break; + + case 'm' : + if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication + _cimg_mp_op("Function 'mul()'"); + s1 = ss4; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " + "do not match with third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary + _cimg_mp_op("Function 'mproj()'"); + s1 = ss6; while (s1::%s: %s: Type of first argument ('%s') " + "do not match with second argument 'nb_colsS=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,wS, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (wD*hD!=p2) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of third argument ('%s') " + "do not match with fourth argument 'nb_colsD=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg3)._data,wD, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (hS!=hD) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Type of first argument ('%s') " + "do not match with third argument ('%s'), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg3)._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(wS*wD); + CImg::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables + _cimg_mp_op("Function 'merge()'"); + s1 = ss6; while (s1::%s: %s: Merge has already been requested before " + "for specified variable " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + if (arg1==~0U) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified operator " + "(should be one of '=,+,-,*,/,min,max'), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + memmerge.resize(3,memmerge._height + 1,1,1,0,0); + memmerge(0,memmerge._height - 1) = (int)pos; + memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos); + memmerge(2,memmerge._height - 1) = (int)arg1; + _cimg_mp_return(pos); + } + break; + + case 'n' : +#ifdef cimg_mp_func_name + if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector + _cimg_mp_op("Function 'name()'"); + if (*ss5=='#') { // Index specified + s0 = ss6; while (s0::vector((ulongT)mp_name,pos,p1,arg1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + + if (!std::strncmp(ss,"narg(",5)) { // Number of arguments + _cimg_mp_op("Function 'narg()'"); + if (ss5>=se1) _cimg_mp_return(0); + arg1 = 0; + for (s = ss5; s::vector((ulongT)mp_norm0,pos,0).move_to(l_opcode); break; + case 1 : + CImg::vector((ulongT)mp_norm1,pos,0).move_to(l_opcode); break; + case 2 : + CImg::vector((ulongT)mp_norm2,pos,0).move_to(l_opcode); break; + case ~0U : + CImg::vector((ulongT)mp_norminf,pos,0).move_to(l_opcode); break; + default : + CImg::vector((ulongT)mp_normp,pos,0,(ulongT)(arg1==~0U?-1:(int)arg1)). + move_to(l_opcode); + } + for ( ; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + + (l_opcode>'y').move_to(opcode); + if (arg1>0 && opcode._height==4) // Special case with one argument and p>=1 + _cimg_mp_scalar1(mp_abs,opcode[3]); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'p' : + if (!std::strncmp(ss,"permut(",7)) { // Number of permutations + _cimg_mp_op("Function 'permut()'"); + s1 = ss7; while (s1::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode); + for (s = s0; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + _cimg_mp_return_nan(); + } + + if (!std::strncmp(ss,"print(",6) || !std::strncmp(ss,"prints(",7)) { // Print expressions + is_sth = ss[5]=='s'; // is prints() + _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'"); + s0 = is_sth?ss7:ss6; + if (*s0!='#' || is_sth) { // Regular expression + for (s = s0; s::string(s,true,true).unroll('y'),true); + cimg::strpare(variable_name,false,true); + if (_cimg_mp_is_vector(pos)) // Vector + ((CImg::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0), + variable_name)>'y').move_to(opcode); + else // Scalar + ((CImg::vector((ulongT)mp_print,pos,0,is_sth?1:0), + variable_name)>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + *ns = c1; s = ns; + } + _cimg_mp_return(pos); + } else { // Image + p1 = compile(ss7,se1,depth1,0,is_critical); + _cimg_mp_check_list(true); + CImg::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"pseudoinvert(",13)) { // Matrix/scalar pseudo-inversion + _cimg_mp_op("Function 'pseudoinvert()'"); + s1 = ss + 13; while (s1::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1); + CImg::vector((ulongT)mp_matrix_pseudoinvert,pos,arg1,p2,p3,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'r' : + if (!std::strncmp(ss,"ref(",4)) { // Variable declaration + _cimg_mp_op("Function 'ref()'"); + s1 = ss4; while (s1=se1 || !*s1) compile(s1,s1,depth1,0,is_critical); // Will throw missing argument error + arg3 = compile(ss4,s1++,depth1,p_ref,is_critical); + *se1 = 0; + + if (!is_varname(s1)) { // Invalid variable name + variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0; + cimg::strellipsize(variable_name,64); + *se1 = ')'; + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid specified variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + + } + get_variable_pos(s1,arg1,arg2); + if (arg2!=~0U) reserved_label[arg2] = arg3; + else if (arg1!=~0U) variable_pos[arg1] = arg3; + else { // New variable + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg3; + CImg::string(s1).move_to(variable_def); + } + if (_cimg_mp_is_vector(arg3)) + set_reserved_vector(arg3); // Prevent from being used in further optimization + else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; + *se1 = ')'; + _cimg_mp_return(arg3); + } + + if (!std::strncmp(ss,"repeat(",7)) { // Repeat + _cimg_mp_op("Function 'repeat()'"); + s0 = ss7; while (s0::%s: %s: Invalid loop variable name '%s', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + get_variable_pos(variable_name,arg2,arg3); + arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot + if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) || + _cimg_mp_is_constant(arg2))) { // Variable is not a vector or is a constant -> error + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' " + "(expected 'scalar'), in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg2)._data,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else if (arg2==~0U) { // Variable does not exist -> create it + arg2 = scalar(); + if (arg3!=~0U) reserved_label[arg3] = arg2; + else { + if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0); + variable_pos[variable_def._width] = arg2; + variable_name.move_to(variable_def); + } + memtype[arg2] = -1; + } + arg3 = compile(++s1,se1,depth1,0,is_critical); + } else { // Version with 2 arguments + arg2 = ~0U; + arg3 = compile(s0,se1,depth1,0,is_critical); + } + // arg2 = variable slot, arg3 = fill expression. + CImg::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1); + _cimg_mp_return(arg3); + } + + if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize + _cimg_mp_op("Function 'resize()'"); + if (*ss7!='#') { // Vector + s1 = ss7; while (s1::vector((ulongT)mp_vector_resize,pos,arg2,arg1,(ulongT)_cimg_mp_size(arg1), + arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + + } else { // Image + if (!is_critical) is_parallelizable = false; + s0 = ss8; while (s0::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0). + move_to(l_opcode); + pos = 0; + for (s = s0; s10) { + _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: %s arguments, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + pos<1?"Missing":"Too much", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + l_opcode[0].move_to(code); + _cimg_mp_return_nan(); + } + } + + if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse + _cimg_mp_op("Function 'reverse()'"); + arg1 = compile(ss8,se1,depth1,0,is_critical); + if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1); + p1 = _cimg_mp_size(arg1); + pos = vector(p1); + CImg::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation + _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'"); + s1 = ss4; while (s11) { + arg2 = arg1 + 1; + if (p2>2) arg3 = arg2 + 1; + } + arg4 = compile(++s1,se1,depth1,0,is_critical); + } else { + s2 = s1 + 1; while (s2::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code); + } else { // 2D rotation + _cimg_mp_check_type(arg1,1,1,0); + pos = vector(4); + CImg::vector((ulongT)mp_rot2d,pos,arg1).move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"round(",6)) { // Value rounding + _cimg_mp_op("Function 'round()'"); + s1 = ss6; while (s1::vector((ulongT)mp_run,0,0).move_to(l_opcode); + pos = 1; + for (s = ss4; s::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode); + s = ns; + } + (l_opcode>'y').move_to(opcode); + pos = scalar(); + opcode[1] = pos; + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } +#endif + break; + + case 's' : + if (*ss1=='(') { // Image spectrum + _cimg_mp_op("Function 's()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_s,p1); + } + + if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values + _cimg_mp_op("Function 'same()'"); + s1 = ss5; while (s1::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sign(",5)) { // Sign + _cimg_mp_op("Function 'sign()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sign(mem[arg1])); + _cimg_mp_scalar1(mp_sign,arg1); + } + + if (!std::strncmp(ss,"sin(",4)) { // Sine + _cimg_mp_op("Function 'sin()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sin(mem[arg1])); + _cimg_mp_scalar1(mp_sin,arg1); + } + + if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal + _cimg_mp_op("Function 'sinc()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sinc(mem[arg1])); + _cimg_mp_scalar1(mp_sinc,arg1); + } + + if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine + _cimg_mp_op("Function 'sinh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sinh(mem[arg1])); + _cimg_mp_scalar1(mp_sinh,arg1); + } + + if (!std::strncmp(ss,"size(",5)) { // Vector size + _cimg_mp_op("Function 'size()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_constant(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1)); + } + + if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system + _cimg_mp_op("Function 'solve()'"); + s1 = ss6; while (s1::%s: %s: Types of first and second arguments ('%s' and '%s') " + "do not match with third argument 'nb_colsB=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,s_type(arg2)._data,p3, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg4*p3); + CImg::vector((ulongT)mp_solve,pos,arg1,arg2,arg4,arg5,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sort(",5)) { // Sort vector + _cimg_mp_op("Function 'sort()'"); + s1 = ss5; while (s1::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"sqr(",4)) { // Square + _cimg_mp_op("Function 'sqr()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(cimg::sqr(mem[arg1])); + _cimg_mp_scalar1(mp_sqr,arg1); + } + + if (!std::strncmp(ss,"sqrt(",5)) { // Square root + _cimg_mp_op("Function 'sqrt()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::sqrt(mem[arg1])); + _cimg_mp_scalar1(mp_sqrt,arg1); + } + + if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed + _cimg_mp_op("Function 'srand()'"); + arg1 = ss6::vector((ulongT)mp_image_stats,pos,p1).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + +#ifdef cimg_mp_func_store + if (!std::strncmp(ss,"store(",6)) { // Store vector to variable + _cimg_mp_op("Function 'store()'"); + s1 = ss6; while (s1::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg1,p1,arg2,p2, + arg3,arg4,arg5,arg6,pos).move_to(code); + _cimg_mp_return_nan(); + } +#endif + + if (!std::strncmp(ss,"stov(",5)) { // String to double + _cimg_mp_op("Function 'stov()'"); + s1 = ss5; while (s1::vector((ulongT)mp_stov,pos,arg1,p1,arg2,arg3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments + _cimg_mp_op("Function 'string()'"); + CImg::vector((ulongT)mp_string,0,0,0).move_to(l_opcode); + + if (*ss7=='#') { // Output vector size specified, with '#' + s0 = ss8; while (s0::vector(arg2,p2).move_to(l_opcode); + s = ns; + } + if (arg1==~0U) arg1 = p1; + pos = vector(arg1,0); + (l_opcode>'y').move_to(opcode); + opcode[1] = pos; + opcode[2] = arg1; + opcode[3] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD + _cimg_mp_op("Function 'svd()'"); + s1 = ss4; while (s1::%s: %s: Type of first argument ('%s') " + "does not match with second argument 'nb_colsA=%u', " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p1 + p2 + p2*p2); + CImg::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"swap(",5)) { // Swap values + _cimg_mp_op("Function 'swap()'"); + s1 = ss5; while (s1::%s: %s: %s argument cannot be a constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + _cimg_mp_is_constant(arg1)?"First":"Second", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + CImg::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code); + + // Write back values of linked arg1 and arg2. + const unsigned int *_ref = ref; + is_sth = true; // Is first argument? + do { + switch (*_ref) { + case 1 : // arg1: V[k] + arg3 = _ref[1]; // Vector slot + arg4 = _ref[2]; // Index + CImg::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4). + move_to(code); + break; + case 2 : // arg1: i/j[_#ind,off] + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // Offset + if (p1!=~0U) { + if (listout) + CImg::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff), + arg1,p1,arg3).move_to(code); + } else { + if (imgout) + CImg::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff), + arg1,arg3).move_to(code); + } + break; + case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c) + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // X + arg4 = _ref[4]; // Y + arg5 = _ref[5]; // Z + arg6 = _ref[6]; // C + if (p1!=~0U) { + if (listout) + CImg::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc), + arg1,p1,arg3,arg4,arg5,arg6).move_to(code); + } else { + if (imgout) + CImg::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc), + arg1,arg3,arg4,arg5,arg6).move_to(code); + } + break; + case 4: // arg1: I/J[_#ind,off] + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // Offset + if (p1!=~0U) { + if (listout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s), + arg1,p1,arg3).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v), + arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + } + } else { + if (imgout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s), + arg1,arg3).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v), + arg1,arg3,_cimg_mp_size(arg1)).move_to(code); + } + } + break; + case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c) + if (!is_critical) is_parallelizable = false; + p1 = _ref[1]; // Index + is_relative = (bool)_ref[2]; + arg3 = _ref[3]; // X + arg4 = _ref[4]; // Y + arg5 = _ref[5]; // Z + if (p1!=~0U) { + if (listout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s), + arg1,p1,arg3,arg4,arg5).move_to(code); + else { + _cimg_mp_check_constant_index(p1); + CImg::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v), + arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + } else { + if (imgout) { + if (_cimg_mp_is_scalar(arg1)) + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s), + arg1,arg3,arg4,arg5).move_to(code); + else + CImg::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v), + arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code); + } + } + break; + } + + _ref+=7; + arg1 = arg2; + is_sth = !is_sth; + } while (!is_sth); + + if (p_ref) std::memcpy(p_ref,ref,siz_ref); + _cimg_mp_return(arg1); + } + break; + + case 't' : + if (!std::strncmp(ss,"tan(",4)) { // Tangent + _cimg_mp_op("Function 'tan()'"); + arg1 = compile(ss4,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tan(mem[arg1])); + _cimg_mp_scalar1(mp_tan,arg1); + } + + if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent + _cimg_mp_op("Function 'tanh()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant(std::tanh(mem[arg1])); + _cimg_mp_scalar1(mp_tanh,arg1); + } + + if (!std::strncmp(ss,"trace(",6)) { // Matrix trace + _cimg_mp_op("Function 'trace()'"); + arg1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_matrix_square(arg1,1); + p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1))); + _cimg_mp_scalar2(mp_trace,arg1,p1); + } + + if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose + _cimg_mp_op("Function 'transpose()'"); + s1 = ss + 10; while (s1::%s: %s: Size of first argument ('%s') does not match " + "second argument 'nb_cols=%u', in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + s_type(arg1)._data,p2, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(p3*p2); + CImg::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'u' : + if (*ss1=='(') { // Random value with uniform distribution + _cimg_mp_op("Function 'u()'"); + if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1); + s1 = ss2; while (s1float conversion + _cimg_mp_op("Function 'ui2f()'"); + arg1 = compile(ss5,se1,depth1,0,is_critical); + if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1); + if (_cimg_mp_is_constant(arg1)) _cimg_mp_constant((double)cimg::uint2float((unsigned int)mem[arg1])); + _cimg_mp_scalar1(mp_ui2f,arg1); + } + + if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable + _cimg_mp_op("Function 'unref()'"); + arg1=~0U; + for (s0 = ss6; s0ss6 && *s0==',') ++s0; + s1 = s0; while (s1s0) { + *s1 = 0; + get_variable_pos(s0,arg1,arg2); + if (arg2!=~0U) reserved_label[arg2] = ~0U; + else if (arg1!=~0U) { + variable_def.remove(arg1); + if (arg10) || + !std::strncmp(ss,"vector(",7) || + (!std::strncmp(ss,"vector",6) && ss7::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode); + arg2+=arg4; + } else { CImg::vector(arg3).move_to(l_opcode); ++arg2; } + s = ns; + } + if (arg1==~0U) arg1 = arg2; + if (!arg1) _cimg_mp_return(0); + pos = vector(arg1); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) || + !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) || + !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) || + !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) || + !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) || + !std::strncmp(ss,"vprod(",6) || + !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) || + !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) || + !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions + _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'": + ss[4]=='k'?"Function 'vargkth()'": + ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'": + ss[5]=='i'?"Function vargminabs()'": + ss[7]=='('?"Function 'vargmax()'": + "Function 'vargmaxabs()'"): + ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"): + ss[1]=='k'?"Function 'vkth()'": + ss[1]=='p'?"Function 'vprod()'": + ss[1]=='v'?"Function 'vvar()'": + ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'": + "Function 'vminabs()'"): + ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'": + "Function 'vmaxabs()'"): + "Function 'vmed()'"); + op = ss[1]=='a'?(ss[2]=='v'?mp_vavg: + ss[4]=='k'?mp_vargkth: + ss[5]=='i' && ss[7]=='('?mp_vargmin: + ss[5]=='i'?mp_vargminabs: + ss[7]=='('?mp_vargmax:mp_vargmaxabs): + ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd): + ss[1]=='k'?mp_vkth: + ss[1]=='p'?mp_vprod: + ss[1]=='v'?mp_vvar: + ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs): + ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs): + mp_vmedian; + CImg::vector((ulongT)op,0,0,0).move_to(l_opcode); + p1 = ~0U; + p3 = 1; + for (s = std::strchr(ss,'(') + 1; s::vector(arg2,p2).move_to(l_opcode); + s = ns; + ++p3; + } + (l_opcode>'y').move_to(opcode); + if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1); + opcode[1] = pos; + opcode[2] = p1; + opcode[3] = opcode._height; + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + if (!std::strncmp(ss,"vtos(",5)) { // Double(s) to string + _cimg_mp_op("Function 'vtos()'"); + s1 = ss5; while (s1::vector((ulongT)mp_vtos,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + break; + + case 'w' : + if (*ss1=='(') { // Image width + _cimg_mp_op("Function 'w()'"); + if (*ss2=='#') { // Index specified + p1 = compile(ss3,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss2!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_w,p1); + } + + if (*ss1=='h' && *ss2=='(') { // Image width*height + _cimg_mp_op("Function 'wh()'"); + if (*ss3=='#') { // Index specified + p1 = compile(ss4,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss3!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_wh,p1); + } + + if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth + _cimg_mp_op("Function 'whd()'"); + if (*ss4=='#') { // Index specified + p1 = compile(ss5,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss4!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_whd,p1); + } + + if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum + _cimg_mp_op("Function 'whds()'"); + if (*ss5=='#') { // Index specified + p1 = compile(ss6,se1,depth1,0,is_critical); + _cimg_mp_check_list(false); + } else { if (ss5!=se1) break; p1 = ~0U; } + _cimg_mp_scalar1(mp_image_whds,p1); + } + + if (!std::strncmp(ss,"while(",6)) { // While...do + _cimg_mp_op("Function 'while()'"); + s0 = *ss5=='('?ss6:ss8; + s1 = s0; while (s1::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2, + pos>=arg6 && !_cimg_mp_is_constant(pos), + arg1>=arg6 && !_cimg_mp_is_constant(arg1)).move_to(code,p1); + _cimg_mp_return(pos); + } + break; + + case 'x' : + if (!std::strncmp(ss,"xor(",4)) { // Xor + _cimg_mp_op("Function 'xor()'"); + s1 = ss4; while (s1::vector((ulongT)op,pos,0).move_to(l_opcode); + for (s = std::strchr(ss,'(') + 1; s::sequence(_cimg_mp_size(arg2),arg2 + 1, + arg2 + (ulongT)_cimg_mp_size(arg2)). + move_to(l_opcode); + else CImg::vector(arg2).move_to(l_opcode); + is_sth&=_cimg_mp_is_constant(arg2); + s = ns; + } + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + if (is_sth) _cimg_mp_constant(op(*this)); + opcode.move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // No corresponding built-in function -> Look for a user-defined macro call. + s0 = strchr(ss,'('); + if (s0) { + variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0; + + // Count number of specified arguments. + p1 = 0; + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && !p1) break; + ns = s; while (ns _expr = macro_body[l]; // Expression to be substituted + + p1 = 1; // Index of current parsed argument + for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments + while (*s && cimg::is_blank(*s)) ++s; + if (*s==')' && p1==1) break; // Function has no arguments + if (p1>p2) { ++p1; break; } + ns = s; while (ns _pexpr(_expr._width); + ns = _pexpr._data; + for (ps = _expr._data, c1 = ' '; *ps; ++ps) { + if (!cimg::is_blank(*ps)) c1 = *ps; + *(ns++) = c1; + } + *ns = 0; + + CImg _level = get_level(_expr); + expr.swap(_expr); + pexpr.swap(_pexpr); + level.swap(_level); + s0 = user_macro; + user_macro = macro_def[l]; + pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,is_critical); + user_macro = s0; + level.swap(_level); + pexpr.swap(_pexpr); + expr.swap(_expr); + _cimg_mp_return(pos); + } + + if (arg3) { // Macro name matched but number of arguments does not + CImg sig_nargs(arg3); + arg1 = 0; + cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name)) + sig_nargs[arg1++] = (unsigned int)macro_def[l].back(); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + if (sig_nargs._width>1) { + sig_nargs.sort(); + arg1 = sig_nargs.back(); + --sig_nargs._width; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " + "does not match macro declaration (defined for %s or %u arguments), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p1,sig_nargs.value_string()._data,arg1, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } else + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) " + "does not match macro declaration (defined for %u argument%s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,variable_name._data, + p1,*sig_nargs,*sig_nargs!=1?"s":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + } // if (se1==')') + + // Char / string initializer. + if (*se1=='\'' && + ((se1>ss && *ss=='\'') || + (se1>ss1 && *ss=='_' && *ss1=='\''))) { + if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; } + else { _cimg_mp_op("String initializer"); s1 = ss1; } + arg1 = (unsigned int)(se1 - s1); // Original string length + if (arg1) { + CImg(s1,arg1 + 1).move_to(variable_name).back() = 0; + cimg::strunescape(variable_name); + arg1 = (unsigned int)std::strlen(variable_name); + } + if (!arg1) _cimg_mp_return(0); // Empty string -> 0 + if (*ss=='_') { + if (arg1==1) _cimg_mp_constant((unsigned char)*variable_name); + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s: Literal %s contains more than one byte, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op, + ss1, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + pos = vector(arg1); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + } + + // Vector initializer [ ... ]. + if (*ss=='[' && *se1==']') { + _cimg_mp_op("Vector initializer"); + s1 = ss1; while (s1s1 && cimg::is_blank(*s2)) --s2; + if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string + arg1 = (unsigned int)(s2 - s1 - 1); // Original string length + if (arg1) { + CImg(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0; + cimg::strunescape(variable_name); + arg1 = (unsigned int)std::strlen(variable_name); + } + if (!arg1) _cimg_mp_return(0); // Empty string -> 0 + pos = vector(arg1); + CImg::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode); + CImg(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode); + std::memcpy((char*)l_opcode[1]._data,variable_name,arg1); + (l_opcode>'y').move_to(code); + } else { // Vector values provided as list of items + arg1 = 0; // Number of specified values + if (*ss1!=']') for (s = ss1; s::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode); + arg1+=arg3; + } else { CImg::vector(arg2).move_to(l_opcode); ++arg1; } + s = ns; + } + if (!arg1) _cimg_mp_return(0); + pos = vector(arg1); + l_opcode.insert(CImg::vector((ulongT)mp_vector_init,pos,0,arg1),0); + (l_opcode>'y').move_to(opcode); + opcode[2] = opcode._height; + opcode.move_to(code); + } + return_new_comp = true; + _cimg_mp_return(pos); + } + + // Variables related to the input list of images. + if (*ss1=='#' && ss2::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code); + return_new_comp = true; + _cimg_mp_return(pos); + case 'R' : // R#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0, + 0,_cimg_mp_boundary); + case 'G' : // G#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1, + 0,_cimg_mp_boundary); + case 'B' : // B#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2, + 0,_cimg_mp_boundary); + case 'A' : // A#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3, + 0,_cimg_mp_boundary); + } + } + + if (*ss1 && *ss2=='#' && ss3='0' && *ss1<='9') { // i0#ind...i9#ind + if (!listin) _cimg_mp_return(0); + _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0', + 0,_cimg_mp_boundary); + } + + if (*ss1=='c') { // ic#ind + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_median) list_median.assign(listin._width); + if (!list_median[p1]) CImg::vector(listin[p1].median()).move_to(list_median[p1]); + _cimg_mp_constant(*list_median[p1]); + } + _cimg_mp_scalar1(mp_list_median,arg1); + } + + if (*ss1=='n') { // in#ind + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_norm) list_norm.assign(listin._width); + if (!list_norm[p1]) CImg::vector(listin[p1].magnitude()).move_to(list_norm[p1]); + _cimg_mp_constant(*list_norm[p1]); + } + _cimg_mp_scalar1(mp_list_norm,arg1); + } + + switch (*ss1) { + case 'm' : arg2 = 0; break; // im#ind + case 'M' : arg2 = 1; break; // iM#ind + case 'a' : arg2 = 2; break; // ia#ind + case 'v' : arg2 = 3; break; // iv#ind + case 's' : arg2 = 12; break; // is#ind + case 'p' : arg2 = 13; break; // ip#ind + } + } else if (*ss1=='m') switch (*ss) { + case 'x' : arg2 = 4; break; // xm#ind + case 'y' : arg2 = 5; break; // ym#ind + case 'z' : arg2 = 6; break; // zm#ind + case 'c' : arg2 = 7; break; // cm#ind + } else if (*ss1=='M') switch (*ss) { + case 'x' : arg2 = 8; break; // xM#ind + case 'y' : arg2 = 9; break; // yM#ind + case 'z' : arg2 = 10; break; // zM#ind + case 'c' : arg2 = 11; break; // cM#ind + } + if (arg2!=~0U) { + if (!listin) _cimg_mp_return(0); + if (_cimg_mp_is_constant(arg1)) { + if (!list_stats) list_stats.assign(listin._width); + if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(listin[p1].get_stats(),false); + _cimg_mp_constant(list_stats(p1,arg2)); + } + _cimg_mp_scalar2(mp_list_stats,arg1,arg2); + } + } + + if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4 error. + c1 = *se1; + _cimg_mp_strerr; + cimg::strellipsize(variable_name,64); + if (is_sth) + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Undefined variable '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + s1 = std::strchr(ss,'('); + s_op = s1 && c1==')'?"function call":"item"; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function, + s_op,variable_name._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + + // Evaluation procedure. + double operator()(const double x, const double y, const double z, const double c) { + mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; + for (p_code = code; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + return *result; + } + + // Evaluation procedure (return output values in vector 'output'). + template + void operator()(const double x, const double y, const double z, const double c, t *const output) { + mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c; + for (p_code = code; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + if (result_dim) { + const double *ptrs = result + 1; + t *ptrd = output; + for (unsigned int k = 0; k_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + p_code_end = code.end(); + } + + // Evaluation procedure for end_t() bloc. + void end_t() { + if (!code_end_t) return; + if (imgin) { + mem[_cimg_mp_slot_x] = imgin._width - 1.; + mem[_cimg_mp_slot_y] = imgin._height - 1.; + mem[_cimg_mp_slot_z] = imgin._depth - 1.; + mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; + } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_end_t.end(); + for (p_code = code_end_t; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Evaluation procedure the end() bloc. + void end() { + if (!code_end) return; + if (imgin) { + mem[_cimg_mp_slot_x] = imgin._width - 1.; + mem[_cimg_mp_slot_y] = imgin._height - 1.; + mem[_cimg_mp_slot_z] = imgin._depth - 1.; + mem[_cimg_mp_slot_c] = imgin._spectrum - 1.; + } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0; + p_code_end = code_end.end(); + for (p_code = code_end; p_code_data; + const ulongT target = opcode[1]; + mem[target] = _cimg_mp_defunc(*this); + } + } + + // Merge inter-thread variables. + // (argument 'mp' is the master instance). + void merge(_cimg_math_parser& mp) { + if (&mp==this) return; + cimg_rofY(mp.memmerge,k) { + const unsigned int + pos = (unsigned int)mp.memmerge(0,k), + siz = (unsigned int)mp.memmerge(1,k), + iop = (unsigned int)mp.memmerge(2,k); + if (!siz) switch (iop) { // Scalar value + case 0 : mp.mem[pos] = mem[pos]; break; // Assignment + case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+ + case 2 : mp.mem[pos]-=mem[pos]; break; // Operator- + case 3 : mp.mem[pos]*=mem[pos]; break; // Operator* + case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/ + case 5 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min' + case 6 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max' + } else switch (iop) { // Vector value + case 0 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true) = CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 1 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 2 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 3 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 4 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg(&mem[pos + 1],siz,1,1,1,true); + break; + case 5 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg(&mem[pos + 1],siz,1,1,1,true)); + break; + case 6 : + CImg(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg(&mem[pos + 1],siz,1,1,1,true)); + break; + } + } + } + + // Return specified argument number as a string. + static const char *s_argth(const unsigned int n_arg) { + const char + *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth", + "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th", + "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" }; + return _s_arg[n_arg s_calling_function() const { + CImg res; + const unsigned int + l1 = calling_function?(unsigned int)std::strlen(calling_function):0U, + l2 = user_macro?(unsigned int)std::strlen(user_macro):0U; + if (l2) { + res.assign(l1 + l2 + 48); + cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro); + } else { + res.assign(l1 + l2 + 4); + cimg_snprintf(res,res._width,"%s()",calling_function); + } + return res; + } + + // Return type of a memory element as a string. + CImg s_type(const unsigned int arg) const { + CImg res; + if (_cimg_mp_is_vector(arg)) { // Vector + CImg::string("vectorXXXXXXXXXXXXXXXX").move_to(res); + cimg_sprintf(res._data + 6,"%u",_cimg_mp_size(arg)); + } else if (_cimg_mp_is_constant(arg)) CImg::string("const scalar").move_to(res); // Const scalar + else CImg::string("scalar").move_to(res); // Scalar + return res; + } + + // Count parentheses/brackets level of each character of the expression. + CImg get_level(CImg& _expr) const { + bool is_escaped = false, next_is_escaped = false; + unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string + CImg res(_expr._width - 1); + unsigned int *pd = res._data; + int _level = 0; + for (const char *ps = _expr._data; *ps && _level>=0; ++ps) { + if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true; + if (!is_escaped && *ps=='\'') { // Non-escaped character + if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string + else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string + else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string + } + *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1): + *ps=='(' || *ps=='['?_level++: + *ps==')' || *ps==']'?--_level: + _level); + mode = next_mode; + is_escaped = next_is_escaped; + next_is_escaped = false; + } + if (mode) { + cimg::strellipsize(_expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unterminated string literal, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + _expr._data); + } + if (_level) { + cimg::strellipsize(_expr,64); + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.", + pixel_type(),_cimg_mp_calling_function, + _expr._data); + } + return res; + } + + // Find and return index of current image 'imgin' within image list 'listin'. + unsigned int get_mem_img_index() { + if (mem_img_index==~0U) { + if (&imgout>listout.data() && &imgout='0' && c2<='9') rp = 20 + c2 - '0'; // i0...i9 + else if (c2=='m') rp = 4; // im + else if (c2=='M') rp = 5; // iM + else if (c2=='a') rp = 6; // ia + else if (c2=='v') rp = 7; // iv + else if (c2=='s') rp = 8; // is + else if (c2=='p') rp = 9; // ip + else if (c2=='c') rp = 10; // ic + else if (c2=='n') rp = 11; // in + } else if (c2=='m') { + if (c1=='x') rp = 12; // xm + else if (c1=='y') rp = 13; // ym + else if (c1=='z') rp = 14; // zm + else if (c1=='c') rp = 15; // cm + } else if (c2=='M') { + if (c1=='x') rp = 16; // xM + else if (c1=='y') rp = 17; // yM + else if (c1=='z') rp = 18; // zM + else if (c1=='c') rp = 19; // cM + } + } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd + } else if (variable_name[1] && variable_name[2] && variable_name[3] && + !variable_name[4]) { // Four-chars variable + c1 = variable_name[0]; + c2 = variable_name[1]; + c3 = variable_name[2]; + c4 = variable_name[3]; + if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds + } else if (!std::strcmp(variable_name,"interpolation")) rp = 30; // interpolation + else if (!std::strcmp(variable_name,"boundary")) rp = 31; // boundary + + if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels + + // Multi-char variable name : check for existing variable with same name + cimglist_for(variable_def,i) + if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; } + } + + // Tell for each character of an expression if it is inside a string or not. + CImg is_inside_string(CImg& _expr) const { + bool is_escaped = false, next_is_escaped = false; + unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string + CImg res = CImg::string(_expr); + bool *pd = res._data; + for (const char *ps = _expr._data; *ps; ++ps) { + if (!next_is_escaped && *ps=='\\') next_is_escaped = true; + if (!is_escaped && *ps=='\'') { // Non-escaped character + if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string + else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string + else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string + } + *(pd++) = mode>=1 || is_escaped; + mode = next_mode; + is_escaped = next_is_escaped; + next_is_escaped = false; + } + return res; + } + + // Return true if specified argument can be a part of an allowed variable name. + bool is_varchar(const char c) const { + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + } + + // Return true if specified argument can be considered as a variable name. + bool is_varname(const char *const str, const unsigned int length=~0U) const { + if (*str>='0' && *str<='9') return false; + for (unsigned int l = 0; l128) return false; + const int *ptr = memtype.data(arg + 1); + bool is_tmp = true; + while (siz-->0) if (*(ptr++)) { is_tmp = false; break; } + return is_tmp; + } + + // Check if a memory slot is a positive integer constant scalar value. + // 'mode' can be: + // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant } + void check_constant(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, + char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,1,0); + if (!(_cimg_mp_is_constant(arg) && + (!mode || (double)(int)mem[arg]==mem[arg]) && + (mode<2 || mem[arg]>=(mode==3)))) { + const char *const s_arg = s_argth(n_arg); + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a%s constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_arg?" argument":" Argument",s_type(arg)._data, + !mode?"":mode==1?"n integer": + mode==2?" positive integer":" strictly positive integer", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check if an image index is a constant value. + void check_constant_index(const unsigned int arg, + char *const ss, char *const se, const char saved_char) { + if (arg!=~0U && !_cimg_mp_is_constant(arg)) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Specified image index is not a constant, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check a matrix is square. + void check_matrix_square(const unsigned int arg, const unsigned int n_arg, + char *const ss, char *const se, const char saved_char) { + _cimg_mp_check_type(arg,n_arg,2,0); + const unsigned int + siz = _cimg_mp_size(arg), + n = (unsigned int)cimg::round(std::sqrt((float)siz)); + if (n*n!=siz) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand"; + else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One"; + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s (of type '%s') " + "cannot be considered as a square matrix, in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), + s_type(arg)._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check type compatibility for one argument. + // Bits of 'mode' tells what types are allowed: + // { 1 = scalar | 2 = vectorN }. + // If 'N' is not zero, it also restricts the vectors to be of size N only. + void check_type(const unsigned int arg, const unsigned int n_arg, + const unsigned int mode, const unsigned int N, + char *const ss, char *const se, const char saved_char) { + const bool + is_scalar = _cimg_mp_is_scalar(arg), + is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N); + bool cond = false; + if (mode&1) cond|=is_scalar; + if (mode&2) cond|=is_vector; + if (!cond) { + const char *s_arg; + if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand"; + else s_arg = s_argth(n_arg); + CImg sb_type(32); + if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'"); + else if (mode==2) { + if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'vector'"); + } else { + if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N); + else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'"); + } + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"), + s_type(arg)._data,sb_type._data, + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Check that listin or listout are not empty. + void check_list(const bool is_out, + char *const ss, char *const se, const char saved_char) { + if ((!is_out && !listin) || (is_out && !listout)) { + char *s0; _cimg_mp_strerr; + throw CImgArgumentException("[" cimg_appname "_math_parser] " + "CImg<%s>::%s: %s%s Invalid call with an empty image list, " + "in expression '%s%s%s'.", + pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"", + s0>expr._data?"...":"",s0,se<&expr.back()?"...":""); + } + } + + // Insert constant value in memory. + unsigned int constant(const double val) { + + // Search for built-in constant. + if (cimg::type::is_nan(val)) return _cimg_mp_slot_nan; + if (val==(double)(int)val) { + if (val>=0 && val<=10) return (unsigned int)val; + if (val<0 && val>=-5) return (unsigned int)(10 - val); + } + if (val==0.5) return 16; + + // Search for constant already requested before (in const cache). + unsigned int ind = ~0U; + if (constcache_size<1024) { + if (!constcache_size) { + constcache_vals.assign(16,1,1,1,0); + constcache_inds.assign(16,1,1,1,0); + *constcache_vals = val; + constcache_size = 1; + ind = 0; + } else { // Dichotomic search + const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1]; + if (val_beg>=val) ind = 0; + else if (val_end==val) ind = constcache_size - 1; + else if (val_end=constcache_size || constcache_vals[ind]!=val) { + ++constcache_size; + if (constcache_size>constcache_vals._width) { + constcache_vals.resize(-200,1,1,1,0); + constcache_inds.resize(-200,1,1,1,0); + } + const int l = constcache_size - (int)ind - 1; + if (l>0) { + std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double)); + std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int)); + } + constcache_vals[ind] = val; + constcache_inds[ind] = 0; + } + } + if (constcache_inds[ind]) return constcache_inds[ind]; + } + + // Insert new constant in memory if necessary. + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); } + const unsigned int pos = mempos++; + mem[pos] = val; + memtype[pos] = 1; // Set constant property + if (ind!=~0U) constcache_inds[ind] = pos; + return pos; + } + + // Insert new scalar in memory. + unsigned int scalar() { + if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); } + return mempos++; + } + + // Insert new vector of specified size in memory. + unsigned int vector(const unsigned int siz) { + if (mempos + siz>=mem._width) { + mem.resize(2*mem._width + siz,1,1,1,0); + memtype.resize(mem._width,1,1,1,0); + } + const unsigned int pos = mempos++; + mem[pos] = cimg::type::nan(); + memtype[pos] = siz + 1; + mempos+=siz; + return pos; + } + + // Insert new initialized vector. + unsigned int vector(const unsigned int siz, const double value) { + const unsigned int pos = vector(siz); + double *ptr = &mem[pos] + 1; + for (unsigned int i = 0; i::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code); + return pos; + } + + // Set reserved status to all values of a vector. + void set_reserved_vector(const unsigned int arg) { + unsigned int siz = _cimg_mp_size(arg); + int *ptr = memtype.data(arg + 1); + while (siz-->0) *(ptr++) = -1; + } + + unsigned int scalar0(const mp_func op) { + const unsigned int pos = scalar(); + CImg::vector((ulongT)op,pos).move_to(code); + return_new_comp = true; + return pos; + } + + unsigned int scalar1(const mp_func op, const unsigned int arg1) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1).move_to(code); + return pos; + } + + unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2).move_to(code); + return pos; + } + + unsigned int scalar3(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code); + return pos; + } + + unsigned int scalar4(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code); + return pos; + } + + unsigned int scalar5(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code); + return pos; + } + + unsigned int scalar6(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code); + return pos; + } + + unsigned int scalar7(const mp_func op, + const unsigned int arg1, const unsigned int arg2, const unsigned int arg3, + const unsigned int arg4, const unsigned int arg5, const unsigned int arg6, + const unsigned int arg7) { + const unsigned int pos = + arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1: + arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2: + arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3: + arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4: + arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5: + arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6: + arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7: + ((return_new_comp = true), scalar()); + CImg::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code); + return pos; + } + + void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) { + const unsigned int siz = _cimg_mp_size(pos); + if (siz>24) CImg::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]); + } + } + + void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) { + const unsigned int siz = _cimg_mp_size(pos); + if (siz>24) CImg::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); + } + } + + unsigned int vector1_v(const mp_func op, const unsigned int arg1) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_v,pos,siz,(ulongT)op,arg1).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vs,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) { + const unsigned int + siz = _cimg_mp_size(arg2), + pos = is_comp_vector(arg2)?arg2: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_sv,pos,siz,(ulongT)op,arg1,arg2).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2, + const unsigned int arg3) { + const unsigned int + siz = _cimg_mp_size(arg1), + pos = is_comp_vector(arg1)?arg1: + ((return_new_comp = true), vector(siz)); + if (siz>24) CImg::vector((ulongT)mp_vector_map_vss,pos,siz,(ulongT)op,arg1,arg2,arg3).move_to(code); + else { + code.insert(siz); + for (unsigned int k = 1; k<=siz; ++k) + CImg::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]); + } + return pos; + } + + // Evaluation functions, known by the parser. + // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT), + // so we can store pointers to them directly in the opcode vectors. +#ifdef _mp_arg +#undef _mp_arg +#endif +#define _mp_arg(x) mp.mem[mp.opcode[x]] + + static double mp_abs(_cimg_math_parser& mp) { + return cimg::abs(_mp_arg(2)); + } + + static double mp_add(_cimg_math_parser& mp) { + return _mp_arg(2) + _mp_arg(3); + } + + static double mp_acos(_cimg_math_parser& mp) { + return std::acos(_mp_arg(2)); + } + + static double mp_acosh(_cimg_math_parser& mp) { + return cimg::acosh(_mp_arg(2)); + } + + static double mp_asinh(_cimg_math_parser& mp) { + return cimg::asinh(_mp_arg(2)); + } + + static double mp_atanh(_cimg_math_parser& mp) { + return cimg::atanh(_mp_arg(2)); + } + + static double mp_arg(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(4); + const unsigned int + nb_args = (unsigned int)mp.opcode[2] - 4, + ind = _ind<0?_ind + nb_args:(unsigned int)_ind, + siz = (unsigned int)mp.opcode[3]; + if (siz>0) { + if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); + else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); + return cimg::type::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + static double mp_arg0(_cimg_math_parser& mp) { + const int _ind = (int)_mp_arg(4); + const unsigned int + nb_args = (unsigned int)mp.opcode[2] - 4, + ind = _ind<0?_ind + nb_args:_ind + 1U, + siz = (unsigned int)mp.opcode[3]; + if (siz>0) { + if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double)); + else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double)); + return cimg::type::nan(); + } + if (ind>=nb_args) return 0; + return _mp_arg(ind + 4); + } + + static double mp_argkth(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = mp_kth(mp); + for (unsigned int i = 4; ival) { val = _val; argval = i - 3; } + } + return (double)argval; + } + + static double mp_argmaxabs(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3), absval = cimg::abs(val); + unsigned int argval = 0; + for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; argval = i - 3; } + } + return (double)argval; + } + + static double mp_asin(_cimg_math_parser& mp) { + return std::asin(_mp_arg(2)); + } + + static double mp_atan(_cimg_math_parser& mp) { + return std::atan(_mp_arg(2)); + } + + static double mp_atan2(_cimg_math_parser& mp) { + return std::atan2(_mp_arg(2),_mp_arg(3)); + } + + static double mp_avg(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i>(unsigned int)_mp_arg(3)); + } + + static double mp_bitwise_xor(_cimg_math_parser& mp) { + return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3)); + } + + static double mp_bool(_cimg_math_parser& mp) { + return (double)(bool)_mp_arg(2); + } + + static double mp_break(_cimg_math_parser& mp) { + mp.break_type = 1; + mp.p_code = mp.p_break - 1; + return cimg::type::nan(); + } + + static double mp_breakpoint(_cimg_math_parser& mp) { + cimg_abort_init; + cimg_abort_test; + cimg::unused(mp); + return cimg::type::nan(); + } + +#ifdef cimg_mp_func_run + static double mp_run(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + CImg str = _str>'x'; + cimg_mp_func_run(str._data); + return cimg::type::nan(); + } +#endif + + static double mp_cbrt(_cimg_math_parser& mp) { + return cimg::cbrt(_mp_arg(2)); + } + + static double mp_ceil(_cimg_math_parser& mp) { + return std::ceil(_mp_arg(2)); + } + + static double mp_complex_abs(_cimg_math_parser& mp) { + return cimg::_hypot(_mp_arg(2),_mp_arg(3)); + } + + static double mp_complex_conj(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = real; + ptrd[1] = -imag; + return cimg::type::nan(); + } + + static double mp_complex_div_sv(_cimg_math_parser& mp) { + const double + *ptr2 = &_mp_arg(3) + 1, + r1 = _mp_arg(2), + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = r1*r2/denom; + *ptrd = -r1*i2/denom; + return cimg::type::nan(); + } + + static double mp_complex_div_vv(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + const double denom = r2*r2 + i2*i2; + *(ptrd++) = (r1*r2 + i1*i2)/denom; + *ptrd = (r2*i1 - r1*i2)/denom; + return cimg::type::nan(); + } + + static double mp_complex_exp(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = exp_real*std::cos(imag); + ptrd[1] = exp_real*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_log(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = 0.5*std::log(real*real + imag*imag); + ptrd[1] = std::atan2(imag,real); + return cimg::type::nan(); + } + + static double mp_complex_mul(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1, + r1 = *(ptr1++), i1 = *ptr1, + r2 = *(ptr2++), i2 = *ptr2; + double *ptrd = &_mp_arg(1) + 1; + *(ptrd++) = r1*r2 - i1*i2; + *(ptrd++) = r1*i2 + r2*i1; + return cimg::type::nan(); + } + + static void _mp_complex_pow(const double r1, const double i1, + const double r2, const double i2, + double *ptrd) { + double ro, io; + if (cimg::abs(i2)<1e-15) { // Exponent is real + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) { + if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; } + else ro = io = 0; + } else { + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2), + phio = r2*phi1; + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + } else { // Exponent is complex + if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0; + const double + mod1_2 = r1*r1 + i1*i1, + phi1 = std::atan2(i1,r1), + modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1), + phio = r2*phi1 + 0.5*i2*std::log(mod1_2); + ro = modo*std::cos(phio); + io = modo*std::sin(phio); + } + *(ptrd++) = ro; + *ptrd = io; + } + + static double mp_complex_pow_ss(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_sv(_cimg_math_parser& mp) { + const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vs(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd); + return cimg::type::nan(); + } + + static double mp_complex_pow_vv(_cimg_math_parser& mp) { + const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1; + double *ptrd = &_mp_arg(1) + 1; + _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd); + return cimg::type::nan(); + } + + static double mp_complex_cos(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::cos(real)*std::cosh(imag); + ptrd[1] = -std::sin(real)*std::sinh(imag); + return cimg::type::nan(); + } + + static double mp_complex_sin(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sin(real)*std::cosh(imag); + ptrd[1] = std::cos(real)*std::sinh(imag); + return cimg::type::nan(); + } + + static double mp_complex_tan(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sin(2*real)/denom; + ptrd[1] = std::sinh(2*imag)/denom; + return cimg::type::nan(); + } + + static double mp_complex_cosh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::cosh(real)*std::cos(imag); + ptrd[1] = std::sinh(real)*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_sinh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sinh(real)*std::cos(imag); + ptrd[1] = std::cosh(real)*std::sin(imag); + return cimg::type::nan(); + } + + static double mp_complex_tanh(_cimg_math_parser& mp) { + const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag); + double *ptrd = &_mp_arg(1) + 1; + ptrd[0] = std::sinh(2*real)/denom; + ptrd[1] = std::sin(2*imag)/denom; + return cimg::type::nan(); + } + + static double mp_continue(_cimg_math_parser& mp) { + mp.break_type = 2; + mp.p_code = mp.p_break - 1; + return cimg::type::nan(); + } + + static double mp_convolve(_cimg_math_parser &mp) { + return _mp_correlate(mp,true); + } + + static double mp_copy(_cimg_math_parser& mp) { + return _mp_arg(2); + } + + static double mp_correlate(_cimg_math_parser &mp) { + return _mp_correlate(mp,false); + } + + static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) { + double *ptrd = &_mp_arg(1) + 1; + const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1; + const unsigned int + wA = (unsigned int)mp.opcode[3], + hA = (unsigned int)mp.opcode[4], + dA = (unsigned int)mp.opcode[5], + sA = (unsigned int)mp.opcode[6], + wM = (unsigned int)mp.opcode[8], + hM = (unsigned int)mp.opcode[9], + dM = (unsigned int)mp.opcode[10], + sM = (unsigned int)mp.opcode[11], + boundary_conditions = (unsigned int)_mp_arg(12), + channel_mode = (unsigned int)mp.opcode[14], + xcenter = (unsigned int)_mp_arg(15), + ycenter = (unsigned int)_mp_arg(16), + zcenter = (unsigned int)_mp_arg(17), + xstart = (unsigned int)mp.opcode[18], + ystart = (unsigned int)mp.opcode[19], + zstart = (unsigned int)mp.opcode[20], + xend = (unsigned int)mp.opcode[21], + yend = (unsigned int)mp.opcode[22], + zend = (unsigned int)mp.opcode[23]; + const bool + is_normalized = (bool)_mp_arg(13); + const float + xstride = (float)_mp_arg(24), + ystride = (float)_mp_arg(25), + zstride = (float)_mp_arg(26), + xdilation = (float)_mp_arg(27), + ydilation = (float)_mp_arg(28), + zdilation = (float)_mp_arg(29); + CImg res; + if (is_convolve) res = CImg(ptrA,wA,hA,dA,sA,true). + get_convolve(CImg(ptrM,wM,hM,dM,sM,true), + boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter, + xstart,ystart,zstart, + xend,yend,zend, + xstride,ystride,zstride, + xdilation,ydilation,zdilation); + else res = CImg(ptrA,wA,hA,dA,sA,true). + get_correlate(CImg(ptrM,wM,hM,dM,sM,true), + boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter, + xstart,ystart,zstart, + xend,yend,zend, + xstride,ystride,zstride, + xdilation,ydilation,zdilation); + CImg(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res; + return cimg::type::nan(); + } + + static double mp_cos(_cimg_math_parser& mp) { + return std::cos(_mp_arg(2)); + } + + static double mp_cosh(_cimg_math_parser& mp) { + return std::cosh(_mp_arg(2)); + } + + static double mp_critical(_cimg_math_parser& mp) { + const ulongT g_target = mp.opcode[1]; + cimg_pragma_openmp(critical(mp_critical)) + { + for (const CImg *const p_end = ++mp.p_code + mp.opcode[2]; + mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + } + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const unsigned int + dx = (unsigned int)mp.opcode[7], + dy = (unsigned int)mp.opcode[8], + dz = (unsigned int)mp.opcode[9], + dc = (unsigned int)mp.opcode[10]; + const unsigned int boundary_conditions = (unsigned int)_mp_arg(11); + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgin:mp.listin[ind]; + if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double)); + else CImg(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c, + x + dx - 1,y + dy - 1, + z + dz - 1,c + dc - 1, + boundary_conditions); + return cimg::type::nan(); + } + + static double mp_cross(_cimg_math_parser& mp) { + CImg + vout(&_mp_arg(1) + 1,1,3,1,1,true), + v1(&_mp_arg(2) + 1,1,3,1,1,true), + v2(&_mp_arg(3) + 1,1,3,1,1,true); + (vout = v1).cross(v2); + return cimg::type::nan(); + } + + static double mp_cut(_cimg_math_parser& mp) { + double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4); + return valcmax?cmax:val; + } + + static double mp_date(_cimg_math_parser& mp) { + const unsigned int + siz_out = (unsigned int)mp.opcode[2], + siz_arg1 = (unsigned int)mp.opcode[4], + siz_arg2 = (unsigned int)mp.opcode[6]; + double *ptr_out = &_mp_arg(1) + (siz_out?1:0); + const double + *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0), + *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1; + + if (!ptr_arg2) { // No filename specified + if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1); + if (siz_arg1==~0U) for (unsigned int k = 0; k::nan(); + } + + // Filename specified. + CImg ss(siz_arg2 + 1); + cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i]; + ss.back() = 0; + if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1); + for (unsigned int k = 0; k::nan(); + } + + static double mp_debug(_cimg_math_parser& mp) { + CImg expr(mp.opcode[2] - 4); + { + const ulongT *ptrs = mp.opcode._data + 4; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + } + cimg::strellipsize(expr); + const ulongT g_target = mp.opcode[1]; + +#if cimg_use_openmp==0 + const unsigned int n_thread = 0; +#else + const unsigned int n_thread = omp_get_thread_num(); +#endif + cimg_pragma_openmp(critical(mp_debug)) + { + std::fprintf(cimg::output(), + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" + "Start debugging expression '%s', code length %u -> mem[%u] (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width); + std::fflush(cimg::output()); + mp.debug_indent+=3; + } + const CImg *const p_end = ++mp.p_code + mp.opcode[3]; + CImg _op; + for ( ; mp.p_code &op = *mp.p_code; + mp.opcode._data = op._data; + + _op.assign(1,op._height - 1); + const ulongT *ptrs = op._data + 1; + for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd mem[%u] = %.17g", + (void*)&mp,n_thread,mp.debug_indent,' ', + (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(), + (unsigned int)target,mp.mem[target]); + std::fflush(cimg::output()); + } + } + cimg_pragma_openmp(critical(mp_debug)) + { + mp.debug_indent-=3; + std::fprintf(cimg::output(), + "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c" + "End debugging expression '%s' -> mem[%u] = %.17g (memsize: %u)", + (void*)&mp,n_thread,mp.debug_indent,' ', + expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width); + std::fflush(cimg::output()); + } + --mp.p_code; + return mp.mem[g_target]; + } + + static double mp_decrement(_cimg_math_parser& mp) { + return _mp_arg(2) - 1; + } + + static double mp_det(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + return CImg(ptrs,k,k,1,1,true).det(); + } + + static double mp_diag(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3; + double *ptrd = &_mp_arg(1) + 1; + std::memset(ptrd,0,siz*siz*sizeof(double)); + for (unsigned int i = 3; i::nan(); + } + + static double mp_display_memory(_cimg_math_parser& mp) { + cimg::unused(mp); + std::fputc('\n',cimg::output()); + CImg title(128); + cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width); + mp.mem.display(title); + return cimg::type::nan(); + } + + static double mp_display(_cimg_math_parser& mp) { + const unsigned int + _siz = (unsigned int)mp.opcode[3], + siz = _siz?_siz:1; + const double *const ptr = &_mp_arg(1) + (_siz?1:0); + const int + w = (int)_mp_arg(4), + h = (int)_mp_arg(5), + d = (int)_mp_arg(6), + s = (int)_mp_arg(7); + CImg img; + if (w>0 && h>0 && d>0 && s>0) { + if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true); + else img.assign(ptr,siz).resize(w,h,d,s,-1); + } else img.assign(ptr,1,siz,1,1,true); + + CImg expr(mp.opcode[2] - 8); + const ulongT *ptrs = mp.opcode._data + 8; + cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++); + ((CImg::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr); + cimg::strellipsize(expr); + std::fputc('\n',cimg::output()); + img.display(expr._data); + return cimg::type::nan(); + } + + static double mp_div(_cimg_math_parser& mp) { + return _mp_arg(2)/_mp_arg(3); + } + + static double mp_dot(_cimg_math_parser& mp) { + const unsigned int siz = (unsigned int)mp.opcode[4]; + return CImg(&_mp_arg(2) + 1,1,siz,1,1,true). + dot(CImg(&_mp_arg(3) + 1,1,siz,1,1,true)); + } + + static double mp_do(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_body = ++mp.p_code, + *const p_cond = p_body + mp.opcode[3], + *const p_end = p_cond + mp.opcode[4]; + const unsigned int vsiz = (unsigned int)mp.opcode[5]; + if (mp.opcode[6]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[7]) mp.mem[mem_cond] = 0; + + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + do { + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } while (mp.mem[mem_cond]); + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_draw(_cimg_math_parser& mp) { + const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7); + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + unsigned int + dx = (unsigned int)mp.opcode[8], + dy = (unsigned int)mp.opcode[9], + dz = (unsigned int)mp.opcode[10], + dc = (unsigned int)mp.opcode[11]; + dx = dx==~0U?img._width:(unsigned int)_mp_arg(8); + dy = dy==~0U?img._height:(unsigned int)_mp_arg(9); + dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10); + dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11); + + const ulongT sizS = mp.opcode[2]; + if (sizS<(ulongT)dx*dy*dz*dc) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Sprite dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + CImg S(&_mp_arg(1) + 1,dx,dy,dz,dc,true); + const float opacity = (float)_mp_arg(12); + + if (img._data) { + if (mp.opcode[13]!=~0U) { // Opacity mask specified + const ulongT sizM = mp.opcode[14]; + if (sizM<(ulongT)dx*dy*dz) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': " + "Mask dimension (%lu values) and specified sprite geometry (%u,%u,%u,%u) " + "(%lu values) do not match.", + mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc); + const CImg M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true); + img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15)); + } else img.draw_image(x,y,z,c,S,opacity); + } + return cimg::type::nan(); + } + + static double mp_echo(_cimg_math_parser& mp) { + const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2; + if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type::nan(); } // No arguments + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(3 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + CImg(1,1,1,1,0).move_to(_str); + const CImg str = _str>'x'; + std::fprintf(cimg::output(),"\n%s",str._data); + return cimg::type::nan(); + } + + static double mp_ellipse(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + CImg color(img._spectrum,1,1,1,0); + bool is_invalid_arguments = false, is_outlined = false; + float r1 = 0, r2 = 0, angle = 0, opacity = 1; + unsigned int i = 4, pattern = ~0U; + int x0 = 0, y0 = 0; + if (i>=i_end) is_invalid_arguments = true; + else { + x0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + y0 = (int)cimg::round(_mp_arg(i++)); + if (i>=i_end) is_invalid_arguments = true; + else { + r1 = (float)_mp_arg(i++); + if (i>=i_end) r2 = r1; + else { + r2 = (float)_mp_arg(i++); + if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + + static double mp_eq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)==_mp_arg(3)); + } + + static double mp_erf(_cimg_math_parser& mp) { + return std::erf(_mp_arg(2)); + } + + static double mp_erfinv(_cimg_math_parser& mp) { + return cimg::erfinv(_mp_arg(2)); + } + + static double mp_exp(_cimg_math_parser& mp) { + return std::exp(_mp_arg(2)); + } + + static double mp_expr(_cimg_math_parser& mp) { + const unsigned int + sizs = (unsigned int)mp.opcode[3], + w = (unsigned int)mp.opcode[4], + h = (unsigned int)mp.opcode[5], + d = (unsigned int)mp.opcode[6], + s = (unsigned int)mp.opcode[7], + sizd = w*h*d*s; + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1); + CImg ss(sizs + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + if (!sizd) return CImg(w,h,d,s,0).eval(ss,0,0,0,0,&mp.listin,&mp.listout); // Scalar result + CImg(++ptrd,w,h,d,s,true) = CImg(w,h,d,s,0).fill(ss,true,true,&mp.listin,&mp.listout); + return cimg::type::nan(); + } + + static double mp_eye(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int k = (unsigned int)mp.opcode[2]; + CImg(ptrd,k,k,1,1,true).identity_matrix(); + return cimg::type::nan(); + } + + static double mp_f2ui(_cimg_math_parser& mp) { + return (double)cimg::float2uint((float)_mp_arg(2)); + } + + static double mp_factorial(_cimg_math_parser& mp) { + return cimg::factorial((int)_mp_arg(2)); + } + + static double mp_fibonacci(_cimg_math_parser& mp) { + return cimg::fibonacci((int)_mp_arg(2)); + } + + static double mp_fill(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double + *ptrd = &_mp_arg(1), + *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0, + *const ptrs = &_mp_arg(4); + if (siz) ++ptrd; else ++siz; // Fill vector-valued slot + const CImg + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[5]; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + unsigned int it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + *ptrc = (double)it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + else ptrd[it] = *ptrs; + ++it; + } + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return *ptrd; + } + + static double mp_find(_cimg_math_parser& mp) { + const int _step = (int)_mp_arg(6), step = _step?_step:-1; + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1); + if (ind<0 || ind>=(longT)siz) return -1.; + const double + *const ptrb = &_mp_arg(2) + 1, + *const ptre = ptrb + siz, + val = _mp_arg(4), + *ptr = ptrb + ind; + + // Forward search + if (step>0) { + while (ptr=ptre?-1.:(double)(ptr - ptrb); + } + + // Backward search. + while (ptr>=ptrb && *ptr!=val) ptr+=step; + return ptr0?0:siz1 - 1); + if (ind<0 || ind>=(longT)siz1) return -1.; + const double + *const ptr1b = &_mp_arg(2) + 1, + *const ptr1e = ptr1b + siz1, + *const ptr2b = &_mp_arg(4) + 1, + *const ptr2e = ptr2b + siz2, + *ptr1 = ptr1b + ind, + *p1 = 0, + *p2 = 0; + + // Forward search. + if (step>0) { + do { + while (ptr1=ptr1e) return -1.; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b && *ptr1!=*ptr2b) ptr1+=step; + if (ptr1=ptr1b); + return p2 + *const p_init = ++mp.p_code, + *const p_cond = p_init + mp.opcode[4], + *const p_body = p_cond + mp.opcode[5], + *const p_post = p_body + mp.opcode[6], + *const p_end = p_post + mp.opcode[7]; + const unsigned int vsiz = (unsigned int)mp.opcode[2]; + bool is_cond = false; + if (mp.opcode[8]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[9]) mp.mem[mem_cond] = 0; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + for (mp.p_code = p_init; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + + if (!mp.break_type) do { + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; + + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond && !mp.break_type) { + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + + for (mp.p_code = p_post; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } + } while (is_cond); + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_fsize(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const ulongT siz = (ulongT)mp.opcode[3]; + CImg ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::fsize(ss); + } + + static double mp_g(_cimg_math_parser& mp) { + cimg::unused(mp); + return cimg::grand(&mp.rng); + } + + static double mp_gauss(_cimg_math_parser& mp) { + const double x = _mp_arg(2), s = _mp_arg(3); + return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1); + } + +#ifdef cimg_mp_func_get + static double mp_get(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + double *ptrd = &_mp_arg(1); + const unsigned int + sizs = (unsigned int)mp.opcode[3], + sizd = (unsigned int)mp.opcode[4]; + const bool to_string = (bool)mp.opcode[5]; + CImg ss(sizs + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data); + else cimg_mp_func_get(ptrd,0,to_string,ss._data); + return cimg::type::nan(); + } +#endif + + static double mp_gcd(_cimg_math_parser& mp) { + return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3)); + } + +#ifdef cimg_mp_func_name + static double mp_name(_cimg_math_parser& mp) { + double *const ptr = &_mp_arg(1) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) std::memset(ptr,0,siz*sizeof(double)); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + cimg_mp_func_name(ind,ptr,siz); + } + return cimg::type::nan(); + } +#endif + + static double mp_gt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>_mp_arg(3)); + } + + static double mp_gte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)>=_mp_arg(3)); + } + + static double mp_i(_cimg_math_parser& mp) { + return (double)mp.imgin.atXYZC((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y], + (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c],(T)0); + } + + static double mp_if(_cimg_math_parser& mp) { + const bool is_cond = (bool)_mp_arg(2); + const ulongT + mem_left = mp.opcode[3], + mem_right = mp.opcode[4]; + const CImg + *const p_right = ++mp.p_code + mp.opcode[5], + *const p_end = p_right + mp.opcode[6]; + const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7]; + if (is_cond) for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + else for (mp.p_code = p_right; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.p_code==mp.p_break) --mp.p_code; + else mp.p_code = p_end - 1; + if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz); + return mp.mem[is_cond?mem_left:mem_right]; + } + + static double mp_image_d(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.depth(); + } + + static double mp_image_display(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + CImg title(256); + std::fputc('\n',cimg::output()); + cimg_snprintf(title,title._width,"[ Image #%u ]",ind); + img.display(title); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_h(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.height(); + } + + static double mp_image_median(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.median(); + } + + static double mp_image_norm(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.magnitude(); + } + + static double mp_image_print(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + CImg title(256); + std::fputc('\n',cimg::output()); + cimg_snprintf(title,title._width,"[ Image #%u ]",ind); + img.print(title); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_resize(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + const double + _w = mp.opcode[3]==~0U?-100:_mp_arg(3), + _h = mp.opcode[4]==~0U?-100:_mp_arg(4), + _d = mp.opcode[5]==~0U?-100:_mp_arg(5), + _s = mp.opcode[6]==~0U?-100:_mp_arg(6); + const unsigned int + w = (unsigned int)(_w>=0?_w:-_w*img.width()/100), + h = (unsigned int)(_h>=0?_h:-_h*img.height()/100), + d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100), + s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100), + interp = (int)_mp_arg(7); + if (mp.is_fill && img._data==mp.imgout._data) { + cimg::mutex(6,0); + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': " + "Cannot both fill and resize image (%u,%u,%u,%u) " + "to new dimensions (%u,%u,%u,%u).", + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s); + } + const unsigned int + boundary = (int)_mp_arg(8); + const float + cx = (float)_mp_arg(9), + cy = (float)_mp_arg(10), + cz = (float)_mp_arg(11), + cc = (float)_mp_arg(12); + img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_s(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.spectrum(); + } + + static double mp_image_sort(_cimg_math_parser& mp) { + const bool is_increasing = (bool)_mp_arg(3); + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listout.width()), + axis = (unsigned int)_mp_arg(4); + cimg::mutex(6); + CImg &img = mp.listout[ind]; + img.sort(is_increasing, + axis==0 || axis=='x'?'x': + axis==1 || axis=='y'?'y': + axis==2 || axis=='z'?'z': + axis==3 || axis=='c'?'c':0); + cimg::mutex(6,0); + return cimg::type::nan(); + } + + static double mp_image_stats(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind==~0U) CImg(ptrd,14,1,1,1,true) = mp.imgout.get_stats(); + else { + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg(ptrd,14,1,1,1,true) = mp.listout[ind].get_stats(); + } + return cimg::type::nan(); + } + + static double mp_image_w(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width(); + } + + static double mp_image_wh(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height(); + } + + static double mp_image_whd(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height()*img.depth(); + } + + static double mp_image_whds(_cimg_math_parser& mp) { + unsigned int ind = (unsigned int)mp.opcode[2]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + const CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + return (double)img.width()*img.height()*img.depth()*img.spectrum(); + } + + static double mp_increment(_cimg_math_parser& mp) { + return _mp_arg(2) + 1; + } + + static double mp_inrange(_cimg_math_parser& mp) { + const unsigned int sizd = (unsigned int)mp.opcode[2]; + const bool + include_m = (bool)_mp_arg(9), + include_M = (bool)_mp_arg(10); + if (!sizd) { // Scalar result + const double val = _mp_arg(3); + const double m = _mp_arg(5), M = _mp_arg(7); + if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val=M):(val>M)) && (include_m?(val<=m):(val=m) + ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val=M):(val>M)) && (include_m?(val<=m):(val::nan(); + } + + static double mp_int(_cimg_math_parser& mp) { + return (double)(longT)_mp_arg(2); + } + + static double mp_ioff(_cimg_math_parser& mp) { + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3); + const CImg &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::is_directory(ss); + } + + static double mp_isin(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + const double val = _mp_arg(3); + for (unsigned int i = 4; i::is_inf(_mp_arg(2)); + } + + static double mp_isint(_cimg_math_parser& mp) { + return (double)((double)(longT)_mp_arg(2)==_mp_arg(2)); + } + + static double mp_isfile(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2) + 1; + const ulongT siz = (ulongT)mp.opcode[3]; + CImg ss(siz + 1); + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + return (double)cimg::is_file(ss); + } + + static double mp_isnan(_cimg_math_parser& mp) { + return (double)cimg::type::is_nan(_mp_arg(2)); + } + + static double mp_ixyzc(_cimg_math_parser& mp) { + const unsigned int + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7); + const CImg &img = mp.imgin; + const double + x = _mp_arg(2), y = _mp_arg(3), + z = _mp_arg(4), c = _mp_arg(5); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx &img = mp.imgin; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), + z = oz + _mp_arg(4), c = oc + _mp_arg(5); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx vals(i_end - 4); + double *p = vals.data(); + for (unsigned int i = 4; i &img = mp.listin[indi]; + const int _step = (int)_mp_arg(5), step = _step?_step:-1; + const ulongT siz = (ulongT)img.size(); + longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1); + if (ind<0 || ind>=(longT)siz) return -1.; + const T + *const ptrb = img.data(), + *const ptre = img.end(), + *ptr = ptrb + ind; + const double val = _mp_arg(3); + + // Forward search + if (step>0) { + while (ptr=ptre?-1.:(double)(ptr - ptrb); + } + + // Backward search. + while (ptr>=ptrb && (double)*ptr!=val) ptr+=step; + return ptr &img = mp.listin[indi]; + const int _step = (bool)_mp_arg(6), step = _step?_step:-1; + const ulongT + siz1 = (ulongT)img.size(), + siz2 = (ulongT)mp.opcode[4]; + longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1); + if (ind<0 || ind>=(longT)siz1) return -1.; + const T + *const ptr1b = img.data(), + *const ptr1e = ptr1b + siz1, + *ptr1 = ptr1b + ind, + *p1 = 0; + const double + *const ptr2b = &_mp_arg(3) + 1, + *const ptr2e = ptr2b + siz2, + *p2 = 0; + + // Forward search. + if (step>0) { + do { + while (ptr1=ptr1e) return -1.; + p1 = ptr1 + 1; + p2 = ptr2b + 1; + while (p1=ptr1b && *ptr1!=*ptr2b) ptr1+=step; + if (ptr1=ptr1b); + return p2 &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.listin[ind]; + const double + x = _mp_arg(3), y = _mp_arg(4), + z = _mp_arg(5), c = _mp_arg(6); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx &img = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + if (off>=0 && off &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), + z = oz + _mp_arg(5), c = oc + _mp_arg(6); + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._cubic_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + case 1 : // Linear interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), + mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2); + return (double)img._linear_atXYZ(mx=img._spectrum?img._spectrum - 1:c)); + default : // Dirichlet + if (c<0 || c>=img._spectrum) return (T)0; + return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0); + } + default : // Nearest neighbor interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const int + w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(), + mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), + mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2); + return (double)img(mx::vector(mp.listin[ind].median()).move_to(mp.list_median[ind]); + return *mp.list_median[ind]; + } + + static double mp_list_norm(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + if (!mp.list_norm) mp.list_norm.assign(mp.listin._width); + if (!mp.list_norm[ind]) CImg::vector(mp.listin[ind].magnitude()).move_to(mp.list_norm[ind]); + return *mp.list_norm[ind]; + } + + static double mp_list_set_ioff(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), y = (int)_mp_arg(4), + z = (int)_mp_arg(5), c = (int)_mp_arg(6); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; + const int + x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const int + x = (int)_mp_arg(3), + y = (int)_mp_arg(4), + z = (int)_mp_arg(5); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_set_Joff_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.listout[ind]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + CImg &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(3)), + y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.listout[ind]; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(3)), + y = (int)(oy + _mp_arg(4)), + z = (int)(oz + _mp_arg(5)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_list_spectrum(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._spectrum; + } + + static double mp_list_stats(_cimg_math_parser& mp) { + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + k = (unsigned int)mp.opcode[3]; + if (!mp.list_stats) mp.list_stats.assign(mp.listin._width); + if (!mp.list_stats[ind]) mp.list_stats[ind].assign(1,14,1,1,0).fill(mp.listin[ind].get_stats(),false); + return mp.list_stats(ind,k); + } + + static double mp_list_wh(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height; + } + + static double mp_list_whd(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth; + } + + static double mp_list_whds(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width*mp.listin[ind]._height*mp.listin[ind]._depth*mp.listin[ind]._spectrum; + } + + static double mp_list_width(_cimg_math_parser& mp) { + const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()); + return (double)mp.listin[ind]._width; + } + + static double mp_list_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4), + vsiz = (unsigned int)mp.opcode[5]; + const CImg &img = mp.listin[ind]; + const longT + off = (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_list_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7), + vsiz = (unsigned int)mp.opcode[8]; + const CImg &img = mp.listin[ind]; + const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_list_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + boundary_conditions = (unsigned int)_mp_arg(4), + vsiz = (unsigned int)mp.opcode[5]; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z]; + const CImg &img = mp.listin[ind]; + const longT + off = img.offset(ox,oy,oz) + (longT)_mp_arg(3), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_list_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.listin.width()), + interpolation = (unsigned int)_mp_arg(6), + boundary_conditions = (unsigned int)_mp_arg(7), + vsiz = (unsigned int)mp.opcode[8]; + const CImg &img = mp.listin[ind]; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], + x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_log(_cimg_math_parser& mp) { + return std::log(_mp_arg(2)); + } + + static double mp_log10(_cimg_math_parser& mp) { + return std::log10(_mp_arg(2)); + } + + static double mp_log2(_cimg_math_parser& mp) { + return cimg::log2(_mp_arg(2)); + } + + static double mp_logical_and(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (!val_left) { mp.p_code = p_end - 1; return 0; } + const ulongT mem_right = mp.opcode[3]; + for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_logical_not(_cimg_math_parser& mp) { + return (double)!_mp_arg(2); + } + + static double mp_logical_or(_cimg_math_parser& mp) { + const bool val_left = (bool)_mp_arg(2); + const CImg *const p_end = ++mp.p_code + mp.opcode[4]; + if (val_left) { mp.p_code = p_end - 1; return 1; } + const ulongT mem_right = mp.opcode[3]; + for ( ; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + --mp.p_code; + return (double)(bool)mp.mem[mem_right]; + } + + static double mp_lowercase(_cimg_math_parser& mp) { + return cimg::lowercase(_mp_arg(2)); + } + + static double mp_lt(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<_mp_arg(3)); + } + + static double mp_lte(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)<=_mp_arg(3)); + } + + static double mp_matrix_eig(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + CImg val, vec; + CImg(ptr1,k,k,1,1,true).symmetric_eigen(val,vec); + CImg(ptrd,1,k,1,1,true) = val; + CImg(ptrd + k,k,k,1,1,true) = vec.get_transpose(); + return cimg::type::nan(); + } + + static double mp_matrix_invert(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptr1 = &_mp_arg(2) + 1; + const unsigned int k = (unsigned int)mp.opcode[3]; + const bool use_LU = (bool)_mp_arg(4); + CImg(ptrd,k,k,1,1,true) = CImg(ptr1,k,k,1,1,true).get_invert(use_LU); + return cimg::type::nan(); + } + + static double mp_matrix_mul(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode[4], + l = (unsigned int)mp.opcode[5], + m = (unsigned int)mp.opcode[6]; + CImg(ptrd,m,k,1,1,true) = CImg(ptr1,l,k,1,1,true)*CImg(ptr2,m,l,1,1,true); + return cimg::type::nan(); + } + + static double mp_matrix_pseudoinvert(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + const bool use_LU = (bool)_mp_arg(5); + CImg(ptrd,l,k,1,1,true) = CImg(ptr1,k,l,1,1,true).get_pseudoinvert(use_LU); + return cimg::type::nan(); + } + + static double mp_matrix_svd(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptr1 = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + CImg U, S, V; + CImg(ptr1,k,l,1,1,true).SVD(U,S,V); + CImg(ptrd,k,l,1,1,true) = U; + CImg(ptrd + k*l,1,k,1,1,true) = S; + CImg(ptrd + k*l + k,k,k,1,1,true) = V; + return cimg::type::nan(); + } + + static double mp_max(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; iabsval) { val = _val; absval = _absval; } + } + return val; + } + + static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref, + const longT siz, const long inc) { + const longT + off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind, + eoff = off + (siz - 1)*inc; + if (off<0 || eoff>=mp.mem.width()) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " + "Out-of-bounds variable pointer " + "(length: %ld, increment: %ld, offset start: %ld, " + "offset end: %ld, offset max: %u).", + mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1); + return &mp.mem[off]; + } + + static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref, + const longT siz, const long inc, const bool is_out) { + const unsigned ind = (unsigned int)p_ref[1]; + const CImg &img = is_out? + (ind==~0U?mp.imgout:mp.listout[cimg::mod((int)mp.mem[ind],mp.listout.width())]): + (ind==~0U?mp.imgin:mp.listin[cimg::mod((int)mp.mem[ind],mp.listin.width())]); + const bool is_relative = (bool)p_ref[2]; + int ox, oy, oz, oc; + longT off = 0; + if (is_relative) { + ox = (int)mp.mem[_cimg_mp_slot_x]; + oy = (int)mp.mem[_cimg_mp_slot_y]; + oz = (int)mp.mem[_cimg_mp_slot_z]; + oc = (int)mp.mem[_cimg_mp_slot_c]; + off = img.offset(ox,oy,oz,oc); + } + if ((*p_ref)%2) { + const int + x = (int)mp.mem[p_ref[3]], + y = (int)mp.mem[p_ref[4]], + z = (int)mp.mem[p_ref[5]], + c = *p_ref==5?0:(int)mp.mem[p_ref[6]]; + off+=img.offset(x,y,z,c); + } else off+=(longT)mp.mem[p_ref[3]]; + const longT eoff = off + (siz - 1)*inc; + if (off<0 || eoff>=(longT)img.size()) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': " + "Out-of-bounds image pointer " + "(length: %ld, increment: %ld, offset start: %ld, " + "offset end: %ld, offset max: %lu).", + mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1); + return (float*)&img[off]; + } + + static double mp_memcopy(_cimg_math_parser& mp) { + longT siz = (longT)_mp_arg(4); + const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6); + const float + _opacity = (float)_mp_arg(7), + opacity = (float)cimg::abs(_opacity), + omopacity = 1 - std::max(_opacity,0.f); + if (siz>0) { + const bool + is_doubled = mp.opcode[8]<=1, + is_doubles = mp.opcode[15]<=1; + if (is_doubled && is_doubles) { // (double*) <- (double*) + double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); + const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); + if (inc_d==1 && inc_s==1 && _opacity>=1) { + if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double)); + else std::memmove(ptrd,ptrs,siz*sizeof(double)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) { + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else { // Overlapping buffers + CImg buf((unsigned int)siz); + cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; } + ptrs = buf; + if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } + } + } + } else if (is_doubled && !is_doubles) { // (double*) <- (float*) + double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d); + const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false); + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else if (!is_doubled && is_doubles) { // (float*) <- (double*) + float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true); + const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s); + if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; } + } else { // (float*) <- (float*) + float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true); + const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false); + if (inc_d==1 && inc_s==1 && _opacity>=1) { + if (ptrs + siz - 1ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float)); + else std::memmove(ptrd,ptrs,siz*sizeof(float)); + } else { + if (ptrs + (siz - 1)*inc_sptrd + (siz - 1)*inc_d) { + if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; } + } else { // Overlapping buffers + CImg buf((unsigned int)siz); + cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; } + ptrs = buf; + if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; } + else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; } + } + } + } + } + return _mp_arg(1); + } + + static double mp_min(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i(ptrd,wS,wD,1,1,true) = CImg(ptrS,wS,hS,1,1,false). + project_matrix(CImg(ptrD,wD,hS,1,1,true),method,max_iter,max_residual); + return cimg::type::nan(); + } + + static double mp_mul(_cimg_math_parser& mp) { + return _mp_arg(2)*_mp_arg(3); + } + + static double mp_mul2(_cimg_math_parser& mp) { + return _mp_arg(2)*_mp_arg(3)*_mp_arg(4); + } + + static double mp_neq(_cimg_math_parser& mp) { + return (double)(_mp_arg(2)!=_mp_arg(3)); + } + + static double mp_norm0(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + switch (i_end - 3) { + case 1 : return _mp_arg(3)!=0; + case 2 : return (_mp_arg(3)!=0) + (_mp_arg(4)!=0); + } + double res = 0; + for (unsigned int i = 3; ires) res = val; + } + return res; + } + + static double mp_normp(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + if (i_end==4) return cimg::abs(_mp_arg(3)); + const double p = (double)mp.opcode[3]; + double res = 0; + for (unsigned int i = 4; i0?res:0.; + } + + static double mp_permutations(_cimg_math_parser& mp) { + return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4)); + } + + static double mp_polygon(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + unsigned int ind = (unsigned int)mp.opcode[3]; + if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.listin.width()); + CImg &img = ind==~0U?mp.imgout:mp.listout[ind]; + bool is_invalid_arguments = i_end<=4, is_outlined = false; + if (!is_invalid_arguments) { + int nbv = (int)_mp_arg(4); + if (!nbv) is_invalid_arguments = true; + else { + if (nbv<0) { nbv = -nbv; is_outlined = true; } + CImg points(nbv,2,1,1,0); + CImg color(img._spectrum,1,1,1,0); + float opacity = 1; + unsigned int i = 5, pattern=~0U; + cimg_foroff(points,k) if (i args(i_end - 4); + cimg_forX(args,k) args[k] = _mp_arg(4 + k); + if (ind==~0U) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '%s'. ", + mp.imgin.pixel_type(),args.value_string()._data); + else + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': " + "Invalid arguments '#%u%s%s'. ", + mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data); + } + return cimg::type::nan(); + } + + static double mp_pow(_cimg_math_parser& mp) { + const double v = _mp_arg(2), p = _mp_arg(3); + return std::pow(v,p); + } + + static double mp_pow0_25(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return std::sqrt(std::sqrt(val)); + } + + static double mp_pow3(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val; + } + + static double mp_pow4(_cimg_math_parser& mp) { + const double val = _mp_arg(2); + return val*val*val*val; + } + + static double mp_print(_cimg_math_parser& mp) { + const double val = _mp_arg(1); + const bool print_char = (bool)mp.opcode[3]; + cimg_pragma_openmp(critical(mp_print)) + { + CImg _expr(mp.opcode[2] - 4); + const ulongT *ptrs = mp.opcode._data + 4; + cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(_expr); + cimg::mutex(6); + if (print_char) + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'", + _expr._data,val,(int)val); + else + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g", + _expr._data,val); + std::fflush(cimg::output()); + cimg::mutex(6,0); + } + return val; + } + + static double mp_prod(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i + *const p_body = ++mp.p_code, + *const p_end = p_body + mp.opcode[4]; + + if (nb_it>0) { + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + + double it = 0; + if (ptrc) { // Version with loop variable (3 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + *ptrc = it; + } else // Version without loop variable (2 arguments) + while (it_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + ++it; + } + mp.break_type = _break_type; + } + + mp.p_code = p_end - 1; + return *ptrs; + } + + static double mp_rol(_cimg_math_parser& mp) { + return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3)); + } + + static double mp_ror(_cimg_math_parser& mp) { + return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3)); + } + + static double mp_rot2d(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float + theta = (float)_mp_arg(2)*cimg::PI/180, + ca = std::cos(theta), + sa = std::sin(theta); + *(ptrd++) = ca; + *(ptrd++) = -sa; + *(ptrd++) = sa; + *ptrd = ca; + return cimg::type::nan(); + } + + static double mp_rot3d(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const float x = (float)_mp_arg(2), y = (float)_mp_arg(3), z = (float)_mp_arg(4), theta = (float)_mp_arg(5); + CImg(ptrd,3,3,1,1,true) = CImg::rotation_matrix(x,y,z,theta); + return cimg::type::nan(); + } + + static double mp_round(_cimg_math_parser& mp) { + return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4)); + } + + static double mp_self_add(_cimg_math_parser& mp) { + return _mp_arg(1)+=_mp_arg(2); + } + + static double mp_self_bitwise_and(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val & (longT)_mp_arg(2)); + } + + static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val<<(unsigned int)_mp_arg(2)); + } + + static double mp_self_bitwise_or(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val | (longT)_mp_arg(2)); + } + + static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = (double)((longT)val>>(unsigned int)_mp_arg(2)); + } + + static double mp_self_decrement(_cimg_math_parser& mp) { + return --_mp_arg(1); + } + + static double mp_self_increment(_cimg_math_parser& mp) { + return ++_mp_arg(1); + } + + static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2]; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode[2] = mp.opcode[4]; // Scalar argument + l_opcode.swap(mp.opcode); + ulongT &target = mp.opcode[1]; + while (siz-->0) { target = ptrd++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector + unsigned int + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + ulongT &target = mp.opcode[1], &argument = mp.opcode[2]; + while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_self_mul(_cimg_math_parser& mp) { + return _mp_arg(1)*=_mp_arg(2); + } + + static double mp_self_div(_cimg_math_parser& mp) { + return _mp_arg(1)/=_mp_arg(2); + } + + static double mp_self_modulo(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = cimg::mod(val,_mp_arg(2)); + } + + static double mp_self_pow(_cimg_math_parser& mp) { + double &val = _mp_arg(1); + return val = std::pow(val,_mp_arg(2)); + } + + static double mp_self_sub(_cimg_math_parser& mp) { + return _mp_arg(1)-=_mp_arg(2); + } + + static double mp_set_ioff(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + x = (int)_mp_arg(2), y = (int)_mp_arg(3), + z = (int)_mp_arg(4), c = (int)_mp_arg(5); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whds = (longT)img.size(); + const double val = _mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], + oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c]; + const int + x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5)); + const double val = _mp_arg(1); + if (x>=0 && x=0 && y=0 && z=0 && c &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Ixyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + x = (int)_mp_arg(2), + y = (int)_mp_arg(3), + z = (int)_mp_arg(4); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const int + x = (int)_mp_arg(2), + y = (int)_mp_arg(3), + z = (int)_mp_arg(4); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_set_Joff_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T val = (T)_mp_arg(1); + if (off>=0 && off &img = mp.imgout; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c]; + const longT + off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const double *ptrs = &_mp_arg(1) + 1; + if (off>=0 && off::nan(); + } + + static double mp_set_Jxyz_s(_cimg_math_parser& mp) { + CImg &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(2)), + y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)); + const T val = (T)_mp_arg(1); + if (x>=0 && x=0 && y=0 && z &img = mp.imgout; + const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z]; + const int + x = (int)(ox + _mp_arg(2)), + y = (int)(oy + _mp_arg(3)), + z = (int)(oz + _mp_arg(4)); + const double *ptrs = &_mp_arg(1) + 1; + if (x>=0 && x=0 && y=0 && z::nan(); + } + + static double mp_shift(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int siz = (unsigned int)mp.opcode[3]; + const int + shift = (int)_mp_arg(4), + boundary_conditions = (int)_mp_arg(5); + CImg(ptrd,siz,1,1,1,true) = CImg(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions); + return cimg::type::nan(); + } + + static double mp_sign(_cimg_math_parser& mp) { + return cimg::sign(_mp_arg(2)); + } + + static double mp_sin(_cimg_math_parser& mp) { + return std::sin(_mp_arg(2)); + } + + static double mp_sinc(_cimg_math_parser& mp) { + return cimg::sinc(_mp_arg(2)); + } + + static double mp_sinh(_cimg_math_parser& mp) { + return std::sinh(_mp_arg(2)); + } + + static double mp_solve(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(3) + 1; + const unsigned int + k = (unsigned int)mp.opcode[4], + l = (unsigned int)mp.opcode[5], + m = (unsigned int)mp.opcode[6]; + CImg(ptrd,m,k,1,1,true) = CImg(ptr2,m,l,1,1,false).solve(CImg(ptr1,k,l,1,1,true)); + return cimg::type::nan(); + } + + static double mp_sort(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const bool is_increasing = (bool)_mp_arg(4); + const unsigned int + siz = (unsigned int)mp.opcode[3], + nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5), + siz_elt = (unsigned int)_mp_arg(6); + const ulongT sn = siz_elt*nb_elts; + if (sn>siz || siz_elt<1) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': " + "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid " + "for sorting a vector of size %u.", + mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz); + CImg(ptrd,siz_elt,nb_elts,1,1,true) = CImg(ptrs,siz_elt,nb_elts,1,1,true). + get_sort(is_increasing,siz_elt>1?'y':0); + if (sn(ptrd + sn,siz - sn,1,1,1,true) = CImg(ptrs + sn,siz - sn,1,1,1,true); + return cimg::type::nan(); + } + + static double mp_sqr(_cimg_math_parser& mp) { + return cimg::sqr(_mp_arg(2)); + } + + static double mp_sqrt(_cimg_math_parser& mp) { + return std::sqrt(_mp_arg(2)); + } + + static double mp_srand(_cimg_math_parser& mp) { + mp.rng = (cimg_uint64)_mp_arg(2); + return cimg::type::nan(); + } + + static double mp_srand0(_cimg_math_parser& mp) { + cimg::srand(&mp.rng); + +#if cimg_use_openmp!=0 + mp.rng+=omp_get_thread_num(); +#endif + return cimg::type::nan(); + } + + static double mp_std(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i0) mp.mem[ptrd++] = (double)*(ptrs++); + return cimg::type::nan(); + } + +#ifdef cimg_mp_func_store + static double mp_store(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2), + *ptr2 = &_mp_arg(4) + 1; + const unsigned int + siz1 = (unsigned int)mp.opcode[3], + siz2 = (unsigned int)mp.opcode[5]; + const int + w = (int)_mp_arg(6), + h = (int)_mp_arg(7), + d = (int)_mp_arg(8), + s = (int)_mp_arg(9); + + const bool is_compressed = (bool)_mp_arg(10); + if (w<0 || h<0 || d<0 || s<0) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': " + "Specified image dimensions (%d,%d,%d,%d) are invalid.", + pixel_type(),w,h,d,s); + CImg ss(siz2 + 1); + cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i]; + ss.back() = 0; + if (siz1) cimg_mp_func_store(ptr1 + 1,siz1, + (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); + else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s, + is_compressed,ss._data); + return cimg::type::nan(); + } +#endif + + static double mp_stov(_cimg_math_parser& mp) { + const double *ptrs = &_mp_arg(2); + const ulongT siz = (ulongT)mp.opcode[3]; + longT ind = (longT)_mp_arg(4); + const bool is_strict = (bool)_mp_arg(5); + double val = cimg::type::nan(); + if (ind<0 || ind>=(longT)siz) return val; + if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val; + + CImg ss(siz + 1 - ind); + ptrs+=1 + ind; + cimg_forX(ss,i) ss[i] = (char)ptrs[i]; + ss.back() = 0; + + const char *s = ss._data; + while (*s && *s<=32) ++s; + const bool is_negative = *s=='-'; + if (is_negative || *s=='+') ++s; + int err = 0; + char sep; + + if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number + unsigned int ival; + err = cimg_sscanf(s + 2,"%x%c",&ival,&sep); + if (err>0) val = (double)ival; + } else if (*s>32) { // Decimal number + err = cimg_sscanf(s,"%lf%c",&val,&sep); +#if cimg_OS==2 + // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able + // to read those particular values. + if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) { + if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type::inf(); err = 1 + (s[3]!=0); } + else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type::nan(); err = 1 + (s[3]!=0); } + } +#endif + } + if (err<=0 || (is_strict && err!=1)) return cimg::type::nan(); + if (is_negative) val = -val; + return val; + } + + static double mp_string(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2; + CImgList _str; + CImg it; + for (unsigned int n = 0; n string + const double *ptr = &_mp_arg(4 + 2*n) + 1; + unsigned int l = 0; + while (l(ptr,l,1,1,1,true).move_to(_str); + } else { // Scalar argument -> number + it.assign(24); + cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n)); + CImg::string(it,false,true).move_to(_str); + } + } + const CImg str = _str>'x'; + const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]); + std::memset(ptrd,0,mp.opcode[2]*sizeof(double)); + for (unsigned int k = 0; k::nan(); + } + + static double mp_sub(_cimg_math_parser& mp) { + return _mp_arg(2) - _mp_arg(3); + } + + static double mp_sum(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + double val = _mp_arg(3); + for (unsigned int i = 4; i(ptrs,k,k,1,1,true).trace(); + } + + static double mp_transpose(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const unsigned int + k = (unsigned int)mp.opcode[3], + l = (unsigned int)mp.opcode[4]; + CImg(ptrd,l,k,1,1,true) = CImg(ptrs,k,l,1,1,true).get_transpose(); + return cimg::type::nan(); + } + + static double mp_u(_cimg_math_parser& mp) { + return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng); + } + + static double mp_ui2f(_cimg_math_parser& mp) { + return (double)cimg::uint2float((unsigned int)_mp_arg(2)); + } + + static double mp_uppercase(_cimg_math_parser& mp) { + return cimg::uppercase(_mp_arg(2)); + } + + static double mp_var(_cimg_math_parser& mp) { + const unsigned int i_end = (unsigned int)mp.opcode[2]; + CImg vals(i_end - 3); + double *p = vals.data(); + for (unsigned int i = 3; i::nan(); + } + + static double mp_vector_crop(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const double *ptrs = &_mp_arg(2) + 1; + const longT + length = (longT)mp.opcode[3], + start = (longT)_mp_arg(4), + sublength = (longT)mp.opcode[5], + step = (longT)_mp_arg(6); + if (start<0 || start + step*(sublength-1)>=length) + throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': " + "Out-of-bounds sub-vector request " + "(length: %ld, start: %ld, sub-length: %ld, step: %ld).", + mp.imgin.pixel_type(),length,start,sublength,step); + ptrs+=start; + if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double)); + else for (longT k = 0; k::nan(); + } + + static double mp_vector_init(_cimg_math_parser& mp) { + unsigned int + ptrs = 4U, + ptrd = (unsigned int)mp.opcode[1] + 1, + siz = (unsigned int)mp.opcode[3]; + switch (mp.opcode[2] - 4) { + case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given + case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break; + default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; } + } + return cimg::type::nan(); + } + + static double mp_vector_eq(_cimg_math_parser& mp) { + const double + *ptr1 = &_mp_arg(2) + 1, + *ptr2 = &_mp_arg(4) + 1; + unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n; + const int N = (int)_mp_arg(6); + const bool case_sensitive = (bool)_mp_arg(7); + bool still_equal = true; + double value; + if (!N) return true; + + // Compare all values. + if (N<0) { + if (p1>0 && p2>0) { // Vector == vector + if (p1!=p2) return false; + if (case_sensitive) + while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++); + else + while (still_equal && p1--) + still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); + return still_equal; + } else if (p1>0 && !p2) { // Vector == scalar + value = _mp_arg(4); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && p1--) still_equal = *(ptr1++)==value; + return still_equal; + } else if (!p1 && p2>0) { // Scalar == vector + value = _mp_arg(2); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && p2--) still_equal = *(ptr2++)==value; + return still_equal; + } else { // Scalar == scalar + if (case_sensitive) return _mp_arg(2)==_mp_arg(4); + else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); + } + } + + // Compare only first N values. + if (p1>0 && p2>0) { // Vector == vector + n = cimg::min((unsigned int)N,p1,p2); + if (case_sensitive) + while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++); + else + while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++)); + return still_equal; + } else if (p1>0 && !p2) { // Vector == scalar + n = std::min((unsigned int)N,p1); + value = _mp_arg(4); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && n--) still_equal = *(ptr1++)==value; + return still_equal; + } else if (!p1 && p2>0) { // Scalar == vector + n = std::min((unsigned int)N,p2); + value = _mp_arg(2); + if (!case_sensitive) value = cimg::lowercase(value); + while (still_equal && n--) still_equal = *(ptr2++)==value; + return still_equal; + } // Scalar == scalar + if (case_sensitive) return _mp_arg(2)==_mp_arg(4); + return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4)); + } + + static double mp_vector_lerp(_cimg_math_parser& mp) { + unsigned int siz = (unsigned int)mp.opcode[2]; + double *ptrd = &_mp_arg(1) + 1; + const double + *ptrs1 = &_mp_arg(3) + 1, + *ptrs2 = &_mp_arg(4) + 1, + t = _mp_arg(5); + for (unsigned int k = 0; k::nan(); + } + + static double mp_vector_off(_cimg_math_parser& mp) { + const unsigned int + ptr = (unsigned int)mp.opcode[2] + 1, + siz = (unsigned int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type::nan(); + } + + static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(4); + l_opcode[2] = mp.opcode[4]; // Scalar argument1 + l_opcode.swap(mp.opcode); + ulongT &argument2 = mp.opcode[3]; + while (siz-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,3); + l_opcode.swap(mp.opcode); + ulongT &argument = mp.opcode[2]; + while (siz-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vs(_cimg_math_parser& mp) { // Operator(vector,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vss(_cimg_math_parser& mp) { // Operator(vector,scalar,scalar) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs = (unsigned int)mp.opcode[4] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,5); + l_opcode[3] = mp.opcode[5]; // Scalar argument2 + l_opcode[4] = mp.opcode[6]; // Scalar argument3 + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2]; + while (siz-->0) { argument1 = ptrs++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector) + unsigned int + siz = (unsigned int)mp.opcode[2], + ptrs1 = (unsigned int)mp.opcode[4] + 1, + ptrs2 = (unsigned int)mp.opcode[5] + 1; + double *ptrd = &_mp_arg(1) + 1; + mp_func op = (mp_func)mp.opcode[3]; + CImg l_opcode(1,4); + l_opcode.swap(mp.opcode); + ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3]; + while (siz-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); } + l_opcode.swap(mp.opcode); + return cimg::type::nan(); + } + + static double mp_vector_neq(_cimg_math_parser& mp) { + return !mp_vector_eq(mp); + } + + static double mp_vector_print(_cimg_math_parser& mp) { + const bool print_string = (bool)mp.opcode[4]; + cimg_pragma_openmp(critical(mp_vector_print)) + { + CImg _expr(mp.opcode[2] - 5); + const ulongT *ptrs = mp.opcode._data + 5; + cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++); + cimg::strellipsize(_expr); + unsigned int + ptr = (unsigned int)mp.opcode[1] + 1, + siz0 = (unsigned int)mp.opcode[3], + siz = siz0; + cimg::mutex(6); + std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data); + unsigned int count = 0; + while (siz-->0) { + if (count>=64 && siz>=64) { + std::fprintf(cimg::output(),"...,"); + ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64; + siz = 64; + } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":""); + ++count; + } + if (print_string) { + CImg str(siz0 + 1); + ptr = (unsigned int)mp.opcode[1] + 1; + for (unsigned int k = 0; k::nan(); + } + + static double mp_vector_resize(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4]; + const int + interpolation = (int)_mp_arg(5), + boundary_conditions = (int)_mp_arg(6); + if (p2) { // Resize vector + const double *const ptrs = &_mp_arg(3) + 1; + CImg(ptrd,p1,1,1,1,true) = CImg(ptrs,p2,1,1,1,true). + get_resize(p1,1,1,1,interpolation,boundary_conditions); + } else { // Resize scalar + const double value = _mp_arg(3); + CImg(ptrd,p1,1,1,1,true) = CImg(1,1,1,1,value).resize(p1,1,1,1,interpolation, + boundary_conditions); + } + return cimg::type::nan(); + } + + static double mp_vector_reverse(_cimg_math_parser& mp) { + double *const ptrd = &_mp_arg(1) + 1; + const double *const ptrs = &_mp_arg(2) + 1; + const unsigned int p1 = (unsigned int)mp.opcode[3]; + CImg(ptrd,p1,1,1,1,true) = CImg(ptrs,p1,1,1,1,true).get_mirror('x'); + return cimg::type::nan(); + } + + static double mp_vector_set_off(_cimg_math_parser& mp) { + const unsigned int + ptr = (unsigned int)mp.opcode[2] + 1, + siz = (unsigned int)mp.opcode[3]; + const int off = (int)_mp_arg(4); + if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1); + return _mp_arg(1); + } + +#define _cimg_mp_vfunc(func) \ + const longT sizd = (longT)mp.opcode[2];\ + const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \ + double *const ptrd = &_mp_arg(1) + (sizd?1:0); \ + cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \ + { CImg vec(nbargs); double res; \ + cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \ + cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \ + func; ptrd[k] = res; \ + }} \ + return sizd?cimg::type::nan():*ptrd; + + static double _mp_vargkth(CImg& vec) { + const double val = (+vec).get_shared_points(1,vec.width() - 1). + kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)); + cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.; + return 1.; + } + + static double mp_vargkth(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = _mp_vargkth(vec)); + } + + static double mp_vargmax(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data())); + } + + static double mp_vargmaxabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data())); + } + + static double mp_vargmin(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data())); + } + + static double mp_vargminabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data())); + } + + static double mp_vavg(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.mean()); + } + + static double mp_vkth(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1). + kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2))); + } + + static double mp_vmax(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.max()); + } + + static double mp_vmaxabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.maxabs()); + } + + static double mp_vmedian(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.median()); + } + + static double mp_vmin(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.min()); + } + + static double mp_vminabs(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.minabs()); + } + + static double mp_vprod(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.product()); + } + + static double mp_vstd(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3])); + } + + static double mp_vsum(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.sum()); + } + + static double mp_vvar(_cimg_math_parser& mp) { + _cimg_mp_vfunc(res = vec.get_stats()[3]); + } + + static double mp_vtos(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + sizd = (unsigned int)mp.opcode[2], + sizs = (unsigned int)mp.opcode[4]; + std::memset(ptrd,0,sizd*sizeof(double)); + const int nb_digits = (int)_mp_arg(5); + CImg format(8); + switch (nb_digits) { + case -1 : std::strcpy(format,"%g"); break; + case 0 : std::strcpy(format,"%.17g"); break; + default : cimg_snprintf(format,format._width,"%%.%dg",nb_digits); + } + CImg str; + if (sizs) { // Vector expression + const double *ptrs = &_mp_arg(3) + 1; + CImg(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str); + } else { // Scalar expression + str.assign(sizd + 1); + cimg_snprintf(str,sizd + 1,format,_mp_arg(3)); + } + const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1); + CImg(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1); + return cimg::type::nan(); + } + + static double mp_while(_cimg_math_parser& mp) { + const ulongT + mem_body = mp.opcode[1], + mem_cond = mp.opcode[2]; + const CImg + *const p_cond = ++mp.p_code, + *const p_body = p_cond + mp.opcode[3], + *const p_end = p_body + mp.opcode[4]; + const unsigned int vsiz = (unsigned int)mp.opcode[5]; + bool is_cond = false; + if (mp.opcode[6]) { // Set default value for result and condition if necessary + if (vsiz) CImg(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type::nan()); + else mp.mem[mem_body] = cimg::type::nan(); + } + if (mp.opcode[7]) mp.mem[mem_cond] = 0; + const unsigned int _break_type = mp.break_type; + mp.break_type = 0; + do { + for (mp.p_code = p_cond; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; + is_cond = (bool)mp.mem[mem_cond]; + if (is_cond && !mp.break_type) // Evaluate body + for (mp.p_code = p_body; mp.p_code_data; + const ulongT target = mp.opcode[1]; + mp.mem[target] = _cimg_mp_defunc(mp); + } + if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0; + } while (is_cond); + + mp.break_type = _break_type; + mp.p_code = p_end - 1; + return mp.mem[mem_body]; + } + + static double mp_Ioff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3), + vsiz = (unsigned int)mp.opcode[4]; + const CImg &img = mp.imgin; + const longT + off = (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_Ixyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6), + vsiz = (unsigned int)mp.opcode[7]; + const CImg &img = mp.imgin; + const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + + static double mp_Joff(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + boundary_conditions = (unsigned int)_mp_arg(3), + vsiz = (unsigned int)mp.opcode[4]; + const CImg &img = mp.imgin; + const int + ox = (int)mp.mem[_cimg_mp_slot_x], + oy = (int)mp.mem[_cimg_mp_slot_y], + oz = (int)mp.mem[_cimg_mp_slot_z]; + const longT + off = img.offset(ox,oy,oz) + (longT)_mp_arg(2), + whd = (longT)img.width()*img.height()*img.depth(); + const T *ptrs; + if (off>=0 && off::nan(); + } + if (img._data) switch (boundary_conditions) { + case 3 : { // Mirror + const longT whd2 = 2*whd, moff = cimg::mod(off,whd2); + ptrs = &img[moff::nan(); + } + case 2 : // Periodic + ptrs = &img[cimg::mod(off,whd)]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + case 1 : // Neumann + ptrs = off<0?&img[0]:&img[whd - 1]; + cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return cimg::type::nan(); + default : // Dirichlet + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + std::memset(ptrd,0,vsiz*sizeof(double)); + return cimg::type::nan(); + } + + static double mp_Jxyz(_cimg_math_parser& mp) { + double *ptrd = &_mp_arg(1) + 1; + const unsigned int + interpolation = (unsigned int)_mp_arg(5), + boundary_conditions = (unsigned int)_mp_arg(6), + vsiz = (unsigned int)mp.opcode[7]; + const CImg &img = mp.imgin; + const double + ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z], + x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4); + const ulongT whd = (ulongT)img._width*img._height*img._depth; + const T *ptrs; + switch (interpolation) { + case 2 : // Cubic interpolation + switch (boundary_conditions) { + case 3 : { // Mirror + const float + w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), + mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2), + cx = mx::nan(); + } + +#undef _mp_arg + + }; // struct _cimg_math_parser {} + +#define _cimg_create_pointwise_functions(name,func,min_size) \ + CImg& name() { \ + if (is_empty()) return *this; \ + cimg_openmp_for(*this,func((double)*ptr),min_size); \ + return *this; \ + } \ + CImg get_##name() const { \ + return CImg(*this,false).name(); \ + } + + //! Compute the square value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqr().normalize(0,255)).display(); + \endcode + \image html ref_sqr.jpg + **/ + _cimg_create_pointwise_functions(sqr,cimg::sqr,524288) + + //! Compute the square root of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg img("reference.jpg"); + (img,img.get_sqrt().normalize(0,255)).display(); + \endcode + \image html ref_sqrt.jpg + **/ + _cimg_create_pointwise_functions(sqrt,std::sqrt,8192) + + //! Compute the exponential of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(exp,std::exp,4096) + + //! Compute the logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm + \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log,std::log,262144) + + //! Compute the base-2 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm + \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log2,cimg::log2,4096) + + //! Compute the base-10 logarithm of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm + \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(log10,std::log10,4096) + + //! Compute the absolute value of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(abs,cimg::abs,524288) + + //! Compute the sign of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign + \f$\mathrm{sign}(I_{(x,y,z,c)})\f$. + \note + - The sign is set to: + - \c 1 if pixel value is strictly positive. + - \c -1 if pixel value is strictly negative. + - \c 0 if pixel value is equal to \c 0. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sign,cimg::sign,32768) + + //! Compute the cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(cos,std::cos,8192) + + //! Compute the sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being in \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sin,std::sin,8192) + + //! Compute the sinc of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc + \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sinc,cimg::sinc,2048) + + //! Compute the tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$. + \note + - Pixel values are regarded as being exin \e radian. + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(tan,std::tan,2048) + + //! Compute the hyperbolic cosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine + \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(cosh,std::cosh,2048) + + //! Compute the hyperbolic sine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine + \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(sinh,std::sinh,2048) + + //! Compute the hyperbolic tangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent + \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(tanh,std::tanh,2048) + + //! Compute the arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine + \f$\mathrm{acos}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(acos,std::acos,8192) + + //! Compute the arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine + \f$\mathrm{asin}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(asin,std::asin,8192) + + //! Compute the arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent + \f$\mathrm{atan}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(atan,std::atan,8192) + + //! Compute the arctangent2 of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2 + \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$. + \param img Image whose pixel values specify the second argument of the \c atan2() function. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2' + img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2' + img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value + (img_x,img_y,img_atan2).display(); + \endcode + **/ + template + CImg& atan2(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return atan2(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_atan2(const CImg& img) const { + return CImg(*this,false).atan2(img); + } + + //! Compute the hyperbolic arccosine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh + \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(acosh,cimg::acosh,8192) + + //! Compute the hyperbolic arcsine of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine + \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(asinh,cimg::asinh,8192) + + //! Compute the hyperbolic arctangent of each pixel value. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent + \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + **/ + _cimg_create_pointwise_functions(atanh,cimg::atanh,8192) + + //! In-place pointwise multiplication. + /** + Compute the pointwise multiplication between the image instance and the specified input image \c img. + \param img Input image, as the second operand of the multiplication. + \note + - Similar to operator+=(const CImg&), except that it performs a pointwise multiplication + instead of an addition. + - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg&) instead. + \par Example + \code + CImg + img("reference.jpg"), + shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false); + shade.normalize(0,1); + (img,shade,img.get_mul(shade)).display(); + \endcode + **/ + template + CImg& mul(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return mul(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_mul(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).mul(img); + } + + //! In-place pointwise division. + /** + Similar to mul(const CImg&), except that it performs a pointwise division instead of a multiplication. + **/ + template + CImg& div(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return div(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_div(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).div(img); + } + + //! Raise each pixel value to a specified power. + /** + Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$. + \param p Exponent value. + \note + - The \inplace of this method statically casts the computed values to the pixel type \c T. + - The \newinstance returns a \c CImg image, if the pixel type \c T is \e not float-valued. + \par Example + \code + const CImg + img0("reference.jpg"), // Load reference color image + img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8 + img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5 + (img0,img1,img2).display(); + \endcode + **/ + CImg& pow(const double p) { + if (is_empty()) return *this; + if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; } + if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; } + if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; } + if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; } + if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; } + if (p==0) return fill((T)1); + if (p==0.5) return sqrt(); + if (p==1) return *this; + if (p==2) return sqr(); + if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; } + if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; } + cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024); + return *this; + } + + //! Raise each pixel value to a specified power \newinstance. + CImg get_pow(const double p) const { + return CImg(*this,false).pow(p); + } + + //! Raise each pixel value to a power, specified from an expression. + /** + Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition. + **/ + CImg& pow(const char *const expression) { + return pow((+*this)._fill(expression,true,1,0,0,"pow",this)); + } + + //! Raise each pixel value to a power, specified from an expression \newinstance. + CImg get_pow(const char *const expression) const { + return CImg(*this,false).pow(expression); + } + + //! Raise each pixel value to a power, pointwisely specified from another image. + /** + Similar to operator+=(const CImg& img), except that it performs an exponentiation instead of an addition. + **/ + template + CImg& pow(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return pow(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_pow(const CImg& img) const { + return CImg(*this,false).pow(img); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const unsigned int n=1) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::rol(*ptr,n),32768); + return *this; + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const unsigned int n=1) const { + return (+*this).rol(n); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift. + **/ + CImg& rol(const char *const expression) { + return rol((+*this)._fill(expression,true,1,0,0,"rol",this)); + } + + //! Compute the bitwise left rotation of each pixel value \newinstance. + CImg get_rol(const char *const expression) const { + return (+*this).rol(expression); + } + + //! Compute the bitwise left rotation of each pixel value. + /** + Similar to operator<<=(const CImg&), except that it performs a left rotation instead of a left shift. + **/ + template + CImg& rol(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return rol(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_rol(const CImg& img) const { + return (+*this).rol(img); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const unsigned int n=1) { + if (is_empty()) return *this; + cimg_openmp_for(*this,cimg::ror(*ptr,n),32768); + return *this; + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const unsigned int n=1) const { + return (+*this).ror(n); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift. + **/ + CImg& ror(const char *const expression) { + return ror((+*this)._fill(expression,true,1,0,0,"ror",this)); + } + + //! Compute the bitwise right rotation of each pixel value \newinstance. + CImg get_ror(const char *const expression) const { + return (+*this).ror(expression); + } + + //! Compute the bitwise right rotation of each pixel value. + /** + Similar to operator>>=(const CImg&), except that it performs a right rotation instead of a right shift. + **/ + template + CImg& ror(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return ror(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg get_ror(const CImg& img) const { + return (+*this).ror(img); + } + + //! Pointwise min operator between instance image and a value. + /** + \param val Value used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& min(const T& value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,std::min(*ptr,value),65536); + return *this; + } + + //! Pointwise min operator between instance image and a value \newinstance. + CImg get_min(const T& value) const { + return (+*this).min(value); + } + + //! Pointwise min operator between two images. + /** + \param img Image used as the reference argument of the min operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& min(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return min(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_min(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).min(img); + } + + //! Pointwise min operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& min(const char *const expression) { + return min((+*this)._fill(expression,true,1,0,0,"min",this)); + } + + //! Pointwise min operator between an image and an expression \newinstance. + CImg get_min(const char *const expression) const { + return CImg(*this,false).min(expression); + } + + //! Pointwise max operator between instance image and a value. + /** + \param val Value used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& max(const T& value) { + if (is_empty()) return *this; + cimg_openmp_for(*this,std::max(*ptr,value),65536); + return *this; + } + + //! Pointwise max operator between instance image and a value \newinstance. + CImg get_max(const T& value) const { + return (+*this).max(value); + } + + //! Pointwise max operator between two images. + /** + \param img Image used as the reference argument of the max operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& max(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return max(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_max(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).max(img); + } + + //! Pointwise max operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& max(const char *const expression) { + return max((+*this)._fill(expression,true,1,0,0,"max",this)); + } + + //! Pointwise max operator between an image and an expression \newinstance. + CImg get_max(const char *const expression) const { + return CImg(*this,false).max(expression); + } + + //! Pointwise minabs operator between instance image and a value. + /** + \param val Value used as the reference argument of the minabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& minabs(const T& value) { + if (is_empty()) return *this; + const T absvalue = cimg::abs(value); + cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536); + return *this; + } + + //! Pointwise minabs operator between instance image and a value \newinstance. + CImg get_minabs(const T& value) const { + return (+*this).minabs(value); + } + + //! Pointwise minabs operator between two images. + /** + \param img Image used as the reference argument of the minabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& minabs(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return minabs(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_minabs(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).minabs(img); + } + + //! Pointwise minabs operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& minabs(const char *const expression) { + return minabs((+*this)._fill(expression,true,1,0,0,"minabs",this)); + } + + //! Pointwise minabs operator between an image and an expression \newinstance. + CImg get_minabs(const char *const expression) const { + return CImg(*this,false).minabs(expression); + } + + //! Pointwise maxabs operator between instance image and a value. + /** + \param val Value used as the reference argument of the maxabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$. + **/ + CImg& maxabs(const T& value) { + if (is_empty()) return *this; + const T absvalue = cimg::abs(value); + cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536); + return *this; + } + + //! Pointwise maxabs operator between instance image and a value \newinstance. + CImg get_maxabs(const T& value) const { + return (+*this).maxabs(value); + } + + //! Pointwise maxabs operator between two images. + /** + \param img Image used as the reference argument of the maxabs operator. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$. + **/ + template + CImg& maxabs(const CImg& img) { + const ulongT siz = size(), isiz = img.size(); + if (siz && isiz) { + if (is_overlapped(img)) return maxabs(+img); + T *ptrd = _data, *const ptre = _data + siz; + if (siz>isiz) for (ulongT n = siz/isiz; n; --n) + for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs + CImg<_cimg_Tt> get_maxabs(const CImg& img) const { + return CImg<_cimg_Tt>(*this,false).maxabs(img); + } + + //! Pointwise maxabs operator between an image and an expression. + /** + \param expression Math formula as a C-string. + \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by + \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$. + **/ + CImg& maxabs(const char *const expression) { + return maxabs((+*this)._fill(expression,true,1,0,0,"maxabs",this)); + } + + //! Pointwise maxabs operator between an image and an expression \newinstance. + CImg get_maxabs(const char *const expression) const { + return CImg(*this,false).maxabs(expression); + } + + //! Return a reference to the minimum pixel value. + /** + **/ + T& min() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min; + cimg_for(*this,ptrs,T) if (*ptrsmax_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value \const. + const T& max() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max; + cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + return *ptr_max; + } + + //! Return a reference to the maximum pixel value in absolute value. + /** + **/ + T& maxabs() { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "maxabs(): Empty instance.", + cimg_instance); + T *ptr_maxabs = _data; + T maxabs_value = *ptr_maxabs; + cimg_for(*this,ptrs,T) { + const T ma = cimg::abs(*ptrs); + if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; } + } + return *ptr_maxabs; + } + + //! Return a reference to the maximum pixel value in absolute value \const. + const T& maxabs() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "maxabs(): Empty instance.", + cimg_instance); + const T *ptr_maxabs = _data; + T maxabs_value = *ptr_maxabs; + cimg_for(*this,ptrs,T) { + const T ma = cimg::abs(*ptrs); + if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; } + } + return *ptr_maxabs; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value. + /** + \param[out] max_val Maximum pixel value. + **/ + template + T& min_max(t& max_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value as well as the maximum pixel value \const. + template + const T& min_max(t& max_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "min_max(): Empty instance.", + cimg_instance); + const T *ptr_min = _data; + T min_value = *ptr_min, max_value = min_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the maximum pixel value as well as the minimum pixel value. + /** + \param[out] min_val Minimum pixel value. + **/ + template + T& max_min(t& min_val) { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "max_min(): Empty instance.", + cimg_instance); + const T *ptr_max = _data; + T max_value = *ptr_max, min_value = max_value; + cimg_for(*this,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val=size()) return max(); + CImg arr(*this,false); + ulongT l = 0, ir = size() - 1; + for ( ; ; ) { + if (ir<=l + 1) { + if (ir==l + 1 && arr[ir]>1; + cimg::swap(arr[mid],arr[l + 1]); + if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); + if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]); + if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]); + ulongT i = l + 1, j = ir; + const T pivot = arr[l + 1]; + for ( ; ; ) { + do ++i; while (arr[i]pivot); + if (j=k) ir = j - 1; + if (j<=k) l = i; + } + } + } + + //! Return the median pixel value. + /** + **/ + T median() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "median(): Empty instance.", + cimg_instance); + const ulongT s = size(); + switch (s) { + case 1 : return _data[0]; + case 2 : return cimg::median(_data[0],_data[1]); + case 3 : return cimg::median(_data[0],_data[1],_data[2]); + case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]); + case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]); + case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]); + case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8], + _data[9],_data[10],_data[11],_data[12]); + } + const T res = kth_smallest(s>>1); + return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2); + } + + //! Return the product of all the pixel values. + /** + **/ + double product() const { + if (is_empty()) return 0; + double res = 1; + cimg_for(*this,ptrs,T) res*=(double)*ptrs; + return res; + } + + //! Return the sum of all the pixel values. + /** + **/ + double sum() const { + double res = 0; + cimg_for(*this,ptrs,T) res+=(double)*ptrs; + return res; + } + + //! Return the average pixel value. + /** + **/ + double mean() const { + double res = 0; + cimg_for(*this,ptrs,T) res+=(double)*ptrs; + return res/size(); + } + + //! Return the variance of the pixel values. + /** + \param variance_method Method used to estimate the variance. Can be: + - \c 0: Second moment, computed as + \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 = + 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$ + with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$. + - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$. + - \c 2: Least median of squares. + - \c 3: Least trimmed of squares. + **/ + double variance(const unsigned int variance_method=1) const { + double foo; + return variance_mean(variance_method,foo); + } + + //! Return the variance as well as the average of the pixel values. + /** + \param variance_method Method used to estimate the variance (see variance(const unsigned int) const). + \param[out] mean Average pixel value. + **/ + template + double variance_mean(const unsigned int variance_method, t& mean) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_mean(): Empty instance.", + cimg_instance); + + double variance = 0, average = 0; + const ulongT siz = size(); + switch (variance_method) { + case 0 : { // Least mean square (standard definition) + double S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } + variance = (S2 - S*S/siz)/siz; + average = S; + } break; + case 1 : { // Least mean square (robust definition) + double S = 0, S2 = 0; + cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; } + variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + average = S; + } break; + case 2 : { // Least Median of Squares (MAD) + CImg buf(*this,false); + buf.sort(); + const ulongT siz2 = siz>>1; + const double med_i = (double)buf[siz2]; + cimg_for(buf,ptrs,Tfloat) { + const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; + } + buf.sort(); + const double sig = (double)(1.4828*buf[siz2]); + variance = sig*sig; + } break; + default : { // Least trimmed of Squares + CImg buf(*this,false); + const ulongT siz2 = siz>>1; + cimg_for(buf,ptrs,Tfloat) { + const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; + } + buf.sort(); + double a = 0; + const Tfloat *ptrs = buf._data; + for (ulongT j = 0; j0?variance:0; + } + + //! Return estimated variance of the noise. + /** + \param variance_method Method used to compute the variance (see variance(const unsigned int) const). + \note Because of structures such as edges in images it is + recommended to use a robust variance estimation. The variance of the + noise is estimated by computing the variance of the Laplacian \f$(\Delta + I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]= + \sigma^2\f$ where \f$\sigma\f$ is the noise variance. + **/ + double variance_noise(const unsigned int variance_method=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "variance_noise(): Empty instance.", + cimg_instance); + + const ulongT siz = size(); + if (!siz || !_data) return 0; + if (variance_method>1) { // Compute a scaled version of the Laplacian + CImg tmp(*this,false); + if (_depth==1) { + const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3(I,T); + cimg_for3x3(*this,x,y,0,c,I,T) { + tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn + + (double)Icp - 4*(double)Icc); + } + } + } else { + const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,T); + cimg_for3x3x3(*this,x,y,z,c,I,T) { + tmp(x,y,z,c) = cste*( + (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); + } + } + } + return tmp.variance(variance_method); + } + + // Version that doesn't need intermediate images. + double variance = 0, S = 0, S2 = 0; + if (_depth==1) { + const double cste = 1./std::sqrt(20.); + CImg_3x3(I,T); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) { + const double val = cste*((double)Inc + (double)Ipc + + (double)Icn + (double)Icp - 4*(double)Icc); + S+=val; S2+=val*val; + } + } else { + const double cste = 1./std::sqrt(42.); + CImg_3x3x3(I,T); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) { + const double val = cste * + ((double)Incc + (double)Ipcc + (double)Icnc + + (double)Icpc + + (double)Iccn + (double)Iccp - 6*(double)Iccc); + S+=val; S2+=val*val; + } + } + if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0; + else variance = (S2 - S*S/siz)/siz; + return variance>0?variance:0; + } + + //! Compute the MSE (Mean-Squared Error) between two images. + /** + \param img Image used as the second argument of the MSE operator. + **/ + template + double MSE(const CImg& img) const { + if (img.size()!=size()) + throw CImgArgumentException(_cimg_instance + "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + double vMSE = 0; + const t* ptr2 = img._data; + cimg_for(*this,ptr1,T) { + const double diff = (double)*ptr1 - (double)*(ptr2++); + vMSE+=diff*diff; + } + const ulongT siz = img.size(); + if (siz) vMSE/=siz; + return vMSE; + } + + //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images. + /** + \param img Image used as the second argument of the PSNR operator. + \param max_value Maximum theoretical value of the signal. + **/ + template + double PSNR(const CImg& img, const double max_value=255) const { + const double vMSE = (double)std::sqrt(MSE(img)); + return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type::max()); + } + + //! Evaluate math formula. + /** + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + double eval(const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,x,y,z,c,list_inputs,list_outputs); + } + + // Fast function to pre-evaluate common expressions. + // (return 'true' in case of success, and set value of 'res'). + template + bool __eval(const char *const expression, t &res) const { + if (!expression || !*expression) { res = (t)0; return true; } + const char c = *expression; + bool is_success = false; + char c1, end; + double val; + if (c>='0' && c<='9') { // Possible value + if (!expression[1]) { // Single digit + res = (t)(c - '0'); + is_success = true; + } else if (std::sscanf(expression,"%lf%c",&val,&end)==1) { // Single value + res = (t)val; + is_success = true; + } + } else if ((c=='+' || c=='-' || c=='!') && // +Value, -Value or !Value + (c1=expression[1])>='0' && c1<='0') { + if (!expression[2]) { // [+-!] + Single digit + const int ival = c1 - '0'; + res = (t)(c=='+'?ival:c=='-'?-ival:!ival); + is_success = true; + } else if (std::sscanf(expression + 1,"%lf%c",&val,&end)==1) { // [+-!] Single value + res = (t)(c=='+'?val:c=='-'?-val:(double)!val); + is_success = true; + } + } else if (!expression[1]) switch (*expression) { // Other common single-char expressions + case 'w' : res = (t)_width; is_success = true; break; + case 'h' : res = (t)_height; is_success = true; break; + case 'd' : res = (t)_depth; is_success = true; break; + case 's' : res = (t)_spectrum; is_success = true; break; + case 'r' : res = (t)_is_shared; is_success = true; break; + } + return is_success; + } + + double _eval(CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression || !*expression) return 0; + double _val = 0; + if (__eval(expression,_val)) return _val; + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'),"eval", + *this,img_output,list_inputs,list_outputs,false); + mp.begin_t(); + const double val = mp(x,y,z,c); + mp.end_t(); + mp.end(); + return val; + } + + //! Evaluate math formula. + /** + \param[out] output Contains values of output vector returned by the evaluated expression + (or is empty if the returned type is scalar). + \param expression Math formula, as a C-string. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + void eval(CImg &output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + _eval(output,this,expression,x,y,z,c,list_inputs,list_outputs); + } + + //! Evaluate math formula \const. + template + void eval(CImg& output, const char *const expression, + const double x=0, const double y=0, const double z=0, const double c=0, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + _eval(output,0,expression,x,y,z,c,list_inputs,list_outputs); + } + + template + void _eval(CImg& output, CImg *const img_output, const char *const expression, + const double x, const double y, const double z, const double c, + const CImgList *const list_inputs, CImgList *const list_outputs) const { + if (!expression || !*expression) { output.assign(1); *output = 0; return; } + double _val = 0; + if (__eval(expression,_val)) { output.assign(1); *output = _val; return; } + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'),"eval", + *this,img_output,list_inputs,list_outputs,false); + output.assign(1,std::max(1U,mp.result_dim)); + mp.begin_t(); + mp(x,y,z,c,output._data); + mp.end_t(); + mp.end(); + } + + //! Evaluate math formula on a set of variables. + /** + \param expression Math formula, as a C-string. + \param xyzc Set of values (x,y,z,c) used for the evaluation. + \param list_inputs A list of input images attached to the specified math formula. + \param[out] list_outputs A pointer to a list of output images attached to the specified math formula. + **/ + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _eval(this,expression,xyzc,list_inputs,list_outputs); + } + + //! Evaluate math formula on a set of variables \const. + template + CImg eval(const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return _eval(0,expression,xyzc,list_inputs,list_outputs); + } + + template + CImg _eval(CImg *const output, const char *const expression, const CImg& xyzc, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + CImg res(1,xyzc.size()/4); + if (!expression || !*expression) return res.fill(0); + _cimg_math_parser mp(expression,"eval",*this,output,list_inputs,list_outputs,false); + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel if (res._height>=512)) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_eval)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + cimg_pragma_openmp(for) + for (int i = 0; i[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]. + **/ + CImg get_stats(const unsigned int variance_method=1) const { + if (is_empty()) return CImg(); + const ulongT siz = size(); + const longT off_end = (longT)siz; + double S = 0, S2 = 0, P = 1; + longT offm = 0, offM = 0; + T m = *_data, M = m; + + cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) { + longT loffm = 0, loffM = 0; + T lm = *_data, lM = lm; + cimg_pragma_openmp(for) + for (longT off = 0; offlM) { lM = val; loffM = off; } + S+=_val; + S2+=_val*_val; + P*=_val; + } + cimg_pragma_openmp(critical(get_stats)) { + if (lmM || (lM==M && loffM1?(S2 - S*S/siz)/(siz - 1):0): + variance(variance_method)), + variance_value = _variance_value>0?_variance_value:0; + int + xm = 0, ym = 0, zm = 0, cm = 0, + xM = 0, yM = 0, zM = 0, cM = 0; + contains(_data[offm],xm,ym,zm,cm); + contains(_data[offM],xM,yM,zM,cM); + return CImg(1,14).fill((double)m,(double)M,mean_value,variance_value, + (double)xm,(double)ym,(double)zm,(double)cm, + (double)xM,(double)yM,(double)zM,(double)cM, + S,P); + } + + //! Compute statistics vector from the pixel values \inplace. + CImg& stats(const unsigned int variance_method=1) { + return get_stats(variance_method).move_to(*this); + } + + //@} + //------------------------------------- + // + //! \name Vector / Matrix Operations + //@{ + //------------------------------------- + + //! Compute norm of the image, viewed as a matrix. + /** + \param magnitude_type Norm type. Can be: + - \c -1: Linf-norm + - \c 0: L0-norm + - \c 1: L1-norm + - \c 2: L2-norm + **/ + double magnitude(const int magnitude_type=2) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "magnitude(): Empty instance.", + cimg_instance); + const ulongT siz = size(); + double res = 0; + switch (magnitude_type) { + case -1 : { + cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; } + } break; + case 1 : { + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]); + } break; + default : { + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192)) + for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]); + res = (double)std::sqrt(res); + } + } + return res; + } + + //! Compute the trace of the image, viewed as a matrix. + /** + **/ + double trace() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "trace(): Empty instance.", + cimg_instance); + double res = 0; + cimg_forX(*this,k) res+=(double)(*this)(k,k); + return res; + } + + //! Compute the determinant of the image, viewed as a matrix. + /** + **/ + double det() const { + if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "det(): Instance is not a square matrix.", + cimg_instance); + + switch (_width) { + case 1 : return (double)((*this)(0,0)); + case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0)); + case 3 : { + const double + a = (double)_data[0], d = (double)_data[1], g = (double)_data[2], + b = (double)_data[3], e = (double)_data[4], h = (double)_data[5], + c = (double)_data[6], f = (double)_data[7], i = (double)_data[8]; + return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e; + } + default : { + CImg lu(*this,false); + CImg indx; + bool d; + lu._LU(indx,d); + double res = d?(double)1:(double)-1; + cimg_forX(lu,i) res*=lu(i,i); + return res; + } + } + } + + //! Compute the dot product between instance and argument, viewed as matrices. + /** + \param img Image used as a second argument of the dot product. + **/ + template + double dot(const CImg& img) const { + const ulongT nb = std::min(size(),img.size()); + double res = 0; + cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192)) + for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off]; + return res; + } + + //! Get vector-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + CImg res; + if (res._height!=_spectrum) res.assign(1,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + const T *ptrs = data(x,y,z); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get (square) matrix-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \note - The spectrum() of the image must be a square. + **/ + CImg get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { + const int n = (int)cimg::round(std::sqrt((double)_spectrum)); + const T *ptrs = data(x,y,z,0); + const ulongT whd = (ulongT)_width*_height*_depth; + CImg res(n,n); + T *ptrd = res._data; + cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; } + return res; + } + + //! Get tensor-valued pixel located at specified position. + /** + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + CImg get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const { + const T *ptrs = data(x,y,z,0); + const ulongT whd = (ulongT)_width*_height*_depth; + if (_spectrum==6) + return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd)); + if (_spectrum==3) + return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd)); + return tensor(*ptrs); + } + + //! Set vector-valued pixel at specified position. + /** + \param vec Vector to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_vector_at(const CImg& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) { + if (x<_width && y<_height && z<_depth) { + const t *ptrs = vec._data; + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = data(x,y,z); + for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) { + *ptrd = (T)*(ptrs++); ptrd+=whd; + } + } + return *this; + } + + //! Set (square) matrix-valued pixel at specified position. + /** + \param mat Matrix to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_matrix_at(const CImg& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + return set_vector_at(mat,x,y,z); + } + + //! Set tensor-valued pixel at specified position. + /** + \param ten Tensor to put on the instance image. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + **/ + template + CImg& set_tensor_at(const CImg& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { + T *ptrd = data(x,y,z,0); + const ulongT siz = (ulongT)_width*_height*_depth; + if (ten._height==2) { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[3]; + } + else { + *ptrd = (T)ten[0]; ptrd+=siz; + *ptrd = (T)ten[1]; ptrd+=siz; + *ptrd = (T)ten[2]; ptrd+=siz; + *ptrd = (T)ten[4]; ptrd+=siz; + *ptrd = (T)ten[5]; ptrd+=siz; + *ptrd = (T)ten[8]; + } + return *this; + } + + //! Resize image to become a diagonal matrix. + /** + \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient. + **/ + CImg& diagonal() { + return get_diagonal().move_to(*this); + } + + //! Resize image to become a diagonal matrix \newinstance. + CImg get_diagonal() const { + if (is_empty()) return *this; + const unsigned int siz = (unsigned int)size(); + CImg res(siz,siz,1,1,0); + cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off]; + return res; + } + + //! Replace the image by an identity matrix. + /** + \note If the instance image is not square, it is resized to a square matrix using its maximum + dimension as a reference. + **/ + CImg& identity_matrix() { + return identity_matrix(std::max(_width,_height)).move_to(*this); + } + + //! Replace the image by an identity matrix \newinstance. + CImg get_identity_matrix() const { + return identity_matrix(std::max(_width,_height)); + } + + //! Fill image with a linear sequence of values. + /** + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + CImg& sequence(const T& a0, const T& a1) { + if (is_empty()) return *this; + const ulongT siz = size() - 1; + T* ptr = _data; + if (siz) { + const double delta = (double)a1 - (double)a0; + cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); + } else *ptr = a0; + return *this; + } + + //! Fill image with a linear sequence of values \newinstance. + CImg get_sequence(const T& a0, const T& a1) const { + return (+*this).sequence(a0,a1); + } + + //! Transpose the image, viewed as a matrix. + /** + \note Equivalent to \code permute_axes("yxzc"); \endcode. + **/ + CImg& transpose() { + if (_width==1) { _width = _height; _height = 1; return *this; } + if (_height==1) { _height = _width; _width = 1; return *this; } + if (_width==_height) { + cimg_forYZC(*this,y,z,c) for (int x = y; x get_transpose() const { + return get_permute_axes("yxzc"); + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors. + /** + \param img Image used as the second argument of the cross product. + \note The first argument of the cross product is \c *this. + **/ + template + CImg& cross(const CImg& img) { + if (_width!=1 || _height<3 || img._width!=1 || img._height<3) + throw CImgInstanceException(_cimg_instance + "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.", + cimg_instance, + img._width,img._height,img._depth,img._spectrum,img._data); + + const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; + (*this)[0] = (T)(y*img[2] - z*img[1]); + (*this)[1] = (T)(z*img[0] - x*img[2]); + (*this)[2] = (T)(x*img[1] - y*img[0]); + return *this; + } + + //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance. + template + CImg<_cimg_Tt> get_cross(const CImg& img) const { + return CImg<_cimg_Tt>(*this).cross(img); + } + + //! Invert the instance image, viewed as a matrix. + /** + \param use_LU Choose the inverting algorithm. Can be: + - \c true: LU-based matrix inversion. + - \c false: SVD-based matrix inversion. + **/ + CImg& invert(const bool use_LU=true) { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "invert(): Instance is not a square matrix.", + cimg_instance); + const double dete = _width>3?-1.:det(); + if (dete!=0. && _width==2) { + const double + a = _data[0], c = _data[1], + b = _data[2], d = _data[3]; + _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete); + _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete); + } else if (dete!=0. && _width==3) { + const double + a = _data[0], d = _data[1], g = _data[2], + b = _data[3], e = _data[4], h = _data[5], + c = _data[6], f = _data[7], i = _data[8]; + _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete); + _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete); + _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete); + } else { + +#ifdef cimg_use_lapack + int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N]; + Tfloat + *const lapA = new Tfloat[N*N], + *const WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "invert(): LAPACK function dgetri_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0); + delete[] IPIV; delete[] lapA; delete[] WORK; +#else + if (use_LU) { // LU-based + CImg A(*this,false), indx; + bool d; + A._LU(indx,d); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16)) + cimg_forX(*this,j) { + CImg col(1,_width,1,1,0); + col(j) = 1; + col._solve(A,indx); + cimg_forX(*this,i) (*this)(j,i) = (T)col(i); + } + } else pseudoinvert(false); // SVD-based +#endif + } + return *this; + } + + //! Invert the instance image, viewed as a matrix \newinstance. + CImg get_invert(const bool use_LU=true) const { + return CImg(*this,false).invert(use_LU); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix. + /** + **/ + CImg& pseudoinvert(const bool use_LU=false) { + return get_pseudoinvert(use_LU).move_to(*this); + } + + //! Compute the Moore-Penrose pseudo-inverse of the instance image, viewed as a matrix \newinstance. + CImg get_pseudoinvert(const bool use_LU=false) const { + + // LU-based method. + if (use_LU) { + CImg AtA(width(),width()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128)) + cimg_forY(AtA,i) + for (int j = 0; j<=i; ++j) { + double res = 0; + cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k); + AtA(j,i) = AtA(i,j) = (Tfloat)res; + } + AtA.invert(true); + return AtA*get_transpose(); + } + + // SVD-based method. + CImg U, S, V; + SVD(U,S,V,false); + const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max(); + cimg_forX(V,x) { + const Tfloat s = S(x), invs = s>epsilon?1/s:0; + cimg_forY(V,y) V(x,y)*=invs; + } + return V*U.transpose(); + } + + //! Solve a system of linear equations. + /** + \param A Matrix of the linear system. + \param use_LU In case of non square system (least-square solution), + choose between SVD-based (\c false) or LU-based (\c true) method. + LU method is faster for large matrices, but numerically less stable. + \note Solve \c AX = B where \c B=*this. + **/ + template + CImg& solve(const CImg& A, const bool use_LU=false) { + if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + + if (A.size()==1) return (*this)/=A[0]; + if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system + const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3], + fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d), + det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd); + if (fM==fa) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det; + (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y; + } else if (fM==fc) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det; + (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y; + } else if (fM==fb) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det; + (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256)) + cimg_forX(*this,k) { + const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det; + (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d); + } + return *this; + } + + if (A._width==A._height) { // Square linear system +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N]; + Ttfloat + *const lapA = new Ttfloat[N*N], + *const lapB = new Ttfloat[N], + *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l)); + cimg_forX(*this,i) { + cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j)); + cimg::getrf(N,lapA,IPIV,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrf_() returned error code %d.", + cimg_instance, + INFO); + else { + cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); + if (INFO) + cimg::warn(_cimg_instance + "solve(): LAPACK library function dgetrs_() returned error code %d.", + cimg_instance, + INFO); + } + if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0; + } + delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; +#else + CImg lu(A,false); + CImg indx; + bool d; + lu._LU(indx,d); + CImg res(_width,A._width); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16)) + cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx)); + res.move_to(*this); +#endif + } else { // Least-square solution for non-square systems + +#ifdef cimg_use_lapack + char TRANS = 'N'; + int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width; + Ttfloat WORK_QUERY; + Ttfloat + * const lapA = new Ttfloat[M*N], + * const lapB = new Ttfloat[M*NRHS]; + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO); + LWORK = (int) WORK_QUERY; + Ttfloat *const WORK = new Ttfloat[LWORK]; + cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l)); + cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l)); + cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO); + if (INFO != 0) + cimg::warn(_cimg_instance + "solve(): LAPACK library function sgels() returned error code %d.", + cimg_instance, + INFO); + assign(NRHS, N); + if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l]; + else (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); + delete[] lapA; delete[] lapB; delete[] WORK; +#else + (A.get_pseudoinvert(use_LU)*(*this)).move_to(*this); +#endif + } + return *this; + } + + //! Solve a system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve(const CImg& A, const bool use_LU=false) const { + typedef _cimg_Ttfloat Ttfloat; + return CImg(*this,false).solve(A,use_LU); + } + + template + CImg& _solve(const CImg& A, const CImg& indx) { + typedef _cimg_Ttfloat Ttfloat; + const int N = height(); + int ii = -1; + Ttfloat sum; + for (int i = 0; i=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j); + else if (sum!=0) ii = i; + (*this)(i) = (T)sum; + } + for (int i = N - 1; i>=0; --i) { + sum = (*this)(i); + for (int j = i + 1; j + CImg& solve_tridiagonal(const CImg& A) { + const unsigned int siz = (unsigned int)size(); + if (A._width!=3 || A._height!=siz) + throw CImgArgumentException(_cimg_instance + "solve_tridiagonal(): Instance and tridiagonal matrix " + "(%u,%u,%u,%u,%p) have incompatible dimensions.", + cimg_instance, + A._width,A._height,A._depth,A._spectrum,A._data); + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = 1e-4f; + CImg B = A.get_column(1), V(*this,false); + for (int i = 1; i<(int)siz; ++i) { + const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon); + B[i] -= m*A(2,i - 1); + V[i] -= m*V[i - 1]; + } + (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon)); + for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i + 1])/(B[i]?B[i]:epsilon)); + return *this; + } + + //! Solve a tridiagonal system of linear equations \newinstance. + template + CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg& A) const { + return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A); + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. + **/ + template + const CImg& eigen(CImg& val, CImg &vec) const { + if (is_empty()) { val.assign(); vec.assign(); } + else { + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + + if (val.size()<(ulongT)_width) val.assign(1,_width); + if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width); + switch (_width) { + case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break; + case 2 : { + const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d; + double f = e*e - 4*(a*d - b*c); + if (f<0) cimg::warn(_cimg_instance + "eigen(): Complex eigenvalues found.", + cimg_instance); + f = std::sqrt(f); + const double + l1 = 0.5*(e - f), + l2 = 0.5*(e + f), + b2 = b*b, + norm1 = std::sqrt(cimg::sqr(l2 - a) + b2), + norm2 = std::sqrt(cimg::sqr(l1 - a) + b2); + val[0] = (t)l2; + val[1] = (t)l1; + if (norm1>0) { vec(0,0) = (t)(b/norm1); vec(0,1) = (t)((l2 - a)/norm1); } else { vec(0,0) = 1; vec(0,1) = 0; } + if (norm2>0) { vec(1,0) = (t)(b/norm2); vec(1,1) = (t)((l1 - a)/norm2); } else { vec(1,0) = 1; vec(1,1) = 0; } + } break; + default : + throw CImgInstanceException(_cimg_instance + "eigen(): Eigenvalues computation of general matrices is limited " + "to 2x2 matrices.", + cimg_instance); + } + } + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix. + /** + \return A list of two images [val; vec], whose meaning is similar as in eigen(CImg&,CImg&) const. + **/ + CImgList get_eigen() const { + CImgList res(2); + eigen(res[0],res[1]); + return res; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \param[out] val Vector of the estimated eigenvalues, in decreasing order. + \param[out] vec Matrix of the estimated eigenvectors, sorted by columns. + **/ + template + const CImg& symmetric_eigen(CImg& val, CImg& vec) const { + if (is_empty()) { val.assign(); vec.assign(); return *this; } + if (_width!=_height || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "eigen(): Instance is not a square matrix.", + cimg_instance); + val.assign(1,_width); + vec.assign(_width,_width); + + if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; } + if (_width==2) { + const double + a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], + e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)), + l1 = 0.5*(e - f), l2 = 0.5*(e + f), + n = std::sqrt(cimg::sqr(l2 - a) + b*b); + val[0] = (t)l2; + val[1] = (t)l1; + if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; } + vec[1] = -vec[2]; + vec[3] = vec[0]; + return *this; + } + +#ifdef cimg_use_lapack + char JOB = 'V', UPLO = 'U'; + int N = _width, LWORK = 4*N, INFO; + Tfloat + *const lapA = new Tfloat[N*N], + *const lapW = new Tfloat[N], + *const WORK = new Tfloat[LWORK]; + cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l)); + cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); + if (INFO) + cimg::warn(_cimg_instance + "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.", + cimg_instance, + INFO); + if (!INFO) { + cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i]; + cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]); + } else { val.fill(0); vec.fill(0); } + delete[] lapA; delete[] lapW; delete[] WORK; + +#else + CImg V(_width,_width); + Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M)); + (CImg(*this,false)/=maxabs).SVD(vec,val,V,false); + if (maxabs!=1) val*=maxabs; + + bool is_ambiguous = false; + float eig = 0; + cimg_forY(val,p) { // Check for ambiguous cases + if (val[p]>eig) eig = (float)val[p]; + t scal = 0; + cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); + if (cimg::abs(scal)<0.9f) is_ambiguous = true; + if (scal<0) val[p] = -val[p]; + } + if (is_ambiguous) { + ++(eig*=2); + SVD(vec,val,V,false,40,eig); + val-=eig; + } + + CImg permutations; // Sort eigenvalues in decreasing order + CImg tmp(_width); + val.sort(permutations,false); + cimg_forY(vec,k) { + cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k); + std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width); + } +#endif + return *this; + } + + //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix. + /** + \return A list of two images [val; vec], whose meaning are similar as in + symmetric_eigen(CImg&,CImg&) const. + **/ + CImgList get_symmetric_eigen() const { + CImgList res(2); + symmetric_eigen(res[0],res[1]); + return res; + } + + //! Sort pixel values and get sorting permutations. + /** + \param[out] permutations Permutation map used for the sorting. + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + **/ + template + CImg& sort(CImg& permutations, const bool is_increasing=true) { + permutations.assign(_width,_height,_depth,_spectrum); + if (is_empty()) return *this; + cimg_foroff(permutations,off) permutations[off] = (t)off; + return _quicksort(0,size() - 1,permutations,is_increasing,true); + } + + //! Sort pixel values and get sorting permutations \newinstance. + template + CImg get_sort(CImg& permutations, const bool is_increasing=true) const { + return (+*this).sort(permutations,is_increasing); + } + + //! Sort pixel values. + /** + \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way. + \param axis Tells if the value sorting must be done along a specific axis. Can be: + - \c 0: All pixel values are sorted, independently on their initial position. + - \c 'x': Image columns are sorted, according to the first value in each column. + - \c 'y': Image rows are sorted, according to the first value in each row. + - \c 'z': Image slices are sorted, according to the first value in each slice. + - \c 'c': Image channels are sorted, according to the first value in each channel. + **/ + CImg& sort(const bool is_increasing=true, const char axis=0) { + if (is_empty()) return *this; + CImg perm; + switch (cimg::lowercase(axis)) { + case 0 : + _quicksort(0,size() - 1,perm,is_increasing,false); + break; + case 'x' : { + perm.assign(_width); + get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c); + } break; + case 'y' : { + perm.assign(_height); + get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c); + } break; + case 'z' : { + perm.assign(_depth); + get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c); + } break; + case 'c' : { + perm.assign(_spectrum); + get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing); + CImg img(*this,false); + cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]); + } break; + default : + throw CImgArgumentException(_cimg_instance + "sort(): Invalid specified axis '%c' " + "(should be { x | y | z | c }).", + cimg_instance,axis); + } + return *this; + } + + //! Sort pixel values \newinstance. + CImg get_sort(const bool is_increasing=true, const char axis=0) const { + return (+*this).sort(is_increasing,axis); + } + + template + CImg& _quicksort(const long indm, const long indM, CImg& permutations, + const bool is_increasing, const bool is_permutations) { + if (indm(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]>(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]>(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } else { + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + if ((*this)[mid]<(*this)[indM]) { + cimg::swap((*this)[indM],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indM],permutations[mid]); + } + if ((*this)[indm]<(*this)[mid]) { + cimg::swap((*this)[indm],(*this)[mid]); + if (is_permutations) cimg::swap(permutations[indm],permutations[mid]); + } + } + if (indM - indm>=3) { + const T pivot = (*this)[mid]; + long i = indm, j = indM; + if (is_increasing) { + do { + while ((*this)[i]pivot) --j; + if (i<=j) { + if (is_permutations) cimg::swap(permutations[i],permutations[j]); + cimg::swap((*this)[i++],(*this)[j--]); + } + } while (i<=j); + } else { + do { + while ((*this)[i]>pivot) ++i; + while ((*this)[j] A; // Input matrix (assumed to contain some values) + CImg<> U,S,V; + A.SVD(U,S,V) + \endcode + **/ + template + const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + typedef _cimg_Ttfloat Ttfloat; + const Ttfloat epsilon = (Ttfloat)1e-25; + + if (is_empty()) { U.assign(); S.assign(); V.assign(); } + else if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "SVD(): Instance has invalid dimensions (depth or channels different from 1).", + cimg_instance); + else { + U = *this; + if (lambda!=0) { + const unsigned int delta = std::min(U._width,U._height); + for (unsigned int i = 0; i rv1(_width); + Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0; + int l = 0; + + cimg_forX(U,i) { + l = i + 1; + rv1[i] = scale*g; + g = s = scale = 0; + if (i=0?-1:1)*std::sqrt(s)); + h = f*g - s; + U(i,i) = f - g; + for (int j = l; j=0?-1:1)*std::sqrt(s)); + h = f*g - s; + U(l,i) = f - g; + for (int k = l; k=0; --i) { + if (i=0; --i) { + l = i + 1; + g = S[i]; + for (int j = l; j=0; --k) { + int nm = 0; + for (unsigned int its = 0; its=1; --l) { + nm = l - 1; + if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; } + if ((cimg::abs(S[nm]) + anorm)==anorm) break; + } + if (flag) { + c = 0; + s = 1; + for (int i = l; i<=k; ++i) { + f = s*rv1[i]; + rv1[i] = c*rv1[i]; + if ((cimg::abs(f) + anorm)==anorm) break; + g = S[i]; + h = cimg::_hypot(f,g); + S[i] = h; + h = 1/h; + c = g*h; + s = -f*h; + cimg_forY(U,j) { + const t y = U(nm,j), z = U(i,j); + U(nm,j) = y*c + z*s; + U(i,j) = z*c - y*s; + } + } + } + + const t z = S[k]; + if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } + nm = k - 1; + t x = S[l], y = S[nm]; + g = rv1[nm]; + h = rv1[k]; + f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y); + g = cimg::_hypot(f,(Ttfloat)1); + f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x); + c = s = 1; + for (int j = l; j<=nm; ++j) { + const int i = j + 1; + g = rv1[i]; + h = s*g; + g = c*g; + t y1 = S[i], z1 = cimg::_hypot(f,h); + rv1[j] = z1; + c = f/std::max(epsilon,(Ttfloat)z1); + s = h/std::max(epsilon,(Ttfloat)z1); + f = x*c + g*s; + g = g*c - x*s; + h = y1*s; + y1*=c; + cimg_forX(U,jj) { + const t x2 = V(j,jj), z2 = V(i,jj); + V(j,jj) = x2*c + z2*s; + V(i,jj) = z2*c - x2*s; + } + z1 = cimg::_hypot(f,h); + S[j] = z1; + if (z1) { + z1 = 1/std::max(epsilon,(Ttfloat)z1); + c = f*z1; + s = h*z1; + } + f = c*g + s*y1; + x = c*y1 - s*g; + cimg_forY(U,jj) { + const t y2 = U(j,jj), z2 = U(i,jj); + U(j,jj) = y2*c + z2*s; + U(i,jj) = z2*c - y2*s; + } + } + rv1[l] = 0; + rv1[k] = f; + S[k] = x; + } + } + + if (sorting) { + CImg permutations; + CImg tmp(_width); + S.sort(permutations,false); + cimg_forY(U,k) { + cimg_forY(permutations,y) tmp(y) = U(permutations(y),k); + std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width); + } + cimg_forY(V,k) { + cimg_forY(permutations,y) tmp(y) = V(permutations(y),k); + std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width); + } + } + } + return *this; + } + + //! Compute the SVD of the instance image, viewed as a general matrix. + /** + \return A list of three images [U; S; V], whose meaning is similar as in + SVD(CImg&,CImg&,CImg&,bool,unsigned int,float) const. + **/ + CImgList get_SVD(const bool sorting=true, + const unsigned int max_iteration=40, const float lambda=0) const { + CImgList res(3); + SVD(res[0],res[1],res[2],sorting,max_iteration,lambda); + return res; + } + + // [internal] Compute the LU decomposition of a permuted matrix. + template + CImg& _LU(CImg& indx, bool& d) { + const int N = width(); + int imax = 0; + CImg vv(N); + indx.assign(N); + d = true; + + bool return0 = false; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512)) + cimg_forX(*this,i) { + Tfloat vmax = 0; + cimg_forX(*this,j) { + const Tfloat tmp = cimg::abs((*this)(j,i)); + if (tmp>vmax) vmax = tmp; + } + if (vmax==0) return0 = true; else vv[i] = 1/vmax; + } + if (return0) { indx.fill(0); return fill(0); } + + cimg_forX(*this,j) { + for (int i = 0; i=vmax) { vmax = tmp; imax = i; } + } + if (j!=imax) { + cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); + d = !d; + vv[imax] = vv[j]; + } + indx[j] = (t)imax; + if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; + if (j=3 = orthogonal matching pursuit where an orthogonal projection step is performed + every 'method-2' iterations. + \param max_iter Sets the max number of iterations processed for each signal. + If set to '0' (default), 'max_iter' is set to the number of dictionary columns. + (only meaningful for matching pursuit and its variants). + \param max_residual Gives a stopping criterion on signal reconstruction accuracy. + (only meaningful for matching pursuit and its variants). + \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column. + Thus, the matrix product D*W is an approximation of the input matrix. + **/ + template + CImg& project_matrix(const CImg& dictionary, const unsigned int method=0, + const unsigned int max_iter=0, const double max_residual=1e-6) { + return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this); + } + + template + CImg get_project_matrix(const CImg& dictionary, const unsigned int method=0, + const unsigned int max_iter=0, const double max_residual=1e-6) const { + if (_depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "project_matrix(): Instance image is not a matrix.", + cimg_instance); + if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.", + cimg_instance, + dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum); + + if (!method) return get_solve(dictionary,true); + CImg W(_width,dictionary._width,1,1,0); + + // Compute dictionary norm and normalize it. + CImg D(dictionary,false), Dnorm(D._width); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32)) + cimg_forX(Dnorm,d) { + Tfloat norm = 0; + cimg_forY(D,y) norm+=cimg::sqr(D(d,y)); + Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm)); + } + cimg_forXY(D,d,y) D(d,y)/=Dnorm[d]; + + // Matching pursuit. + const unsigned int proj_step = method<3?1:method - 2; + bool is_orthoproj = false; + + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32)) + cimg_forX(*this,x) { + CImg S = get_column(x); + const CImg S0 = method<2?CImg():S; + Tfloat residual = S.magnitude()/S._height; + const unsigned int nmax = max_iter?max_iter:D._width; + + for (unsigned int n = 0; nmax_residual; ++n) { + + // Find best matching column in D. + int dmax = 0; + Tfloat absdotmax = 0, dotmax = 0; + cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32)) + cimg_forX(D,d) { + Tfloat _dot = 0; + cimg_forY(D,y) _dot+=S[y]*D(d,y); + Tfloat absdot = cimg::abs(_dot); + cimg_pragma_openmp(critical(get_project_matrix)) { + if (absdot>absdotmax) { + absdotmax = absdot; + dotmax = _dot; + dmax = d; + } + } + } + + if (!n || method<3 || n%proj_step) { + // Matching Pursuit: Subtract component to signal. + W(x,dmax)+=dotmax; + residual = 0; + cimg_forY(S,y) { + S[y]-=dotmax*D(dmax,y); + residual+=cimg::sqr(S[y]); + } + residual = std::sqrt(residual)/S._height; + is_orthoproj = false; + + } else { + // Orthogonal Matching Pursuit: Orthogonal projection step. + W(x,dmax) = 1; // Used as a marker only. + unsigned int nbW = 0; + cimg_forY(W,d) if (W(x,d)) ++nbW; + CImg sD(nbW,D._height); + CImg inds(nbW); + int sd = 0; + cimg_forY(W,d) if (W(x,d)) { + cimg_forY(sD,y) sD(sd,y) = D(d,y); + inds[sd++] = d; + } + S0.get_solve(sD,true).move_to(sD); // sD is now a one-column vector of weights + + // Recompute residual signal. + S = S0; + cimg_forY(sD,k) { + const Tfloat weight = sD[k]; + const unsigned int ind = inds[k]; + W(x,ind) = weight; + cimg_forY(S,y) S[y]-=weight*D(ind,y); + } + residual = S.magnitude()/S._height; + is_orthoproj = true; + } + } + + // Perform last orthoprojection step if needed. + if (method>=2 && !is_orthoproj) { + unsigned int nbW = 0; + cimg_forY(W,d) if (W(x,d)) ++nbW; + if (nbW) { // Avoid degenerated case where 0 coefs are used + CImg sD(nbW,D._height); + CImg inds(nbW); + int sd = 0; + cimg_forY(W,d) if (W(x,d)) { + cimg_forY(sD,y) sD(sd,y) = D(d,y); + inds[sd++] = d; + } + S0.get_solve(sD,true).move_to(sD); + cimg_forY(sD,k) W(x,inds[k]) = sD[k]; + } + } + } + + // Normalize resulting coefficients according to initial (non-normalized) dictionary. + cimg_forXY(W,x,y) W(x,y)/=Dnorm[y]; + return W; + } + + //! Compute minimal path in a graph, using the Dijkstra algorithm. + /** + \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance + between two nodes (i,j). + \param nb_nodes Number of graph nodes. + \param starting_node Index of the starting node. + \param ending_node Index of the ending node (set to ~0U to ignore ending node). + \param previous_node Array that gives the previous node index in the path to the starting node + (optional parameter). + \return Array of distances of each node to the starting node. + **/ + template + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + if (starting_node>=nb_nodes) + throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher " + "than number of nodes %u.", + pixel_type(),starting_node,nb_nodes); + CImg dist(1,nb_nodes,1,1,cimg::type::max()); + dist(starting_node) = 0; + previous_node.assign(1,nb_nodes,1,1,(t)-1); + previous_node(starting_node) = (t)starting_node; + CImg Q(nb_nodes); + cimg_forX(Q,u) Q(u) = (unsigned int)u; + cimg::swap(Q(starting_node),Q(0)); + unsigned int sizeQ = nb_nodes; + while (sizeQ) { + // Update neighbors from minimal vertex + const unsigned int umin = Q(0); + if (umin==ending_node) sizeQ = 0; + else { + const T dmin = dist(umin); + const T infty = cimg::type::max(); + for (unsigned int q = 1; qdist(Q(left))) || + (rightdist(Q(right)));) { + if (right + static CImg dijkstra(const tf& distance, const unsigned int nb_nodes, + const unsigned int starting_node, const unsigned int ending_node=~0U) { + CImg foo; + return dijkstra(distance,nb_nodes,starting_node,ending_node,foo); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + /** + \param starting_node Index of the starting node. + \param ending_node Index of the ending node. + \param previous_node Array that gives the previous node index in the path to the starting node + (optional parameter). + \return Array of distances of each node to the starting node. + \note image instance corresponds to the adjacency matrix of the graph. + **/ + template + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) { + return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + template + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, + CImg& previous_node) const { + if (_width!=_height || _depth!=1 || _spectrum!=1) + throw CImgInstanceException(_cimg_instance + "dijkstra(): Instance is not a graph adjacency matrix.", + cimg_instance); + + return dijkstra(*this,_width,starting_node,ending_node,previous_node); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm. + CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { + return get_dijkstra(starting_node,ending_node).move_to(*this); + } + + //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance. + CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { + CImg foo; + return get_dijkstra(starting_node,ending_node,foo); + } + + //! Return an image containing the character codes of specified string. + /** + \param str input C-string to encode as an image. + \param is_last_zero Tells if the ending \c '0' character appear in the resulting image. + \param is_shared Return result that shares its buffer with \p str. + **/ + static CImg string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) { + if (!str) return CImg(); + return CImg(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared); + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg row_vector(const T& a0) { + return vector(a0); + } + + //! Return a \c 2x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg row_vector(const T& a0, const T& a1) { + CImg r(2,1); + r[0] = a0; r[1] = a1; + return r; + } + + //! Return a \c 3x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg row_vector(const T& a0, const T& a1, const T& a2) { + CImg r(3,1); + r[0] = a0; r[1] = a1; r[2] = a2; + return r; + } + + //! Return a \c 4x1 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3) { + CImg r(4,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; + return r; + } + + //! Return a \c 5x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + CImg r(5,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; + return r; + } + + //! Return a \c 6x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + CImg r(6,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; + return r; + } + + //! Return a \c 7x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + CImg r(7,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; + return r; + } + + //! Return a \c 8x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + CImg r(8,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; + return r; + } + + //! Return a \c 9x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + CImg r(9,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; + return r; + } + + //! Return a \c 10x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + CImg r(10,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + return r; + } + + //! Return a \c 11x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + CImg r(11,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; + return r; + } + + //! Return a \c 12x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + CImg r(12,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; + return r; + } + + //! Return a \c 13x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + CImg r(13,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; + return r; + } + + //! Return a \c 14x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + CImg r(14,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; + return r; + } + + //! Return a \c 15x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + CImg r(15,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; + return r; + } + + //! Return a \c 16x1 image containing specified values. + static CImg row_vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(16,1); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; + return r; + } + + //! Return a \c 1x1 image containing specified value. + /** + \param a0 First vector value. + **/ + static CImg vector(const T& a0) { + CImg r(1,1); + r[0] = a0; + return r; + } + + //! Return a \c 1x2 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + **/ + static CImg vector(const T& a0, const T& a1) { + CImg r(1,2); + r[0] = a0; r[1] = a1; + return r; + } + + //! Return a \c 1x3 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2) { + CImg r(1,3); + r[0] = a0; r[1] = a1; r[2] = a2; + return r; + } + + //! Return a \c 1x4 image containing specified values. + /** + \param a0 First vector value. + \param a1 Second vector value. + \param a2 Third vector value. + \param a3 Fourth vector value. + **/ + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { + CImg r(1,4); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; + return r; + } + + //! Return a \c 1x5 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + CImg r(1,5); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; + return r; + } + + //! Return a \c 1x6 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + CImg r(1,6); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; + return r; + } + + //! Return a \c 1x7 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6) { + CImg r(1,7); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; + return r; + } + + //! Return a \c 1x8 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7) { + CImg r(1,8); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; + return r; + } + + //! Return a \c 1x9 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8) { + CImg r(1,9); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; + return r; + } + + //! Return a \c 1x10 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9) { + CImg r(1,10); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + return r; + } + + //! Return a \c 1x11 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10) { + CImg r(1,11); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; + return r; + } + + //! Return a \c 1x12 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11) { + CImg r(1,12); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; + return r; + } + + //! Return a \c 1x13 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12) { + CImg r(1,13); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; + return r; + } + + //! Return a \c 1x14 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13) { + CImg r(1,14); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; + return r; + } + + //! Return a \c 1x15 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14) { + CImg r(1,15); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; + return r; + } + + //! Return a \c 1x16 image containing specified values. + static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(1,16); + r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9; + r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; + return r; + } + + //! Return a 1x1 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg matrix(const T& a0) { + return vector(a0); + } + + //! Return a 2x2 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, + const T& a2, const T& a3) { + CImg r(2,2); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; + *(ptr++) = a2; *(ptr++) = a3; + return r; + } + + //! Return a 3x3 matrix containing specified coefficients. + /** + \param a0 First matrix value. + \param a1 Second matrix value. + \param a2 Third matrix value. + \param a3 Fourth matrix value. + \param a4 Fifth matrix value. + \param a5 Sixth matrix value. + \param a6 Seventh matrix value. + \param a7 Eighth matrix value. + \param a8 Ninth matrix value. + **/ + static CImg matrix(const T& a0, const T& a1, const T& a2, + const T& a3, const T& a4, const T& a5, + const T& a6, const T& a7, const T& a8) { + CImg r(3,3); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; + *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; + *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; + return r; + } + + //! Return a 4x4 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, + const T& a4, const T& a5, const T& a6, const T& a7, + const T& a8, const T& a9, const T& a10, const T& a11, + const T& a12, const T& a13, const T& a14, const T& a15) { + CImg r(4,4); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; + *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; + *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; + *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; + return r; + } + + //! Return a 5x5 matrix containing specified coefficients. + static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, + const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, + const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, + const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, + const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { + CImg r(5,5); T *ptr = r._data; + *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; + *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; + *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; + *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; + *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; + return r; + } + + //! Return a 1x1 symmetric matrix containing specified coefficients. + /** + \param a0 First matrix value. + \note Equivalent to vector(const T&). + **/ + static CImg tensor(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 symmetric matrix tensor containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2) { + return matrix(a0,a1,a1,a2); + } + + //! Return a 3x3 symmetric matrix containing specified coefficients. + static CImg tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { + return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5); + } + + //! Return a 1x1 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0) { + return matrix(a0); + } + + //! Return a 2x2 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1) { + return matrix(a0,0,0,a1); + } + + //! Return a 3x3 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2) { + return matrix(a0,0,0,0,a1,0,0,0,a2); + } + + //! Return a 4x4 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { + return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); + } + + //! Return a 5x5 diagonal matrix containing specified coefficients. + static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { + return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); + } + + //! Return a NxN identity matrix. + /** + \param N Dimension of the matrix. + **/ + static CImg identity_matrix(const unsigned int N) { + CImg res(N,N,1,1,0); + cimg_forX(res,x) res(x,x) = 1; + return res; + } + + //! Return a N-numbered sequence vector from \p a0 to \p a1. + /** + \param N Size of the resulting vector. + \param a0 Starting value of the sequence. + \param a1 Ending value of the sequence. + **/ + static CImg sequence(const unsigned int N, const T& a0, const T& a1) { + if (N) return CImg(1,N).sequence(a0,a1); + return CImg(); + } + + //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or third quaternion coordinate. + \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). + **/ + static CImg rotation_matrix(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) { + double X, Y, Z, W, N; + if (is_quaternion) { + N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w); + if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; } + else { X = Y = Z = 0; W = 1; } + return CImg::matrix((T)(X*X + Y*Y - Z*Z - W*W),(T)(2*Y*Z - 2*X*W),(T)(2*X*Z + 2*Y*W), + (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y), + (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W)); + } + N = cimg::hypot((double)x,(double)y,(double)z); + if (N>0) { X = x/N; Y = y/N; Z = z/N; } + else { X = Y = 0; Z = 1; } + const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang); + return CImg::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s), + (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s), + (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c)); + } + + //@} + //----------------------------------- + // + //! \name Value Manipulation + //@{ + //----------------------------------- + + //! Fill all pixel values with specified value. + /** + \param val Fill value. + **/ + CImg& fill(const T& val) { + if (is_empty()) return *this; + if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val; + else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*) + return *this; + } + + //! Fill all pixel values with specified value \newinstance. + CImg get_fill(const T& val) const { + return CImg(_width,_height,_depth,_spectrum).fill(val); + } + + //! Fill sequentially all pixel values with specified values. + /** + \param val0 First fill value. + \param val1 Second fill value. + **/ + CImg& fill(const T& val0, const T& val1) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 1; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 2; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 3; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 4; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 5; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 6; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 7; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 8; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 9; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 10; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 11; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 12; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 13; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 14; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14); + } + + //! Fill sequentially all pixel values with specified values \overloading. + CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14, const T& val15) { + if (is_empty()) return *this; + T *ptrd, *ptre = end() - 15; + for (ptrd = _data; ptrd get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, + const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, + const T& val12, const T& val13, const T& val14, const T& val15) const { + return CImg(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10, + val11,val12,val13,val14,val15); + } + + //! Fill sequentially pixel values according to a given expression. + /** + \param expression C-string describing a math formula, or a sequence of values. + \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling. + \param allow_formula Tells that mathematical formulas are authorized for the filling. + \param list_inputs In case of a mathematical expression, attach a list of images to the specified expression. + \param[out] list_outputs In case of a math expression, list of images atatched to the specified expression. + **/ + CImg& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) { + return _fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs,"fill",0); + } + + // 'formula_mode' = { 0 = does not allow formula | 1 = allow formula | + // 2 = allow formula and do not fill image values }. + CImg& _fill(const char *const expression, const bool repeat_values, const unsigned int formula_mode, + const CImgList *const list_inputs, CImgList *const list_outputs, + const char *const calling_function, const CImg *provides_copy) { + if (is_empty() || !expression || !*expression) return *this; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + CImg is_error; + bool is_value_sequence = false; + cimg_abort_init; + + if (formula_mode) { + + // Try to pre-detect regular value sequence to avoid exception thrown by _cimg_math_parser. + double value; + char sep; + const int err = cimg_sscanf(expression,"%lf %c",&value,&sep); + if (err==1 || (err==2 && sep==',')) { + if (err==1) { if (formula_mode==2) return *this; return fill((T)value); } + else is_value_sequence = true; + } + + // Try to fill values according to a formula. + _cimg_abort_init_openmp; + if (!is_value_sequence) try { + CImg base = provides_copy?provides_copy->get_shared():get_shared(); + _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || + *expression=='*' || *expression==':'), + calling_function,base,this,list_inputs,list_outputs,true); + if (!provides_copy && expression && *expression!='>' && *expression!='<' && *expression!=':' && + mp.need_input_copy) + base.assign().assign(*this,false); // Needs input copy + + // Determine 2nd largest image dimension (used as axis for inner loop in parallelized evaluation). + unsigned int M; + if (mp.result_dim) { + M = cimg::max(_width,_height,_depth); + M = M==_width?std::max(_height,_depth):M==_height?std::max(_width,_depth):std::max(_width,_height); + } else { + M = cimg::max(_width,_height,_depth,_spectrum); + M = M==_width?cimg::max(_height,_depth,_spectrum): + M==_height?cimg::max(_width,_depth,_spectrum): + M==_depth?cimg::max(_width,_height,_spectrum):cimg::max(_width,_height,_depth); + } + + bool do_in_parallel = false; +#if cimg_use_openmp!=0 + cimg_openmp_if(*expression=='*' || *expression==':' || + (mp.is_parallelizable && M>=(cimg_openmp_sizefactor)*320 && size()/M>=2)) + do_in_parallel = true; +#endif + if (mp.result_dim) { // Vector-valued expression + const unsigned int N = std::min(mp.result_dim,_spectrum); + const ulongT whd = (ulongT)_width*_height*_depth; + T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data; + if (*expression=='<') { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_rofYZ(*this,y,z) { + cimg_abort_test; + if (formula_mode==2) cimg_rofX(*this,x) mp(x,y,z,0); + else cimg_rofX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); + + } else if (*expression=='>' || !do_in_parallel) { + CImg res(1,mp.result_dim); + mp.begin_t(); + cimg_forYZ(*this,y,z) { + cimg_abort_test; + if (formula_mode==2) cimg_forX(*this,x) mp(x,y,z,0); + else cimg_forX(*this,x) { + mp(x,y,z,0,res._data); + const double *ptrs = res._data; + T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } + } + } + mp.end_t(); + + } else { + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + lmp.is_fill = true; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + +#define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \ + cimg_pragma_openmp(for cimg_openmp_collapse(2)) \ + cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,0); \ + else { \ + CImg res(1,lmp.result_dim); \ + T *__ptrd = data(_sx,_sy,_sz,0); \ + const ulongT off = (ulongT)_off; \ + cimg_for##_X(*this,_x) { \ + lmp(x,y,z,0,res._data); \ + const double *ptrs = res._data; \ + T *_ptrd = __ptrd; \ + for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \ + __ptrd+=off; \ + } \ + } \ + } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp + + if (M==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) } + else if (M==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) } + else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) } + + lmp.end_t(); + cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); } + if (&lmp!=&mp) delete &lmp; + } +#endif + } + + } else { // Scalar-valued expression + T *ptrd = *expression=='<'?end() - 1:_data; + if (*expression=='<') { + mp.begin_t(); + if (formula_mode==2) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); } + else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); } + mp.end_t(); + + } else if (*expression=='>' || !do_in_parallel) { + mp.begin_t(); + if (formula_mode==2) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); } + else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); } + mp.end_t(); + + } else { + +#if cimg_use_openmp!=0 + unsigned int tid = 0; + cimg_pragma_openmp(parallel) + { + _cimg_math_parser *_mp = 0; + cimg_pragma_openmp(critical(_fill)) { _mp = !tid?&mp:new _cimg_math_parser(mp); ++tid; } + _cimg_math_parser &lmp = *_mp; + lmp.is_fill = true; + cimg_pragma_openmp(barrier) + lmp.begin_t(); + +#define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \ + cimg_pragma_openmp(for cimg_openmp_collapse(3)) \ + cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + if (formula_mode==2) cimg_for##_X(*this,_x) lmp(x,y,z,c); \ + else { \ + T *_ptrd = data(_sx,_sy,_sz,_sc); \ + const ulongT off = (ulongT)_off; \ + cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \ + } \ + } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp + + if (M==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) } + else if (M==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) } + else if (M==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) } + else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) } + + lmp.end_t(); + cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); } + if (&lmp!=&mp) delete &lmp; + } +#endif + } + } + mp.end(); + } catch (CImgException& e) { CImg::string(e._message).move_to(is_error); } + } + + // Try to fill values according to a value sequence. + if (!formula_mode || is_value_sequence || is_error) { + CImg item(256); + char sep = 0; + const char *nexpression = expression; + ulongT nb = 0; + const ulongT siz = size(); + T *ptrd = _data; + for (double val = 0; *nexpression && nb0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) { + nexpression+=std::strlen(item) + (err>1); + *(ptrd++) = (T)val; + } else break; + } + cimg::exception_mode(omode); + if (nb get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true, + const CImgList *const list_inputs=0, CImgList *const list_outputs=0) const { + return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_inputs,list_outputs); + } + + //! Fill sequentially pixel values according to the values found in another image. + /** + \param values Image containing the values used for the filling. + \param repeat_values In case there are less values than necessary in \c values, tells if these values must be + repeated for the filling. + **/ + template + CImg& fill(const CImg& values, const bool repeat_values=true) { + if (is_empty() || !values) return *this; + T *ptrd = _data, *ptre = ptrd + size(); + for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs + CImg get_fill(const CImg& values, const bool repeat_values=true) const { + return repeat_values?CImg(_width,_height,_depth,_spectrum).fill(values,repeat_values): + (+*this).fill(values,repeat_values); + } + + //! Fill pixel values along the X-axis at a specified pixel position. + /** + \param y Y-coordinate of the filled column. + \param z Z-coordinate of the filled column. + \param c C-coordinate of the filled column. + \param a0 First fill value. + **/ + CImg& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) { +#define _cimg_fill1(x,y,z,c,off,siz,t) { \ + va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \ + for (unsigned int k = 1; k& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) { + if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position. + /** + \param x X-coordinate of the filled row. + \param z Z-coordinate of the filled row. + \param c C-coordinate of the filled row. + \param a0 First fill value. + **/ + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int); + return *this; + } + + //! Fill pixel values along the Y-axis at a specified pixel position \overloading. + CImg& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) { + if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position. + /** + \param x X-coordinate of the filled slice. + \param y Y-coordinate of the filled slice. + \param c C-coordinate of the filled slice. + \param a0 First fill value. + **/ + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) { + const ulongT wh = (ulongT)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int); + return *this; + } + + //! Fill pixel values along the Z-axis at a specified pixel position \overloading. + CImg& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) { + const ulongT wh = (ulongT)_width*_height; + if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position. + /** + \param x X-coordinate of the filled channel. + \param y Y-coordinate of the filled channel. + \param z Z-coordinate of the filled channel. + \param a0 First filling value. + **/ + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { + const ulongT whd = (ulongT)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int); + return *this; + } + + //! Fill pixel values along the C-axis at a specified pixel position \overloading. + CImg& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) { + const ulongT whd = (ulongT)_width*_height*_depth; + if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double); + return *this; + } + + //! Discard specified sequence of values in the image buffer, along a specific axis. + /** + \param values Sequence of values to discard. + \param axis Axis along which the values are discarded. If set to \c 0 (default value) + the method does it for all the buffer values and returns a one-column vector. + \note Discarded values will change the image geometry, so the resulting image + is returned as a one-column vector. + **/ + template + CImg& discard(const CImg& values, const char axis=0) { + if (is_empty() || !values) return *this; + return get_discard(values,axis).move_to(*this); + } + + template + CImg get_discard(const CImg& values, const char axis=0) const { + if (!values) return +*this; + CImg res; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + ulongT j = 0; + unsigned int k = 0; + int i0 = 0; + res.assign(width(),height(),depth(),spectrum()); + switch (_axis) { + case 'x' : { + cimg_forX(*this,i) { + if ((*this)(i)!=(T)values[j]) { + if (j) --i; + res.draw_image(k,get_columns(i0,i)); + k+=i - i0 + 1; i0 = i + 1; j = 0; + } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = i + 1; } } + } + if (i0=vsiz) { j = 0; i0 = (int)i + 1; }} + } + if ((ulongT)i0& discard(const char axis=0) { + return get_discard(axis).move_to(*this); + } + + //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance. + CImg get_discard(const char axis=0) const { + CImg res; + if (is_empty()) return res; + const char _axis = cimg::lowercase(axis); + T current = *_data?(T)0:(T)1; + int j = 0; + res.assign(width(),height(),depth(),spectrum()); + switch (_axis) { + case 'x' : { + cimg_forX(*this,i) + if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); } + res.resize(j,-100,-100,-100,0); + } break; + case 'y' : { + cimg_forY(*this,i) + if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); } + res.resize(-100,j,-100,-100,0); + } break; + case 'z' : { + cimg_forZ(*this,i) + if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); } + res.resize(-100,-100,j,-100,0); + } break; + case 'c' : { + cimg_forC(*this,i) + if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); } + res.resize(-100,-100,-100,j,0); + } break; + default : { + res.unroll('y'); + cimg_foroff(*this,i) { + const T val = (*this)[i]; + if (val!=current) res[j++] = current = val; + } + res.resize(-100,j,-100,-100,0); + } + } + return res; + } + + //! Invert endianness of all pixel values. + /** + **/ + CImg& invert_endianness() { + cimg::invert_endianness(_data,size()); + return *this; + } + + //! Invert endianness of all pixel values \newinstance. + CImg get_invert_endianness() const { + return (+*this).invert_endianness(); + } + + //! Fill image with random values in specified range. + /** + \param val_min Minimal authorized random value. + \param val_max Maximal authorized random value. + \note Random variables are uniformly distributed in [val_min,val_max]. + **/ + CImg& rand(const T& val_min, const T& val_max) { + const float delta = (float)val_max - (float)val_min + (cimg::type::is_float()?0:1); + if (cimg::type::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng)); + cimg::srand(rng); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng))); + cimg::srand(rng); + } + return *this; + } + + //! Fill image with random values in specified range \newinstance. + CImg get_rand(const T& val_min, const T& val_max) const { + return (+*this).rand(val_min,val_max); + } + + //! Round pixel values. + /** + \param y Rounding precision. + \param rounding_type Rounding type. Can be: + - \c -1: Backward. + - \c 0: Nearest. + - \c 1: Forward. + **/ + CImg& round(const double y=1, const int rounding_type=0) { + if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192); + return *this; + } + + //! Round pixel values \newinstance. + CImg get_round(const double y=1, const unsigned int rounding_type=0) const { + return (+*this).round(y,rounding_type); + } + + //! Add random noise to pixel values. + /** + \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the + global value range. + \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper, + \p 3=Poisson or \p 4=Rician). + \return A reference to the modified image instance. + \note + - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on + the image value itself. + - Function \p CImg::get_noise() is also defined. It returns a non-shared modified copy of the image instance. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_noise(40); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_noise.jpg + **/ + CImg& noise(const double sigma, const unsigned int noise_type=0) { + if (is_empty()) return *this; + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0; + if (nsigma==0 && noise_type!=3) return *this; + if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M); + if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.); + switch (noise_type) { + case 0 : { // Gaussian noise + cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) { + Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng)); + if (val>vmax) val = vmax; + if (valvmax) val = vmax; + if (val::is_float()) { --m; ++M; } + else { m = (Tfloat)cimg::type::min(); M = (Tfloat)cimg::type::max(); } + } + cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_rofoff(*this,off) if (cimg::rand(100,&rng)vmax) val = vmax; + if (val get_noise(const double sigma, const unsigned int noise_type=0) const { + return (+*this).noise(sigma,noise_type); + } + + //! Linearly normalize pixel values. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \param constant_case_ratio In case of instance image having a constant value, tell what ratio + of [min_value,max_value] is used to fill the normalized image + (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(160,220); + (img,res).display(); + \endcode + \image html ref_normalize2.jpg + **/ + CImg& normalize(const T& min_value, const T& max_value, + const float constant_case_ratio=0) { + if (is_empty()) return *this; + const T a = min_value get_normalize(const T& min_value, const T& max_value, + const float ratio_if_constant_image=0) const { + return CImg(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image); + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm. + /** + \par Example + \code + const CImg img("reference.jpg"), res = img.get_normalize(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_normalize.jpg + **/ + CImg& normalize() { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + T *ptrd = data(0,y,z,0); + cimg_forX(*this,x) { + const T *ptrs = ptrd; + float n = 0; + cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; } + n = (float)std::sqrt(n); + T *_ptrd = ptrd++; + if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; } + else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; } + } + } + return *this; + } + + //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance. + CImg get_normalize() const { + return CImg(*this,false).normalize(); + } + + //! Compute Lp-norm of each multi-valued pixel of the image instance. + /** + \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0). + \par Example + \code + const CImg img("reference.jpg"), res = img.get_norm(); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_norm.jpg + **/ + CImg& norm(const int norm_type=2) { + if (_spectrum==1 && norm_type) return abs(); + return get_norm(norm_type).move_to(*this); + } + + //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance. + CImg get_norm(const int norm_type=2) const { + if (is_empty()) return *this; + if (_spectrum==1 && norm_type) return get_abs(); + const ulongT whd = (ulongT)_width*_height*_depth; + CImg res(_width,_height,_depth); + switch (norm_type) { + case -1 : { // Linf-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; } + *(ptrd++) = n; + } + } + } break; + case 0 : { // L0-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + unsigned int n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; } + *(ptrd++) = (Tfloat)n; + } + } + } break; + case 1 : { // L1-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; } + *(ptrd++) = n; + } + } + } break; + case 2 : { // L2-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n); + } + } + } break; + default : { // Linf-norm + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16)) + cimg_forYZ(*this,y,z) { + const ulongT off = (ulongT)offset(0,y,z); + const T *ptrs = _data + off; + Tfloat *ptrd = res._data + off; + cimg_forX(*this,x) { + Tfloat n = 0; + const T *_ptrs = ptrs++; + cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; } + *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type); + } + } + } + } + return res; + } + + //! Cut pixel values in specified range. + /** + \param min_value Minimum desired value of the resulting image. + \param max_value Maximum desired value of the resulting image. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_cut(160,220); + (img,res).display(); + \endcode + \image html ref_cut.jpg + **/ + CImg& cut(const T& min_value, const T& max_value) { + if (is_empty()) return *this; + const T a = min_value get_cut(const T& min_value, const T& max_value) const { + return (+*this).cut(min_value,max_value); + } + + //! Uniformly quantize pixel values. + /** + \param nb_levels Number of quantization levels. + \param keep_range Tells if resulting values keep the same range as the original ones. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_quantize(4); + (img,res).display(); + \endcode + \image html ref_quantize.jpg + **/ + CImg& quantize(const unsigned int nb_levels, const bool keep_range=true) { + if (!nb_levels) + throw CImgArgumentException(_cimg_instance + "quantize(): Invalid quantization request with 0 values.", + cimg_instance); + + if (is_empty()) return *this; + Tfloat m, M = (Tfloat)max_min(m), range = M - m; + if (range>0) { + if (keep_range) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); + _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels); + } else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range); + _data[off] = (T)std::min(val,nb_levels - 1); + } + } + return *this; + } + + //! Uniformly quantize pixel values \newinstance. + CImg get_quantize(const unsigned int n, const bool keep_range=true) const { + return (+*this).quantize(n,keep_range); + } + + //! Threshold pixel values. + /** + \param value Threshold value + \param soft_threshold Tells if soft thresholding must be applied (instead of hard one). + \param strict_threshold Tells if threshold value is strict. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_threshold(128); + (img,res.normalize(0,255)).display(); + \endcode + \image html ref_threshold.jpg + **/ + CImg& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) { + if (is_empty()) return *this; + if (strict_threshold) { + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const T v = _data[off]; + _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) + cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0; + } else { + if (soft_threshold) + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768)) + cimg_rofoff(*this,off) { + const T v = _data[off]; + _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0; + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536)) + cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0; + } + return *this; + } + + //! Threshold pixel values \newinstance. + CImg get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const { + return (+*this).threshold(value,soft_threshold,strict_threshold); + } + + //! Compute the histogram of pixel values. + /** + \param nb_levels Number of desired histogram levels. + \param min_value Minimum pixel value considered for the histogram computation. + All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. + All pixel values higher than \p max_value will not be counted. + \note + - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x + in the image I. + - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional. + \par Example + \code + const CImg img = CImg("reference.jpg").histogram(256); + img.display_graph(0,3); + \endcode + \image html ref_histogram.jpg + **/ + CImg& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) { + return get_histogram(nb_levels,min_value,max_value).move_to(*this); + } + + //! Compute the histogram of pixel values \overloading. + CImg& histogram(const unsigned int nb_levels) { + return get_histogram(nb_levels).move_to(*this); + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const { + if (!nb_levels || is_empty()) return CImg(); + const double + vmin = (double)(min_value res(nb_levels,1,1,1,0); + cimg_rof(*this,ptrs,T) { + const T val = *ptrs; + if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))]; + } + return res; + } + + //! Compute the histogram of pixel values \newinstance. + CImg get_histogram(const unsigned int nb_levels) const { + if (!nb_levels || is_empty()) return CImg(); + T vmax = 0, vmin = min_max(vmax); + return get_histogram(nb_levels,vmin,vmax); + } + + //! Equalize histogram of pixel values. + /** + \param nb_levels Number of histogram levels used for the equalization. + \param min_value Minimum pixel value considered for the histogram computation. + All pixel values lower than \p min_value will not be counted. + \param max_value Maximum pixel value considered for the histogram computation. + All pixel values higher than \p max_value will not be counted. + \par Example + \code + const CImg img("reference.jpg"), res = img.get_equalize(256); + (img,res).display(); + \endcode + \image html ref_equalize.jpg + **/ + CImg& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) { + if (!nb_levels || is_empty()) return *this; + const T + vmin = min_value hist = get_histogram(nb_levels,vmin,vmax); + ulongT cumul = 0; + cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; } + if (!cumul) cumul = 1; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576)) + cimg_rofoff(*this,off) { + const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin)); + if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul); + } + return *this; + } + + //! Equalize histogram of pixel values \overloading. + CImg& equalize(const unsigned int nb_levels) { + if (!nb_levels || is_empty()) return *this; + T vmax = 0, vmin = min_max(vmax); + return equalize(nb_levels,vmin,vmax); + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const { + return (+*this).equalize(nblevels,val_min,val_max); + } + + //! Equalize histogram of pixel values \newinstance. + CImg get_equalize(const unsigned int nblevels) const { + return (+*this).equalize(nblevels); + } + + //! Index multi-valued pixels regarding to a specified colormap. + /** + \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing. + \param dithering Level of dithering (0=disable, 1=standard level). + \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors. + \note + - \p img.index(colormap,dithering,1) is equivalent to img.index(colormap,dithering,0).map(colormap). + \par Example + \code + const CImg img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255); + const CImg res = img.get_index(colormap,1,true); + (img,res).display(); + \endcode + \image html ref_index.jpg + **/ + template + CImg& index(const CImg& colormap, const float dithering=1, const bool map_indexes=false) { + return get_index(colormap,dithering,map_indexes).move_to(*this); + } + + //! Index multi-valued pixels regarding to a specified colormap \newinstance. + template + CImg::Tuint> + get_index(const CImg& colormap, const float dithering=1, const bool map_indexes=true) const { + if (colormap._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "index(): Instance and specified colormap (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + typedef typename CImg::Tuint tuint; + if (is_empty()) return CImg(); + const ulongT + whd = (ulongT)_width*_height*_depth, + pwhd = (ulongT)colormap._width*colormap._height*colormap._depth; + CImg res(_width,_height,_depth,map_indexes?_spectrum:1); + if (dithering>0) { // Dithered versions + tuint *ptrd = res._data; + const float ndithering = cimg::cut(dithering,0,1)/16; + Tfloat valm = 0, valM = (Tfloat)max_min(valm); + if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; } + CImg cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1); + Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0); + const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth; + switch (_spectrum) { + case 1 : { // Optimized for scalars + cimg_forYZ(*this,y,z) { + if (yvalM?valM:_val0; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0valM?valM:_val0, + _val1 = (Tfloat)*ptrs1, val1 = _val1valM?valM:_val1, + _val2 = (Tfloat)*ptrs2, val2 = _val2valM?valM:_val2; + Tfloat distmin = cimg::type::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrpvalM?valM:_val; + dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd; + } + if (dist=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z); + for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd; + for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd; + for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, + *ptrs_end = ptrs0 + _width; ptrs0::max(); const t *ptrmin0 = colormap._data; + for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, + *ptrp_end = ptrp1; ptrp0=(cimg_openmp_sizefactor)*64 && + _height*_depth>=16 && pwhd>=16)) + cimg_forYZ(*this,y,z) { + tuint *ptrd = res.data(0,y,z); + for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs::max(); const t *ptrmin = colormap._data; + for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp img("reference.jpg"), + colormap1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255), + colormap2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255), + res = img.get_index(colormap1,0).map(colormap2); + (img,res).display(); + \endcode + \image html ref_map.jpg + **/ + template + CImg& map(const CImg& colormap, const unsigned int boundary_conditions=0) { + return get_map(colormap,boundary_conditions).move_to(*this); + } + + //! Map predefined colormap on the scalar (indexed) image instance \newinstance. + template + CImg get_map(const CImg& colormap, const unsigned int boundary_conditions=0) const { + const ulongT + whd = (ulongT)_width*_height*_depth, siz = size(), + cwhd = (ulongT)colormap._width*colormap._height*colormap._depth, + cwhd2 = 2*cwhd; + CImg res(_width,_height,_depth,_spectrum*colormap._spectrum); + switch (colormap._spectrum) { + + case 1 : { // Optimized for scalars + switch (boundary_conditions) { + case 3 : // Mirror + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256)) + for (longT off = 0; off<(longT)siz; ++off) { + const ulongT ind = ((ulongT)_data[off])%cwhd2; + res[off] = colormap[ind& label(const bool is_high_connectivity=false, const Tfloat tolerance=0, + const bool is_L2_norm=true) { + if (is_empty()) return *this; + return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this); + } + + //! Label connected components \newinstance. + CImg get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0, + const bool is_L2_norm=true) const { + if (is_empty()) return CImg(); + + // Create neighborhood tables. + int dx[13], dy[13], dz[13], nb = 0; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0; + dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0; + if (is_high_connectivity) { + dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0; + } + if (_depth>1) { // 3D version + dx[nb] = 0; dy[nb] = 0; dz[nb++]=1; + if (is_high_connectivity) { + dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1; + dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1; + + dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1; + dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1; + } + } + return _label(nb,dx,dy,dz,tolerance,is_L2_norm); + } + + //! Label connected components \overloading. + /** + \param connectivity_mask Mask of the neighboring pixels. + \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region. + \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used. + **/ + template + CImg& label(const CImg& connectivity_mask, const Tfloat tolerance=0, + const bool is_L2_norm=true) { + if (is_empty()) return *this; + return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this); + } + + //! Label connected components \newinstance. + template + CImg get_label(const CImg& connectivity_mask, const Tfloat tolerance=0, + const bool is_L2_norm=true) const { + if (is_empty()) return CImg(); + int nb = 0; + cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb; + CImg dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0); + nb = 0; + cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) && + connectivity_mask(x,y,z)) { + dx[nb] = x; dy[nb] = y; dz[nb++] = z; + } + return _label(nb,dx,dy,dz,tolerance,is_L2_norm); + } + + CImg _label(const unsigned int nb, const int *const dx, + const int *const dy, const int *const dz, + const Tfloat tolerance, const bool is_L2_norm) const { + CImg res(_width,_height,_depth); + const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance; + + // Init label numbers. + ulongT *ptr = res.data(); + cimg_foroff(res,p) *(ptr++) = p; + + // For each neighbour-direction, label. + for (unsigned int n = 0; n& _system_strescape() { +#define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\ + move_to(list); \ + CImg(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break + CImgList list; + const T *ptrs = _data; + cimg_for(*this,p,T) switch ((int)*p) { + cimg_system_strescape('\\',"\\\\"); + cimg_system_strescape('\"',"\\\""); + cimg_system_strescape('!',"\"\\!\""); + cimg_system_strescape('`',"\\`"); + cimg_system_strescape('$',"\\$"); + } + if (ptrs(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list); + return (list>'x').move_to(*this); + } + + //@} + //--------------------------------- + // + //! \name Color Base Management + //@{ + //--------------------------------- + + //! Return colormap \e "default", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_default.jpg + **/ + static const CImg& default_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,256,1,3); + for (unsigned int index = 0, r = 16; r<256; r+=32) + for (unsigned int g = 16; g<256; g+=32) + for (unsigned int b = 32; b<256; b+=64) { + colormap(0,index,0) = (Tuchar)r; + colormap(0,index,1) = (Tuchar)g; + colormap(0,index++,2) = (Tuchar)b; + } + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "HSV", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hsv.jpg + **/ + static const CImg& HSV_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + CImg tmp(1,256,1,3,1); + tmp.get_shared_channel(0).sequence(0,359); + colormap = tmp.HSVtoRGB(); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "lines", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_lines.jpg + **/ + static const CImg& lines_LUT256() { + static const unsigned char pal[] = { + 0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255, + 53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125, + 206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138, + 101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125, + 16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0, + 97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121, + 227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85, + 210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255, + 20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49, + 210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121, + 166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190, + 85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81, + 247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0, + 170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81, + 215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121, + 0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125, + 0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219, + 251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239, + 89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57, + 202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125, + 32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142, + 8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65, + 194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210, + 154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85, + 146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125, + 0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49, + 89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125, + 69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69, + 40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194, + 125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0, + 125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93, + 16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85, + 0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97, + 255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49, + 162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130, + 36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125, + 219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125, + 202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210, + 85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255, + 97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255, + 85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239, + 89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166, + 0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0, + 174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69, + 162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81, + 158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97, + 57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4, + 73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 }; + static const CImg colormap(pal,1,256,1,3,false); + return colormap; + } + + //! Return colormap \e "hot", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_hot.jpg + **/ + static const CImg& hot_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "cool", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cool.jpg + **/ + static const CImg& cool_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) colormap.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3); + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "jet", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_jet.jpg + **/ + static const CImg& jet_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "flag", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_flag.jpg + **/ + static const CImg& flag_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,4,1,3,(T)0); + colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255; + colormap.resize(1,256,1,3,0,2); + } + cimg::mutex(8,0); + return colormap; + } + + //! Return colormap \e "cube", containing 256 colors entries in RGB. + /** + \return The following \c 256x1x1x3 colormap is returned: + \image html ref_colormap_cube.jpg + **/ + static const CImg& cube_LUT256() { + static CImg colormap; + cimg::mutex(8); + if (!colormap) { + colormap.assign(1,8,1,3,(T)0); + colormap[1] = colormap[3] = colormap[5] = colormap[7] = + colormap[10] = colormap[11] = colormap[12] = colormap[13] = + colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255; + colormap.resize(1,256,1,3,3); + } + cimg::mutex(8,0); + return colormap; + } + + //! Convert pixel values from sRGB to RGB color spaces. + CImg& sRGBtoRGB() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) + cimg_rofoff(*this,off) { + const Tfloat + sval = (Tfloat)_data[off]/255, + val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f)); + _data[off] = (T)cimg::cut(val*255,0,255); + } + return *this; + } + + //! Convert pixel values from sRGB to RGB color spaces \newinstance. + CImg get_sRGBtoRGB() const { + return CImg(*this,false).sRGBtoRGB(); + } + + //! Convert pixel values from RGB to sRGB color spaces. + CImg& RGBtosRGB() { + if (is_empty()) return *this; + cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32)) + cimg_rofoff(*this,off) { + const Tfloat + val = (Tfloat)_data[off]/255, + sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f); + _data[off] = (T)cimg::cut(sval*255,0,255); + } + return *this; + } + + //! Convert pixel values from RGB to sRGB color spaces \newinstance. + CImg get_RGBtosRGB() const { + return CImg(*this,false).RGBtosRGB(); + } + + //! Convert pixel values from RGB to HSI color spaces. + CImg& RGBtoHSI() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSI(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSI() const { + return CImg(*this,false).RGBtoHSI(); + } + + //! Convert pixel values from HSI to RGB color spaces. + CImg& HSItoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSItoRGB(): Instance is not a HSI image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSItoRGB() const { + return CImg< Tuchar>(*this,false).HSItoRGB(); + } + + //! Convert pixel values from RGB to HSL color spaces. + CImg& RGBtoHSL() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSL(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSL() const { + return CImg(*this,false).RGBtoHSL(); + } + + //! Convert pixel values from HSL to RGB color spaces. + CImg& HSLtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSLtoRGB(): Instance is not a HSL image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSLtoRGB() const { + return CImg(*this,false).HSLtoRGB(); + } + + //! Convert pixel values from RGB to HSV color spaces. + CImg& RGBtoHSV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoHSV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_RGBtoHSV() const { + return CImg(*this,false).RGBtoHSV(); + } + + //! Convert pixel values from HSV to RGB color spaces. + CImg& HSVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "HSVtoRGB(): Instance is not a HSV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256)) + for (longT N = 0; N get_HSVtoRGB() const { + return CImg(*this,false).HSVtoRGB(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& RGBtoYCbCr() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYCbCr(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) + for (longT N = 0; N get_RGBtoYCbCr() const { + return CImg(*this,false).RGBtoYCbCr(); + } + + //! Convert pixel values from RGB to YCbCr color spaces. + CImg& YCbCrtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YCbCrtoRGB(): Instance is not a YCbCr image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512)) + for (longT N = 0; N get_YCbCrtoRGB() const { + return CImg(*this,false).YCbCrtoRGB(); + } + + //! Convert pixel values from RGB to YUV color spaces. + CImg& RGBtoYUV() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoYUV(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) + for (longT N = 0; N get_RGBtoYUV() const { + return CImg(*this,false).RGBtoYUV(); + } + + //! Convert pixel values from YUV to RGB color spaces. + CImg& YUVtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "YUVtoRGB(): Instance is not a YUV image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384)) + for (longT N = 0; N get_YUVtoRGB() const { + return CImg< Tuchar>(*this,false).YUVtoRGB(); + } + + //! Convert pixel values from RGB to CMY color spaces. + CImg& RGBtoCMY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoCMY(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_RGBtoCMY() const { + return CImg(*this,false).RGBtoCMY(); + } + + //! Convert pixel values from CMY to RGB color spaces. + CImg& CMYtoRGB() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoRGB(): Instance is not a CMY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_CMYtoRGB() const { + return CImg(*this,false).CMYtoRGB(); + } + + //! Convert pixel values from CMY to CMYK color spaces. + CImg& CMYtoCMYK() { + return get_CMYtoCMYK().move_to(*this); + } + + //! Convert pixel values from CMY to CMYK color spaces \newinstance. + CImg get_CMYtoCMYK() const { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "CMYtoCMYK(): Instance is not a CMY image.", + cimg_instance); + + CImg res(_width,_height,_depth,4); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) + for (longT N = 0; N=255) C = M = Y = 0; + else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; } + pd1[N] = (Tfloat)cimg::cut(C,0,255), + pd2[N] = (Tfloat)cimg::cut(M,0,255), + pd3[N] = (Tfloat)cimg::cut(Y,0,255), + pd4[N] = (Tfloat)cimg::cut(K,0,255); + } + return res; + } + + //! Convert pixel values from CMYK to CMY color spaces. + CImg& CMYKtoCMY() { + return get_CMYKtoCMY().move_to(*this); + } + + //! Convert pixel values from CMYK to CMY color spaces \newinstance. + CImg get_CMYKtoCMY() const { + if (_spectrum!=4) + throw CImgInstanceException(_cimg_instance + "CMYKtoCMY(): Instance is not a CMYK image.", + cimg_instance); + + CImg res(_width,_height,_depth,3); + const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3); + Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024)) + for (longT N = 0; N& RGBtoXYZ(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "RGBtoXYZ(): Instance is not a RGB image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_RGBtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).RGBtoXYZ(use_D65); + } + + //! Convert pixel values from XYZ to RGB color spaces. + /** + \param use_D65 Tell to use the D65 illuminant (D50 otherwise). + **/ + CImg& XYZtoRGB(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoRGB(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048)) + for (longT N = 0; N get_XYZtoRGB(const bool use_D65=true) const { + return CImg(*this,false).XYZtoRGB(use_D65); + } + + //! Convert pixel values from XYZ to Lab color spaces. + CImg& XYZtoLab(const bool use_D65=true) { +#define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116) + + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoLab(): Instance is not a XYZ image.", + cimg_instance); + const CImg white = CImg(1,1,1,3,255).RGBtoXYZ(use_D65); + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) + for (longT N = 0; N get_XYZtoLab(const bool use_D65=true) const { + return CImg(*this,false).XYZtoLab(use_D65); + } + + //! Convert pixel values from Lab to XYZ color spaces. + CImg& LabtoXYZ(const bool use_D65=true) { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "LabtoXYZ(): Instance is not a Lab image.", + cimg_instance); + const CImg white = CImg(1,1,1,3,255).RGBtoXYZ(use_D65); + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128)) + for (longT N = 0; N216?cX*cX*cX:(116*cX - 16)*27/24389), + Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389), + Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389); + p1[N] = (T)(X*white[0]); + p2[N] = (T)(Y*white[1]); + p3[N] = (T)(Z*white[2]); + } + return *this; + } + + //! Convert pixel values from Lab to XYZ color spaces \newinstance. + CImg get_LabtoXYZ(const bool use_D65=true) const { + return CImg(*this,false).LabtoXYZ(use_D65); + } + + //! Convert pixel values from XYZ to xyY color spaces. + CImg& XYZtoxyY() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "XYZtoxyY(): Instance is not a XYZ image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) + for (longT N = 0; N0?sum:1; + p1[N] = (T)(X/nsum); + p2[N] = (T)(Y/nsum); + p3[N] = (T)Y; + } + return *this; + } + + //! Convert pixel values from XYZ to xyY color spaces \newinstance. + CImg get_XYZtoxyY() const { + return CImg(*this,false).XYZtoxyY(); + } + + //! Convert pixel values from xyY pixels to XYZ color spaces. + CImg& xyYtoXYZ() { + if (_spectrum!=3) + throw CImgInstanceException(_cimg_instance + "xyYtoXYZ(): Instance is not a xyY image.", + cimg_instance); + + T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2); + const longT whd = (longT)width()*height()*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096)) + for (longT N = 0; N0?py:1; + p1[N] = (T)(px*Y/ny); + p2[N] = (T)Y; + p3[N] = (T)((1 - px - py)*Y/ny); + } + return *this; + } + + //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance. + CImg get_xyYtoXYZ() const { + return CImg(*this,false).xyYtoXYZ(); + } + + //! Convert pixel values from RGB to Lab color spaces. + CImg& RGBtoLab(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoLab(use_D65); + } + + //! Convert pixel values from RGB to Lab color spaces \newinstance. + CImg get_RGBtoLab(const bool use_D65=true) const { + return CImg(*this,false).RGBtoLab(use_D65); + } + + //! Convert pixel values from Lab to RGB color spaces. + CImg& LabtoRGB(const bool use_D65=true) { + return LabtoXYZ().XYZtoRGB(use_D65); + } + + //! Convert pixel values from Lab to RGB color spaces \newinstance. + CImg get_LabtoRGB(const bool use_D65=true) const { + return CImg(*this,false).LabtoRGB(use_D65); + } + + //! Convert pixel values from RGB to xyY color spaces. + CImg& RGBtoxyY(const bool use_D65=true) { + return RGBtoXYZ(use_D65).XYZtoxyY(); + } + + //! Convert pixel values from RGB to xyY color spaces \newinstance. + CImg get_RGBtoxyY(const bool use_D65=true) const { + return CImg(*this,false).RGBtoxyY(use_D65); + } + + //! Convert pixel values from xyY to RGB color spaces. + CImg& xyYtoRGB(const bool use_D65=true) { + return xyYtoXYZ().XYZtoRGB(use_D65); + } + + //! Convert pixel values from xyY to RGB color spaces \newinstance. + CImg get_xyYtoRGB(const bool use_D65=true) const { + return CImg(*this,false).xyYtoRGB(use_D65); + } + + //! Convert pixel values from RGB to CMYK color spaces. + CImg& RGBtoCMYK() { + return RGBtoCMY().CMYtoCMYK(); + } + + //! Convert pixel values from RGB to CMYK color spaces \newinstance. + CImg get_RGBtoCMYK() const { + return CImg(*this,false).RGBtoCMYK(); + } + + //! Convert pixel values from CMYK to RGB color spaces. + CImg& CMYKtoRGB() { + return CMYKtoCMY().CMYtoRGB(); + } + + //! Convert pixel values from CMYK to RGB color spaces \newinstance. + CImg get_CMYKtoRGB() const { + return CImg(*this,false).CMYKtoRGB(); + } + + //@} + //------------------------------------------ + // + //! \name Geometric / Spatial Manipulation + //@{ + //------------------------------------------ + + static float _cimg_lanczos(const float x) { + if (x<=-2 || x>=2) return 0; + const float a = (float)cimg::PI*x, b = 0.5f*a; + return (float)(x?std::sin(a)*std::sin(b)/(a*b):1); + } + + //! Resize image to new dimensions. + /** + \param size_x Number of columns (new size along the X-axis). + \param size_y Number of rows (new size along the Y-axis). + \param size_z Number of slices (new size along the Z-axis). + \param size_c Number of vector-channels (new size along the C-axis). + \param interpolation_type Method of interpolation: + - -1 = no interpolation: raw memory resizing. + - 0 = no interpolation: additional space is filled according to \p boundary_conditions. + - 1 = nearest-neighbor interpolation. + - 2 = moving average interpolation. + - 3 = linear interpolation. + - 4 = grid interpolation. + - 5 = cubic interpolation. + - 6 = lanczos interpolation. + \param boundary_conditions Type of boundary conditions used if necessary. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). + **/ + CImg& resize(const int size_x, const int size_y=-100, + const int size_z=-100, const int size_c=-100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + if (!size_x || !size_y || !size_z || !size_c) return assign(); + const unsigned int + _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x), + _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y), + _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z), + _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c), + sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1; + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this; + if (is_empty()) return assign(sx,sy,sz,sc,(T)0); + if (interpolation_type==-1 && sx*sy*sz*sc==size()) { + _width = sx; _height = sy; _depth = sz; _spectrum = sc; + return *this; + } + return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c).move_to(*this); + } + + //! Resize image to new dimensions \newinstance. + CImg get_resize(const int size_x, const int size_y = -100, + const int size_z = -100, const int size_c = -100, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 || + centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1) + throw CImgArgumentException(_cimg_instance + "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].", + cimg_instance, + centering_x,centering_y,centering_z,centering_c); + + if (!size_x || !size_y || !size_z || !size_c) return CImg(); + const unsigned int + sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)), + sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)), + sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)), + sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100)); + if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this; + if (is_empty()) return CImg(sx,sy,sz,sc,(T)0); + CImg res; + switch (interpolation_type) { + + // Raw resizing. + // + case -1 : + std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc)); + break; + + // No interpolation. + // + case 0 : { + const int + xc = (int)(centering_x*((int)sx - width())), + yc = (int)(centering_y*((int)sy - height())), + zc = (int)(centering_z*((int)sz - depth())), + cc = (int)(centering_c*((int)sc - spectrum())); + + switch (boundary_conditions) { + case 3 : { // Mirror + res.assign(sx,sy,sz,sc); + const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024)) + cimg_forXYZC(res,x,y,z,c) { + const int + mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2), + mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2); + res(x,y,z,c) = (*this)(mx sprite; + if (xc>0) { // X-backward + res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite); + } + if (xc + width()<(int)sx) { // X-forward + res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1, + zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite); + } + if (yc>0) { // Y-backward + res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite); + } + if (yc + height()<(int)sy) { // Y-forward + res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1, + zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite); + } + if (zc>0) { // Z-backward + res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite); + for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite); + } + if (zc + depth()<(int)sz) { // Z-forward + res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite); + for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite); + } + if (cc>0) { // C-backward + res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite); + for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite); + } + if (cc + spectrum()<(int)sc) { // C-forward + res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite); + for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite); + } + } break; + default : // Dirichlet + res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this); + } + break; + } break; + + // Nearest neighbor interpolation. + // + case 1 : { + res.assign(sx,sy,sz,sc); + CImg off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1); + const ulongT + wh = (ulongT)_width*_height, + whd = (ulongT)_width*_height*_depth, + sxy = (ulongT)sx*sy, + sxyz = (ulongT)sx*sy*sz, + one = (ulongT)1; + if (sx==_width) off_x.fill(1); + else { + ulongT *poff_x = off_x._data, curr = 0; + cimg_forX(res,x) { + const ulongT old = curr; + curr = (x + one)*_width/sx; + *(poff_x++) = curr - old; + } + } + if (sy==_height) off_y.fill(_width); + else { + ulongT *poff_y = off_y._data, curr = 0; + cimg_forY(res,y) { + const ulongT old = curr; + curr = (y + one)*_height/sy; + *(poff_y++) = _width*(curr - old); + } + *poff_y = 0; + } + if (sz==_depth) off_z.fill(wh); + else { + ulongT *poff_z = off_z._data, curr = 0; + cimg_forZ(res,z) { + const ulongT old = curr; + curr = (z + one)*_depth/sz; + *(poff_z++) = wh*(curr - old); + } + *poff_z = 0; + } + if (sc==_spectrum) off_c.fill(whd); + else { + ulongT *poff_c = off_c._data, curr = 0; + cimg_forC(res,c) { + const ulongT old = curr; + curr = (c + one)*_spectrum/sc; + *(poff_c++) = whd*(curr - old); + } + *poff_c = 0; + } + + T *ptrd = res._data; + const T* ptrc = _data; + const ulongT *poff_c = off_c._data; + for (unsigned int c = 0; c_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,_height,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256)) + cimg_forYZC(tmp,y,z,v) { + for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d; + if (!b) { tmp(t++,y,z,v)/=_width; b = _width; } + if (!c) { ++s; c = sx; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sy!=_height) { + if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,_depth,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256)) + cimg_forXZC(tmp,x,z,v) { + for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d; + else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d; + if (!b) { tmp(x,t++,z,v)/=_height; b = _height; } + if (!c) { ++s; c = sy; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sz!=_depth) { + if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res); + else { + CImg tmp(sx,sy,sz,_spectrum,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256)) + cimg_forXYC(tmp,x,y,v) { + for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d; + else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d; + if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; } + if (!c) { ++s; c = sz; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + if (sc!=_spectrum) { + if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res); + else { + CImg tmp(sx,sy,sz,sc,0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(sc>=256 && _width*_height*_depth>=256)) + cimg_forXYZ(tmp,x,y,z) { + for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) { + const unsigned int d = std::min(b,c); + a-=d; b-=d; c-=d; + if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d; + else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d; + if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; } + if (!c) { ++s; c = sc; } + } + } + tmp.move_to(res); + } + instance_first = false; + } + + } break; + + // Linear interpolation. + // + case 3 : { + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1; + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrssc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double alpha = *(pfoff++); + const T val1 = *ptrs, val2 = ptrs resx, resy, resz, resc; + if (sx!=_width) { + if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + resx.assign(sx,_height,_depth,_spectrum,(T)0); + const int dx = (int)(2*sx), dy = 2*width(); + int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0; + cimg_forX(resx,x) if ((err-=dy)<=0) { + cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c); + ++xs; + err+=dx; + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + resy.assign(sx,sy,_depth,_spectrum,(T)0); + const int dx = (int)(2*sy), dy = 2*height(); + int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0; + cimg_forY(resy,y) if ((err-=dy)<=0) { + cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c); + ++ys; + err+=dx; + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + resz.assign(sx,sy,sz,_spectrum,(T)0); + const int dx = (int)(2*sz), dy = 2*depth(); + int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0; + cimg_forZ(resz,z) if ((err-=dy)<=0) { + cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c); + ++zs; + err+=dx; + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + resc.assign(sx,sy,sz,sc,(T)0); + const int dx = (int)(2*sc), dy = 2*spectrum(); + int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0; + cimg_forC(resc,c) if ((err-=dy)<=0) { + cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs); + ++cs; + err+=dx; + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Cubic interpolation. + // + case 5 : { + const Tfloat vmin = (Tfloat)cimg::type::min(), vmax = (Tfloat)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2); + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1, + val3 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double + t = *(pfoff++), + val1 = (double)*ptrs, + val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1, + val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1, + val3 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Lanczos interpolation. + // + case 6 : { + const double vmin = (double)cimg::type::min(), vmax = (double)cimg::type::max(); + CImg off(cimg::max(sx,sy,sz,sc)); + CImg foff(off._width); + CImg resx, resy, resz, resc; + double curr, old; + + if (sx!=_width) { + if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx); + else { + if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx); + else { + const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0): + (double)_width/sx; + resx.assign(sx,_height,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forX(resx,x) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(width() - 1.,curr + fx); + *(poff++) = (unsigned int)curr - (unsigned int)old; + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256)) + cimg_forYZC(resx,y,z,c) { + const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, + *const ptrsmax = ptrs0 + (_width - 2); + T *ptrd = resx.data(0,y,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forX(resx,x) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2, + val4 = ptrsvmax?vmax:val); + ptrs+=*(poff++); + } + } + } + } + } else resx.assign(*this,true); + + if (sy!=_height) { + if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy); + else { + if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy); + else { + const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0): + (double)_height/sy; + resy.assign(sx,sy,_depth,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forY(resy,y) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(height() - 1.,curr + fy); + *(poff++) = sx*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256)) + cimg_forXZC(resy,x,z,c) { + const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, + *const ptrsmax = ptrs0 + (_height - 2)*sx; + T *ptrd = resy.data(x,0,z,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forY(resy,y) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sx; + ptrs+=*(poff++); + } + } + } + } + resx.assign(); + } else resy.assign(resx,true); + + if (sz!=_depth) { + if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz); + else { + if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz); + else { + const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0): + (double)_depth/sz; + const unsigned int sxy = sx*sy; + resz.assign(sx,sy,sz,_spectrum); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forZ(resz,z) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(depth() - 1.,curr + fz); + *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256)) + cimg_forXYC(resz,x,y,c) { + const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, + *const ptrsmax = ptrs0 + (_depth - 2)*sxy; + T *ptrd = resz.data(x,y,0,c); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forZ(resz,z) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxy; + ptrs+=*(poff++); + } + } + } + } + resy.assign(); + } else resz.assign(resy,true); + + if (sc!=_spectrum) { + if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc); + else { + if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc); + else { + const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0): + (double)_spectrum/sc; + const unsigned int sxyz = sx*sy*sz; + resc.assign(sx,sy,sz,sc); + curr = old = 0; + { + unsigned int *poff = off._data; + double *pfoff = foff._data; + cimg_forC(resc,c) { + *(pfoff++) = curr - (unsigned int)curr; + old = curr; + curr = std::min(spectrum() - 1.,curr + fc); + *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256)) + cimg_forXYZ(resc,x,y,z) { + const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, + *const ptrsmax = ptrs + (_spectrum - 2)*sxyz; + T *ptrd = resc.data(x,y,z,0); + const unsigned int *poff = off._data; + const double *pfoff = foff._data; + cimg_forC(resc,c) { + const double + t = *(pfoff++), + w0 = _cimg_lanczos(t + 2), + w1 = _cimg_lanczos(t + 1), + w2 = _cimg_lanczos(t), + w3 = _cimg_lanczos(t - 1), + w4 = _cimg_lanczos(t - 2), + val2 = (double)*ptrs, + val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2, + val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1, + val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2, + val4 = ptrsvmax?vmax:val); + ptrd+=sxyz; + ptrs+=*(poff++); + } + } + } + } + resz.assign(); + } else resc.assign(resz,true); + + return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc; + } break; + + // Unknown interpolation. + // + default : + throw CImgArgumentException(_cimg_instance + "resize(): Invalid specified interpolation %d " + "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | " + "5=cubic | 6=lanczos }).", + cimg_instance, + interpolation_type); + } + return res; + } + + //! Resize image to dimensions of another image. + /** + \param src Reference image used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + template + CImg& resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of another image \newinstance. + template + CImg get_resize(const CImg& src, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window. + /** + \param disp Reference display window used for dimensions. + \param interpolation_type Interpolation method. + \param boundary_conditions Boundary conditions. + \param centering_x Set centering type (only if \p interpolation_type=0). + \param centering_y Set centering type (only if \p interpolation_type=0). + \param centering_z Set centering type (only if \p interpolation_type=0). + \param centering_c Set centering type (only if \p interpolation_type=0). + **/ + CImg& resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) { + return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to dimensions of a display window \newinstance. + CImg get_resize(const CImgDisplay& disp, + const int interpolation_type=1, const unsigned int boundary_conditions=0, + const float centering_x = 0, const float centering_y = 0, + const float centering_z = 0, const float centering_c = 0) const { + return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions, + centering_x,centering_y,centering_z,centering_c); + } + + //! Resize image to half-size along XY axes, using an optimized filter. + CImg& resize_halfXY() { + return get_resize_halfXY().move_to(*this); + } + + //! Resize image to half-size along XY axes, using an optimized filter \newinstance. + CImg get_resize_halfXY() const { + if (is_empty()) return *this; + static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, + 0.1231940459f, 0.1935127547f, 0.1231940459f, + 0.07842776544f, 0.1231940459f, 0.07842776544f }; + CImg I(9), res(_width/2,_height/2,_depth,_spectrum); + T *ptrd = res._data; + cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T) + if (x%2 && y%2) *(ptrd++) = (T) + (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] + + I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] + + I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]); + return res; + } + + //! Resize image to double-size, using the Scale2X algorithm. + /** + \note Use anisotropic upscaling algorithm + described here. + **/ + CImg& resize_doubleXY() { + return get_resize_doubleXY().move_to(*this); + } + + //! Resize image to double-size, using the Scale2X algorithm \newinstance. + CImg get_resize_doubleXY() const { +#define _cimg_gs2x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width) + +#define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs2x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(_width<<1,_height<<1,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width; + _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) { + if (Icp!=Icn && Ipc!=Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = Ipc==Icn?Ipc:Icc; + *(ptrd2++) = Icn==Inc?Inc:Icc; + } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; } + } + } + return res; + } + + //! Resize image to triple-size, using the Scale3X algorithm. + /** + \note Use anisotropic upscaling algorithm + described here. + **/ + CImg& resize_tripleXY() { + return get_resize_tripleXY().move_to(*this); + } + + //! Resize image to triple-size, using the Scale3X algorithm \newinstance. + CImg get_resize_tripleXY() const { +#define _cimg_gs3x_for3(bound,i) \ + for (int i = 0, _p1##i = 0, \ + _n1##i = 1>=(bound)?(int)(bound) - 1:1; \ + _n1##i<(int)(bound) || i==--_n1##i; \ + _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width) + +#define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \ + _cimg_gs3x_for3((img)._height,y) for (int x = 0, \ + _p1##x = 0, \ + _n1##x = (int)( \ + (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \ + (I[3] = I[4] = (T)(img)(0,y,z,c)), \ + (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \ + 1>=(img)._width?(img).width() - 1:1); \ + (_n1##x<(img).width() && ( \ + (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \ + (I[5] = (T)(img)(_n1##x,y,z,c)), \ + (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \ + x==--_n1##x; \ + I[0] = I[1], I[1] = I[2], \ + I[3] = I[4], I[4] = I[5], \ + I[6] = I[7], I[7] = I[8], \ + _p1##x = x++, ++_n1##x) + + if (is_empty()) return *this; + CImg res(3*_width,3*_height,_depth,_spectrum); + CImg_3x3(I,T); + cimg_forZC(*this,z,c) { + T + *ptrd1 = res.data(0,0,z,c), + *ptrd2 = ptrd1 + res._width, + *ptrd3 = ptrd2 + res._width; + _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) { + if (Icp != Icn && Ipc != Inc) { + *(ptrd1++) = Ipc==Icp?Ipc:Icc; + *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc; + *(ptrd1++) = Icp==Inc?Inc:Icc; + *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc; + *(ptrd2++) = Icc; + *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc; + *(ptrd3++) = Ipc==Icn?Ipc:Icc; + *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc; + *(ptrd3++) = Icn==Inc?Inc:Icc; + } else { + *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc; + *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; + *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc; + } + } + } + return res; + } + + //! Mirror image content along specified axis. + /** + \param axis Mirror axis + **/ + CImg& mirror(const char axis) { + if (is_empty()) return *this; + T *pf, *pb, *buf = 0; + switch (cimg::lowercase(axis)) { + case 'x' : { + pf = _data; pb = data(_width - 1); + const unsigned int width2 = _width/2; + for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) { + for (unsigned int x = 0; x get_mirror(const char axis) const { + return (+*this).mirror(axis); + } + + //! Mirror image content along specified axes. + /** + \param axes Mirror axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" + **/ + CImg& mirror(const char *const axes) { + for (const char *s = axes; *s; ++s) mirror(*s); + return *this; + } + + //! Mirror image content along specified axes \newinstance. + CImg get_mirror(const char *const axes) const { + return (+*this).mirror(axes); + } + + //! Shift image content. + /** + \param delta_x Amount of displacement along the X-axis. + \param delta_y Amount of displacement along the Y-axis. + \param delta_z Amount of displacement along the Z-axis. + \param delta_c Amount of displacement along the C-axis. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const unsigned int boundary_conditions=0) { + if (is_empty()) return *this; + if (boundary_conditions==3) + return get_crop(-delta_x,-delta_y,-delta_z,-delta_c, + width() - delta_x - 1, + height() - delta_y - 1, + depth() - delta_z - 1, + spectrum() - delta_c - 1,3).move_to(*this); + if (delta_x) // Shift along X-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width()); + if (!ndelta_x) return *this; + CImg buf(cimg::abs(ndelta_x)); + if (ndelta_x>0) cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T)); + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T)); + std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T)); + std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_x<0) { + const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(_width - 1,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width())?width() - 1:delta_x; + if (!ndelta_x) return *this; + cimg_forYZC(*this,y,z,c) { + std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T)); + T *ptrd = data(0,y,z,c); + const T val = *ptrd; + for (int l = 0; l=width()) return fill((T)0); + if (delta_x<0) cimg_forYZC(*this,y,z,c) { + std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T)); + std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T)); + } else cimg_forYZC(*this,y,z,c) { + std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T)); + std::memset(data(0,y,z,c),0,delta_x*sizeof(T)); + } + } + + if (delta_y) // Shift along Y-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height()); + if (!ndelta_y) return *this; + CImg buf(width(),cimg::abs(ndelta_y)); + if (ndelta_y>0) cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T)); + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T)); + std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T)); + std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_y<0) { + const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c); + for (int l = 0; l=height())?height() - 1:delta_y; + if (!ndelta_y) return *this; + cimg_forZC(*this,z,c) { + std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T)); + T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c); + for (int l = 0; l=height()) return fill((T)0); + if (delta_y<0) cimg_forZC(*this,z,c) { + std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T)); + std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T)); + } else cimg_forZC(*this,z,c) { + std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T)); + std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T)); + } + } + + if (delta_z) // Shift along Z-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth()); + if (!ndelta_z) return *this; + CImg buf(width(),height(),cimg::abs(ndelta_z)); + if (ndelta_z>0) cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T)); + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T)); + } else cimg_forC(*this,c) { + std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T)); + std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T)); + std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_z<0) { + const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c); + for (int l = 0; l=depth())?depth() - 1:delta_z; + if (!ndelta_z) return *this; + cimg_forC(*this,c) { + std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T)); + T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c); + for (int l = 0; l=depth()) return fill((T)0); + if (delta_z<0) cimg_forC(*this,c) { + std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T)); + std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T)); + } else cimg_forC(*this,c) { + std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T)); + std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T)); + } + } + + if (delta_c) // Shift along C-axis + switch (boundary_conditions) { + case 2 : { // Periodic + const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum()); + if (!ndelta_c) return *this; + CImg buf(width(),height(),depth(),cimg::abs(ndelta_c)); + if (ndelta_c>0) { + std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T)); + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T)); + } else { + std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T)); + std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T)); + std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T)); + } + } break; + case 1 : // Neumann + if (delta_c<0) { + const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c; + if (!ndelta_c) return *this; + std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1); + for (int l = 0; l=spectrum())?spectrum() - 1:delta_c; + if (!ndelta_c) return *this; + std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T)); + T *ptrd = data(0,0,0,1); + for (int l = 0; l=spectrum()) return fill((T)0); + if (delta_c<0) { + std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T)); + std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T)); + } else { + std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T)); + std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T)); + } + } + return *this; + } + + //! Shift image content \newinstance. + CImg get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0, + const unsigned int boundary_conditions=0) const { + return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions); + } + + //! Permute axes order. + /** + \param axes_order Axes permutations, as a C-string of 4 characters. + This function permutes image content regarding the specified axes permutation. + **/ + CImg& permute_axes(const char *const axes_order) { + return get_permute_axes(axes_order).move_to(*this); + } + + //! Permute axes order \newinstance. + CImg get_permute_axes(const char *const axes_order) const { + const T foo = (T)0; + return _permute_axes(axes_order,foo); + } + + template + CImg _permute_axes(const char *const axes_order, const t&) const { + if (is_empty() || !axes_order) return CImg(*this,false); + CImg res; + const T* ptrs = _data; + unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = { 0 }; + for (unsigned int l = 0; axes_order[l]; ++l) { + int c = cimg::lowercase(axes_order[l]); + if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; } + else { ++n_code[c%=4]; s_code[l] = (unsigned char)c; } + } + if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) { + const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]); + ulongT wh, whd; + switch (code) { + case 0x0123 : // xyzc + return +*this; + case 0x0132 : // xycz + res.assign(_width,_height,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0213 : // xzyc + res.assign(_width,_depth,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x0231 : // xzcy + res.assign(_width,_depth,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x0312 : // xcyz + res.assign(_width,_spectrum,_height,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++); + break; + case 0x0321 : // xczy + res.assign(_width,_spectrum,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x1023 : // yxzc + res.assign(_height,_width,_depth,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1032 : // yxcz + res.assign(_height,_width,_spectrum,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1203 : // yzxc + res.assign(_height,_depth,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x1230 : // yzcx + res.assign(_height,_depth,_spectrum,_width); + switch (_width) { + case 1 : { + t *ptr_r = res.data(0,0,0,0); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)*(ptrs++); + } + } break; + case 2 : { + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + ptrs+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB + t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + ptrs+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA + t + *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), + *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3); + for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) { + *(ptr_r++) = (t)ptrs[0]; + *(ptr_g++) = (t)ptrs[1]; + *(ptr_b++) = (t)ptrs[2]; + *(ptr_a++) = (t)ptrs[3]; + ptrs+=4; + } + } break; + default : { + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++); + return res; + } + } + break; + case 0x1302 : // ycxz + res.assign(_height,_spectrum,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x1320 : // yczx + res.assign(_height,_spectrum,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2013 : // zxyc + res.assign(_depth,_width,_height,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2031 : // zxcy + res.assign(_depth,_width,_spectrum,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2103 : // zyxc + res.assign(_depth,_height,_width,_spectrum); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++); + break; + case 0x2130 : // zycx + res.assign(_depth,_height,_spectrum,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++); + break; + case 0x2301 : // zcxy + res.assign(_depth,_spectrum,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x2310 : // zcyx + res.assign(_depth,_spectrum,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3012 : // cxyz + res.assign(_spectrum,_width,_height,_depth); + switch (_spectrum) { + case 1 : { + const T *ptr_r = data(0,0,0,0); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++); + } break; + case 2 : { + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd+=2; + } + } break; + case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd+=3; + } + } break; + case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA + const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + t *ptrd = res._data; + for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) { + ptrd[0] = (t)*(ptr_r++); + ptrd[1] = (t)*(ptr_g++); + ptrd[2] = (t)*(ptr_b++); + ptrd[3] = (t)*(ptr_a++); + ptrd+=4; + } + } break; + default : { + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++); + } + } + break; + case 0x3021 : // cxzy + res.assign(_spectrum,_width,_depth,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3102 : // cyxz + res.assign(_spectrum,_height,_width,_depth); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++); + break; + case 0x3120 : // cyzx + res.assign(_spectrum,_height,_depth,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++); + break; + case 0x3201 : // czxy + res.assign(_spectrum,_depth,_width,_height); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++); + break; + case 0x3210 : // czyx + res.assign(_spectrum,_depth,_height,_width); + wh = (ulongT)res._width*res._height; whd = wh*res._depth; + cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++); + break; + } + } + if (!res) + throw CImgArgumentException(_cimg_instance + "permute_axes(): Invalid specified axes order '%s'.", + cimg_instance, + axes_order); + return res; + } + + //! Unroll pixel values along specified axis. + /** + \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c'). + **/ + CImg& unroll(const char axis) { + const unsigned int siz = (unsigned int)size(); + if (siz) switch (cimg::lowercase(axis)) { + case 'x' : _width = siz; _height = _depth = _spectrum = 1; break; + case 'y' : _height = siz; _width = _depth = _spectrum = 1; break; + case 'z' : _depth = siz; _width = _height = _spectrum = 1; break; + case 'c' : _spectrum = siz; _width = _height = _depth = 1; break; + } + return *this; + } + + //! Unroll pixel values along specified axis \newinstance. + CImg get_unroll(const char axis) const { + return (+*this).unroll(axis); + } + + //! Rotate image with arbitrary angle. + /** + \param angle Rotation angle, in degrees. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note The size of the image is modified. + **/ + CImg& rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate image with arbitrary angle \newinstance. + CImg get_rotate(const float angle, const unsigned int interpolation=1, + const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res; + const float nangle = cimg::mod(angle,360.f); + if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles + const int wm1 = width() - 1, hm1 = height() - 1; + const int iangle = (int)nangle/90; + switch (iangle) { + case 1 : { // 90 deg + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c); + } break; + case 2 : { // 180 deg + res.assign(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c); + } break; + case 3 : { // 270 deg + res.assign(_height,_width,_depth,_spectrum); + T *ptrd = res._data; + cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c); + } break; + default : // 0 deg + return *this; + } + } else { // Generic angle + const float + rad = (float)(nangle*cimg::PI/180.), + ca = (float)std::cos(rad), sa = (float)std::sin(rad), + ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa), + vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca), + w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1); + res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum); + const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1); + _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2); + } + return res; + } + + //! Rotate image with arbitrary angle, around a center point. + /** + \param angle Rotation angle, in degrees. + \param cx X-coordinate of the rotation center. + \param cy Y-coordinate of the rotation center. + \param interpolation Type of interpolation, { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions, { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& rotate(const float angle, const float cx, const float cy, + const unsigned int interpolation, const unsigned int boundary_conditions=0) { + return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate image with arbitrary angle, around a center point \newinstance. + CImg get_rotate(const float angle, const float cx, const float cy, + const unsigned int interpolation, const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy); + return res; + } + + // [internal] Perform 2D rotation with arbitrary angle. + void _rotate(CImg& res, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions, + const float w2, const float h2, + const float rw2, const float rh2) const { + const float + rad = (float)(angle*cimg::PI/180.), + ca = (float)std::cos(rad), sa = (float)std::sin(rad); + + switch (boundary_conditions) { + case 3 : { // Mirror + + switch (interpolation) { + case 2 : { // Cubic interpolation + const float ww = 2.f*width(), hh = 2.f*height(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048)) + cimg_forXYZC(res,x,y,z,c) { + const float xc = x - rw2, yc = y - rh2, + mx = cimg::mod(w2 + xc*ca + yc*sa,ww), + my = cimg::mod(h2 - xc*sa + yc*ca,hh); + res(x,y,z,c) = _cubic_atXY_c(mx{ 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions. + Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \note Most of the time, size of the image is modified. + **/ + CImg rotate(const float u, const float v, const float w, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate volumetric image with arbitrary angle and axis \newinstance. + CImg get_rotate(const float u, const float v, const float w, const float angle, + const unsigned int interpolation, const unsigned int boundary_conditions) const { + if (is_empty()) return *this; + CImg res; + const float + w1 = _width - 1, h1 = _height - 1, d1 = _depth -1, + w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1; + CImg R = CImg::rotation_matrix(u,v,w,angle); + const CImg + X = R*CImg(8,3,1,1, + 0.f,w1,w1,0.f,0.f,w1,w1,0.f, + 0.f,0.f,h1,h1,0.f,0.f,h1,h1, + 0.f,0.f,0.f,0.f,d1,d1,d1,d1); + float + xm, xM = X.get_shared_row(0).max_min(xm), + ym, yM = X.get_shared_row(1).max_min(ym), + zm, zM = X.get_shared_row(2).max_min(zm); + const int + dx = (int)cimg::round(xM - xm), + dy = (int)cimg::round(yM - ym), + dz = (int)cimg::round(zM - zm); + R.transpose(); + res.assign(1 + dx,1 + dy,1 + dz,_spectrum); + const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz; + _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2); + return res; + } + + //! Rotate volumetric image with arbitrary angle and axis, around a center point. + /** + \param u X-coordinate of the 3D rotation axis. + \param v Y-coordinate of the 3D rotation axis. + \param w Z-coordinate of the 3D rotation axis. + \param angle Rotation angle, in degrees. + \param cx X-coordinate of the rotation center. + \param cy Y-coordinate of the rotation center. + \param cz Z-coordinate of the rotation center. + \param interpolation Type of interpolation. Can be { 0=nearest | 1=linear | 2=cubic | 3=mirror }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann | 2=periodic }. + \note Most of the time, size of the image is modified. + **/ + CImg rotate(const float u, const float v, const float w, const float angle, + const float cx, const float cy, const float cz, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + const float nangle = cimg::mod(angle,360.f); + if (nangle==0.f) return *this; + return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this); + } + + //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance. + CImg get_rotate(const float u, const float v, const float w, const float angle, + const float cx, const float cy, const float cz, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty()) return *this; + CImg res(_width,_height,_depth,_spectrum); + CImg R = CImg::rotation_matrix(u,v,w,-angle); + _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz); + return res; + } + + // [internal] Perform 3D rotation with arbitrary axis and angle. + void _rotate(CImg& res, const CImg& R, + const unsigned int interpolation, const unsigned int boundary_conditions, + const float w2, const float h2, const float d2, + const float rw2, const float rh2, const float rd2) const { + switch (boundary_conditions) { + case 3 : // Mirror + switch (interpolation) { + case 2 : { // Cubic interpolation + const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048)) + cimg_forXYZ(res,x,y,z) { + const float + xc = x - rw2, yc = y - rh2, zc = z - rd2, + X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww), + Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh), + Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd); + cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X{ 0=nearest | 1=linear | 2=cubic }. + \param boundary_conditions Boundary conditions { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + template + CImg& warp(const CImg& p_warp, const unsigned int mode=0, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) { + return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this); + } + + //! Warp image content by a warping field \newinstance + template + CImg get_warp(const CImg& p_warp, const unsigned int mode=0, + const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const { + if (is_empty() || !p_warp) return *this; + if (mode && !is_sameXYZ(p_warp)) + throw CImgArgumentException(_cimg_instance + "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) " + "have different XYZ dimensions.", + cimg_instance, + p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data); + + CImg res(p_warp._width,p_warp._height,p_warp._depth,_spectrum); + + if (p_warp._spectrum==1) { // 1D warping + if (mode>=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = x + (int)cimg::round(*(ptrs0++)); + if (X>=0 && X=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = (int)cimg::round(*(ptrs0++)); + if (X>=0 && X=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++)); + if (X>=0 && X=0 && Y=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++)); + if (X>=0 && X=0 && Y=3) { // Forward-relative warp + res.fill((T)0); + if (interpolation>=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++), + z + (float)*(ptrs2++),c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int + X = x + (int)cimg::round(*(ptrs0++)), + Y = y + (int)cimg::round(*(ptrs1++)), + Z = z + (int)cimg::round(*(ptrs2++)); + if (X>=0 && X=0 && Y=0 && Z=1) // Linear interpolation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096)) + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c); + } + else // Nearest-neighbor interpolation + cimg_forYZC(res,y,z,c) { + const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2); + const T *ptrs = data(0,y,z,c); + cimg_forX(res,x) { + const int + X = (int)cimg::round(*(ptrs0++)), + Y = (int)cimg::round(*(ptrs1++)), + Z = (int)cimg::round(*(ptrs2++)); + if (X>=0 && X=0 && Y=0 && Z get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const { + if (is_empty() || _depth<2) return +*this; + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + const CImg + img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1), + img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc"). + resize(_depth,_height,1,-100,-1), + img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1); + return CImg(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())). + draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy). + draw_image(0,img_xy._height,img_xz); + } + + //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace. + CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) { + if (_depth<2) return *this; + return get_projections2d(x0,y0,z0).move_to(*this); + } + + //! Crop image region. + /** + \param x0 = X-coordinate of the upper-left crop rectangle corner. + \param y0 = Y-coordinate of the upper-left crop rectangle corner. + \param z0 = Z-coordinate of the upper-left crop rectangle corner. + \param c0 = C-coordinate of the upper-left crop rectangle corner. + \param x1 = X-coordinate of the lower-right crop rectangle corner. + \param y1 = Y-coordinate of the lower-right crop rectangle corner. + \param z1 = Z-coordinate of the lower-right crop rectangle corner. + \param c1 = C-coordinate of the lower-right crop rectangle corner. + \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + **/ + CImg& crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const unsigned int boundary_conditions=0) { + return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const unsigned int boundary_conditions=0) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "crop(): Empty instance.", + cimg_instance); + const int + nx0 = x0=0 && nx1=0 && ny1=0 && nz1=0 && nc1 res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0); + if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) + switch (_boundary_conditions) { + case 3 : { // Mirror + const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum(); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) { + const int + mx = cimg::mod(nx0 + x,w2), + my = cimg::mod(ny0 + y,h2), + mz = cimg::mod(nz0 + z,d2), + mc = cimg::mod(nc0 + c,s2); + res(x,y,z,c) = (*this)(mx=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) { + res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()), + cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum())); + } + } break; + case 1 : // Neumann + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c); + break; + default : // Dirichlet + res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this); + } + else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this); + return res; + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const unsigned int boundary_conditions=0) { + return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int y0, + const int x1, const int y1, + const unsigned int boundary_conditions=0) { + return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int y0, + const int x1, const int y1, + const unsigned int boundary_conditions=0) const { + return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \overloading. + CImg& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) { + return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Crop image region \newinstance. + CImg get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const { + return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions); + } + + //! Autocrop image region, regarding the specified background value. + CImg& autocrop(const T& value, const char *const axes="czyx") { + if (is_empty()) return *this; + for (const char *s = axes; *s; ++s) { + const char axis = cimg::lowercase(*s); + const CImg coords = _autocrop(value,axis); + if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels + else switch (axis) { + case 'x' : { + const int x0 = coords[0], x1 = coords[1]; + if (x0>=0 && x1>=0) crop(x0,x1); + } break; + case 'y' : { + const int y0 = coords[0], y1 = coords[1]; + if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1); + } break; + case 'z' : { + const int z0 = coords[0], z1 = coords[1]; + if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1); + } break; + default : { + const int c0 = coords[0], c1 = coords[1]; + if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background value \newinstance. + CImg get_autocrop(const T& value, const char *const axes="czyx") const { + return (+*this).autocrop(value,axes); + } + + //! Autocrop image region, regarding the specified background color. + /** + \param color Color used for the crop. If \c 0, color is guessed. + \param axes Axes used for the crop. + **/ + CImg& autocrop(const T *const color=0, const char *const axes="zyx") { + if (is_empty()) return *this; + if (!color) { // Guess color + const CImg col1 = get_vector_at(0,0,0); + const unsigned int w = _width, h = _height, d = _depth, s = _spectrum; + autocrop(col1,axes); + if (_width==w && _height==h && _depth==d && _spectrum==s) { + const CImg col2 = get_vector_at(w - 1,h - 1,d - 1); + autocrop(col2,axes); + } + return *this; + } + for (const char *s = axes; *s; ++s) { + const char axis = cimg::lowercase(*s); + switch (axis) { + case 'x' : { + int x0 = width(), x1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'x'); + const int nx0 = coords[0], nx1 = coords[1]; + if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); } + } + if (x0==width() && x1==-1) return assign(); else crop(x0,x1); + } break; + case 'y' : { + int y0 = height(), y1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'y'); + const int ny0 = coords[0], ny1 = coords[1]; + if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); } + } + if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1); + } break; + default : { + int z0 = depth(), z1 = -1; + cimg_forC(*this,c) { + const CImg coords = get_shared_channel(c)._autocrop(color[c],'z'); + const int nz0 = coords[0], nz1 = coords[1]; + if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); } + } + if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1); + } + } + } + return *this; + } + + //! Autocrop image region, regarding the specified background color \newinstance. + CImg get_autocrop(const T *const color=0, const char *const axes="zyx") const { + return (+*this).autocrop(color,axes); + } + + CImg _autocrop(const T& value, const char axis) const { + CImg res; + switch (cimg::lowercase(axis)) { + case 'x' : { + int x0 = -1, x1 = -1; + cimg_forX(*this,x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); } + if (x0>=0) { + for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c) + if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); } + } + res = CImg::vector(x0,x1); + } break; + case 'y' : { + int y0 = -1, y1 = -1; + cimg_forY(*this,y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); } + if (y0>=0) { + for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c) + if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); } + } + res = CImg::vector(y0,y1); + } break; + case 'z' : { + int z0 = -1, z1 = -1; + cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); } + if (z0>=0) { + for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c) + if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); } + } + res = CImg::vector(z0,z1); + } break; + default : { + int c0 = -1, c1 = -1; + cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); } + if (c0>=0) { + for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z) + if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; } + } + res = CImg::vector(c0,c1); + } + } + return res; + } + + //! Return specified image column. + /** + \param x0 Image column. + **/ + CImg get_column(const int x0) const { + return get_columns(x0,x0); + } + + //! Return specified image column \inplace. + CImg& column(const int x0) { + return columns(x0,x0); + } + + //! Return specified range of image columns. + /** + \param x0 Starting image column. + \param x1 Ending image column. + **/ + CImg& columns(const int x0, const int x1) { + return get_columns(x0,x1).move_to(*this); + } + + //! Return specified range of image columns \inplace. + CImg get_columns(const int x0, const int x1) const { + return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1); + } + + //! Return specified image row. + CImg get_row(const int y0) const { + return get_rows(y0,y0); + } + + //! Return specified image row \inplace. + /** + \param y0 Image row. + **/ + CImg& row(const int y0) { + return rows(y0,y0); + } + + //! Return specified range of image rows. + /** + \param y0 Starting image row. + \param y1 Ending image row. + **/ + CImg get_rows(const int y0, const int y1) const { + return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1); + } + + //! Return specified range of image rows \inplace. + CImg& rows(const int y0, const int y1) { + return get_rows(y0,y1).move_to(*this); + } + + //! Return specified image slice. + /** + \param z0 Image slice. + **/ + CImg get_slice(const int z0) const { + return get_slices(z0,z0); + } + + //! Return specified image slice \inplace. + CImg& slice(const int z0) { + return slices(z0,z0); + } + + //! Return specified range of image slices. + /** + \param z0 Starting image slice. + \param z1 Ending image slice. + **/ + CImg get_slices(const int z0, const int z1) const { + return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1); + } + + //! Return specified range of image slices \inplace. + CImg& slices(const int z0, const int z1) { + return get_slices(z0,z1).move_to(*this); + } + + //! Return specified image channel. + /** + \param c0 Image channel. + **/ + CImg get_channel(const int c0) const { + return get_channels(c0,c0); + } + + //! Return specified image channel \inplace. + CImg& channel(const int c0) { + return channels(c0,c0); + } + + //! Return specified range of image channels. + /** + \param c0 Starting image channel. + \param c1 Ending image channel. + **/ + CImg get_channels(const int c0, const int c1) const { + return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1); + } + + //! Return specified range of image channels \inplace. + CImg& channels(const int c0, const int c1) { + return get_channels(c0,c1).move_to(*this); + } + + //! Return stream line of a 2D or 3D vector field. + CImg get_streamline(const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false) const { + if (_spectrum!=2 && _spectrum!=3) + throw CImgInstanceException(_cimg_instance + "streamline(): Instance is not a 2D or 3D vector field.", + cimg_instance); + if (_spectrum==2) { + if (is_oriented_only) { + typename CImg::_functor4d_streamline2d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.f,_height - 1.f,0.f); + } else { + typename CImg::_functor4d_streamline2d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.f,_height - 1.f,0.f); + } + } + if (is_oriented_only) { + typename CImg::_functor4d_streamline3d_oriented func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true, + 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); + } + typename CImg::_functor4d_streamline3d_directed func(*this); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false, + 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f); + } + + //! Return stream line of a 3D vector field. + /** + \param func Vector field function. + \param x X-coordinate of the starting point of the streamline. + \param y Y-coordinate of the starting point of the streamline. + \param z Z-coordinate of the starting point of the streamline. + \param L Streamline length. + \param dl Streamline length increment. + \param interpolation_type Type of interpolation. + Can be { 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }. + \param is_backward_tracking Tells if the streamline is estimated forward or backward. + \param is_oriented_only Tells if the direction of the vectors must be ignored. + \param x0 X-coordinate of the first bounding-box vertex. + \param y0 Y-coordinate of the first bounding-box vertex. + \param z0 Z-coordinate of the first bounding-box vertex. + \param x1 X-coordinate of the second bounding-box vertex. + \param y1 Y-coordinate of the second bounding-box vertex. + \param z1 Z-coordinate of the second bounding-box vertex. + **/ + template + static CImg streamline(const tfunc& func, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=false, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + if (dl<=0) + throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g " + "(should be >0).", + pixel_type(), + dl); + + const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1); + if (L<=0 || (is_bounded && (xx1 || yy1 || zz1))) return CImg(); + const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1); + CImg coordinates(size_L,3); + const float dl2 = dl/2; + float + *ptr_x = coordinates.data(0,0), + *ptr_y = coordinates.data(0,1), + *ptr_z = coordinates.data(0,2), + pu = (float)(dl*func(x,y,z,0)), + pv = (float)(dl*func(x,y,z,1)), + pw = (float)(dl*func(x,y,z,2)), + X = x, Y = y, Z = z; + + switch (interpolation_type) { + case 0 : { // Nearest integer interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + const int + xi = (int)(X>0?X + 0.5f:X - 0.5f), + yi = (int)(Y>0?Y + 0.5f:Y - 0.5f), + zi = (int)(Z>0?Z + 0.5f:Z - 0.5f); + float + u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)), + v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)), + w = (float)(dl*func((float)xi,(float)yi,(float)zi,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 1 : { // First-order interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u = (float)(dl*func(X,Y,Z,0)), + v = (float)(dl*func(X,Y,Z,1)), + w = (float)(dl*func(X,Y,Z,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + case 2 : { // Second order interpolation + cimg_forX(coordinates,l) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)), + v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)), + w = (float)(dl*func(X + u0,Y + v0,Z + w0,2)); + if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; } + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } break; + default : { // Fourth order interpolation + cimg_forX(coordinates,k) { + *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z; + float + u0 = (float)(dl2*func(X,Y,Z,0)), + v0 = (float)(dl2*func(X,Y,Z,1)), + w0 = (float)(dl2*func(X,Y,Z,2)); + if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; } + float + u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)), + v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)), + w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2)); + if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; } + float + u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)), + v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)), + w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; } + float + u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)), + v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)), + w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2)); + if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; } + const float + u = (u0 + u3)/3 + (u1 + u2)/1.5f, + v = (v0 + v3)/3 + (v1 + v2)/1.5f, + w = (w0 + w3)/3 + (w1 + w2)/1.5f; + if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } + if (is_bounded && (Xx1 || Yy1 || Zz1)) break; + } + } + } + if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0); + return coordinates; + } + + //! Return stream line of a 3D vector field \overloading. + static CImg streamline(const char *const expression, + const float x, const float y, const float z, + const float L=256, const float dl=0.1f, + const unsigned int interpolation_type=2, const bool is_backward_tracking=true, + const bool is_oriented_only=false, + const float x0=0, const float y0=0, const float z0=0, + const float x1=0, const float y1=0, const float z1=0) { + _functor4d_streamline_expr func(expression); + return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1); + } + + struct _functor4d_streamline2d_directed { + const CImg& ref; + _functor4d_streamline2d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0; + } + }; + + struct _functor4d_streamline3d_directed { + const CImg& ref; + _functor4d_streamline3d_directed(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref._linear_atXYZ(x,y,z,c); + } + }; + + struct _functor4d_streamline2d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline2d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,1,2); } + ~_functor4d_streamline2d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign2d(i,j) \ + if (I(i,j,0)*I(0,0,0) + I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z; + const float + dx = x - xi, + dy = y - yi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; + if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width() - 1; + if (nxi>=ref.width()) nxi = ref.width() - 1; + if (yi<0) yi = 0; + if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height() - 1; + if (nyi>=ref.height()) nyi = ref.height() - 1; + I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1); + I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1); + I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1); + I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1); + _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1); + } + return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0; + } + }; + + struct _functor4d_streamline3d_oriented { + const CImg& ref; + CImg *pI; + _functor4d_streamline3d_oriented(const CImg& pref):ref(pref),pI(0) { pI = new CImg(2,2,2,3); } + ~_functor4d_streamline3d_oriented() { delete pI; } + float operator()(const float x, const float y, const float z, const unsigned int c) const { +#define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0) + I(i,j,k,1)*I(0,0,0,1) + I(i,j,k,2)*I(0,0,0,2)<0) { \ + I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); } + int + xi = (int)x - (x>=0?0:1), nxi = xi + 1, + yi = (int)y - (y>=0?0:1), nyi = yi + 1, + zi = (int)z - (z>=0?0:1), nzi = zi + 1; + const float + dx = x - xi, + dy = y - yi, + dz = z - zi; + if (c==0) { + CImg& I = *pI; + if (xi<0) xi = 0; + if (nxi<0) nxi = 0; + if (xi>=ref.width()) xi = ref.width() - 1; + if (nxi>=ref.width()) nxi = ref.width() - 1; + if (yi<0) yi = 0; + if (nyi<0) nyi = 0; + if (yi>=ref.height()) yi = ref.height() - 1; + if (nyi>=ref.height()) nyi = ref.height() - 1; + if (zi<0) zi = 0; + if (nzi<0) nzi = 0; + if (zi>=ref.depth()) zi = ref.depth() - 1; + if (nzi>=ref.depth()) nzi = ref.depth() - 1; + I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); + I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0); + I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2); + I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); + I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0); + I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2); + I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1); + I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0); + I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2); + I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1); + I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0); + I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2); + _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0); + _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1); + } + return (float)pI->_linear_atXYZ(dx,dy,dz,c); + } + }; + + struct _functor4d_streamline_expr { + _cimg_math_parser *mp; + ~_functor4d_streamline_expr() { mp->end(); delete mp; } + _functor4d_streamline_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,"streamline",CImg::const_empty(),0); + } + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)(*mp)(x,y,z,c); + } + }; + + //! Return a shared-memory image referencing a range of pixels of the image instance. + /** + \param x0 X-coordinate of the starting pixel. + \param x1 X-coordinate of the ending pixel. + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(x0,y0,z0,c0), + end = (ulongT)offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of pixels of the image instance \const. + const CImg get_shared_points(const unsigned int x0, const unsigned int x1, + const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(x0,y0,z0,c0), + end = (ulongT)offset(x1,y0,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).", + cimg_instance, + x0,x1,y0,z0,c0); + return CImg(_data + beg,x1 - x0 + 1,1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance. + /** + \param y0 Y-coordinate of the starting row. + \param y1 Y-coordinate of the ending row. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(0,y0,z0,c0), + end = (ulongT)offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", + cimg_instance, + _width - 1,y0,y1,z0,c0); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); + } + + //! Return a shared-memory image referencing a range of rows of the image instance \const. + const CImg get_shared_rows(const unsigned int y0, const unsigned int y1, + const unsigned int z0=0, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(0,y0,z0,c0), + end = (ulongT)offset(0,y1,z0,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_rows(): Invalid request of a shared-memory subset " + "(0->%u,%u->%u,%u,%u).", + cimg_instance, + _width - 1,y0,y1,z0,c0); + return CImg(_data + beg,_width,y1 - y0 + 1,1,1,true); + } + + //! Return a shared-memory image referencing one row of the image instance. + /** + \param y0 Y-coordinate. + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared-memory image referencing one row of the image instance \const. + const CImg get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const { + return get_shared_rows(y0,y0,z0,c0); + } + + //! Return a shared memory image referencing a range of slices of the image instance. + /** + \param z0 Z-coordinate of the starting slice. + \param z1 Z-coordinate of the ending slice. + \param c0 C-coordinate. + **/ + CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) { + const ulongT + beg = (ulongT)offset(0,0,z0,c0), + end = (ulongT)offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width - 1,_height - 1,z0,z1,c0); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); + } + + //! Return a shared memory image referencing a range of slices of the image instance \const. + const CImg get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const { + const ulongT + beg = (ulongT)offset(0,0,z0,c0), + end = (ulongT)offset(0,0,z1,c0); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_slices(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,%u->%u,%u).", + cimg_instance, + _width - 1,_height - 1,z0,z1,c0); + return CImg(_data + beg,_width,_height,z1 - z0 + 1,1,true); + } + + //! Return a shared-memory image referencing one slice of the image instance. + /** + \param z0 Z-coordinate. + \param c0 C-coordinate. + **/ + CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing one slice of the image instance \const. + const CImg get_shared_slice(const unsigned int z0, const unsigned int c0=0) const { + return get_shared_slices(z0,z0,c0); + } + + //! Return a shared-memory image referencing a range of channels of the image instance. + /** + \param c0 C-coordinate of the starting channel. + \param c1 C-coordinate of the ending channel. + **/ + CImg get_shared_channels(const unsigned int c0, const unsigned int c1) { + const ulongT + beg = (ulongT)offset(0,0,0,c0), + end = (ulongT)offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width - 1,_height - 1,_depth - 1,c0,c1); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); + } + + //! Return a shared-memory image referencing a range of channels of the image instance \const. + const CImg get_shared_channels(const unsigned int c0, const unsigned int c1) const { + const ulongT + beg = (ulongT)offset(0,0,0,c0), + end = (ulongT)offset(0,0,0,c1); + if (beg>end || beg>=size() || end>=size()) + throw CImgArgumentException(_cimg_instance + "get_shared_channels(): Invalid request of a shared-memory subset " + "(0->%u,0->%u,0->%u,%u->%u).", + cimg_instance, + _width - 1,_height - 1,_depth - 1,c0,c1); + return CImg(_data + beg,_width,_height,_depth,c1 - c0 + 1,true); + } + + //! Return a shared-memory image referencing one channel of the image instance. + /** + \param c0 C-coordinate. + **/ + CImg get_shared_channel(const unsigned int c0) { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory image referencing one channel of the image instance \const. + const CImg get_shared_channel(const unsigned int c0) const { + return get_shared_channels(c0,c0); + } + + //! Return a shared-memory version of the image instance. + CImg get_shared() { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Return a shared-memory version of the image instance \const. + const CImg get_shared() const { + return CImg(_data,_width,_height,_depth,_spectrum,true); + } + + //! Split image into a list along specified axis. + /** + \param axis Splitting axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param nb Number of split parts. + \note + - If \c nb==0, instance image is split into blocs of egal values along the specified axis. + - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide. + - If \c nb>0, instance image is split into \c nb blocs. + **/ + CImgList get_split(const char axis, const int nb=-1) const { + CImgList res; + if (is_empty()) return res; + const char _axis = cimg::lowercase(axis); + + if (nb<0) { // Split by bloc size + const unsigned int dp = (unsigned int)(nb?-nb:1); + switch (_axis) { + case 'x': { + if (_width>dp) { + res.assign(_width/dp + (_width%dp?1:0),1,1); + const unsigned int pe = _width - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _height*_depth*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'y': { + if (_height>dp) { + res.assign(_height/dp + (_height%dp?1:0),1,1); + const unsigned int pe = _height - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_depth*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'z': { + if (_depth>dp) { + res.assign(_depth/dp + (_depth%dp?1:0),1,1); + const unsigned int pe = _depth - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_height*_spectrum>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]); + get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } break; + case 'c' : { + if (_spectrum>dp) { + res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1); + const unsigned int pe = _spectrum - dp; + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 && + _width*_height*_depth>=128)) + for (int p = 0; p<(int)pe; p+=dp) + get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]); + get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back()); + } else res.assign(*this); + } + } + } else if (nb>0) { // Split by number of (non-homogeneous) blocs + const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0; + if ((unsigned int)nb>siz) + throw CImgArgumentException(_cimg_instance + "get_split(): Instance cannot be split along %c-axis into %u blocs.", + cimg_instance, + axis,nb); + if (nb==1) res.assign(*this); + else { + int err = (int)siz; + unsigned int _p = 0; + switch (_axis) { + case 'x' : { + cimg_forX(*this,p) if ((err-=nb)<=0) { + get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'y' : { + cimg_forY(*this,p) if ((err-=nb)<=0) { + get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'z' : { + cimg_forZ(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } break; + case 'c' : { + cimg_forC(*this,p) if ((err-=nb)<=0) { + get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res); + err+=(int)siz; + _p = p + 1U; + } + } + } + } + } else { // Split by egal values according to specified axis + T current = *_data; + switch (_axis) { + case 'x' : { + int i0 = 0; + cimg_forX(*this,i) + if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); } + get_columns(i0,width() - 1).move_to(res); + } break; + case 'y' : { + int i0 = 0; + cimg_forY(*this,i) + if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); } + get_rows(i0,height() - 1).move_to(res); + } break; + case 'z' : { + int i0 = 0; + cimg_forZ(*this,i) + if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); } + get_slices(i0,depth() - 1).move_to(res); + } break; + case 'c' : { + int i0 = 0; + cimg_forC(*this,i) + if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); } + get_channels(i0,spectrum() - 1).move_to(res); + } break; + default : { + longT i0 = 0; + cimg_foroff(*this,i) + if ((*this)[i]!=current) { + CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = (longT)i; current = (*this)[i]; + } + CImg(_data + i0,1,(unsigned int)(size() - i0)).move_to(res); + } + } + } + return res; + } + + //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis. + /** + \param values Splitting value sequence. + \param axis Axis along which the splitting is performed. Can be '0' to ignore axis. + \param keep_values Tells if the splitting sequence must be kept in the split blocs. + **/ + template + CImgList get_split(const CImg& values, const char axis=0, const bool keep_values=true) const { + typedef _cimg_Tt Tt; + + CImgList res; + if (is_empty()) return res; + const ulongT vsiz = values.size(); + const char _axis = cimg::lowercase(axis); + if (!vsiz) return CImgList(*this); + if (vsiz==1) { // Split according to a single value + const T value = (T)*values; + switch (_axis) { + case 'x' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_width && (*this)(i)==value) ++i; + if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; } + while (i<_width && (*this)(i)!=value) ++i; + if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; } + } while (i<_width); + } break; + case 'y' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_height && (*this)(0,i)==value) ++i; + if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; } + while (i<_height && (*this)(0,i)!=value) ++i; + if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; } + } while (i<_height); + } break; + case 'z' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_depth && (*this)(0,0,i)==value) ++i; + if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; } + while (i<_depth && (*this)(0,0,i)!=value) ++i; + if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; } + } while (i<_depth); + } break; + case 'c' : { + unsigned int i0 = 0, i = 0; + do { + while (i<_spectrum && (*this)(0,0,0,i)==value) ++i; + if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; } + while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i; + if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; } + } while (i<_spectrum); + } break; + default : { + const ulongT siz = size(); + ulongT i0 = 0, i = 0; + do { + while (ii0) { + if (keep_values) CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); + i0 = i; + } + while (ii0) { CImg(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; } + } while (i=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_columns(i0,i1 - 1).move_to(res); + if (keep_values) get_columns(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_width); + if (i0<_width) get_columns(i0,width() - 1).move_to(res); + } break; + case 'y' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_rows(i0,i1 - 1).move_to(res); + if (keep_values) get_rows(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_height); + if (i0<_height) get_rows(i0,height() - 1).move_to(res); + } break; + case 'z' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_slices(i0,i1 - 1).move_to(res); + if (keep_values) get_slices(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_depth); + if (i0<_depth) get_slices(i0,depth() - 1).move_to(res); + } break; + case 'c' : { + unsigned int i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)(0,0,0,i)==(Tt)*values) { + i1 = i; j = 0; + while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) get_channels(i0,i1 - 1).move_to(res); + if (keep_values) get_channels(i1,i - 1).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i<_spectrum); + if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res); + } break; + default : { + const ulongT siz = size(); + ulongT i0 = 0, i1 = 0, i = 0; + do { + if ((Tt)(*this)[i]==(Tt)*values) { + i1 = i; j = 0; + while (i=vsiz) j = 0; } + i-=j; + if (i>i1) { + if (i1>i0) CImg(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res); + if (keep_values) CImg(_data + i1,1,(unsigned int)(i - i1)).move_to(res); + i0 = i; + } else ++i; + } else ++i; + } while (i(_data + i0,1,(unsigned int)(siz - i0)).move_to(res); + } break; + } + } + return res; + } + + //! Append two images along specified axis. + /** + \param img Image to append with instance image. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Append alignment in \c [0,1]. + **/ + template + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,true).insert(img).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \specialization. + CImg& append(const CImg& img, const char axis='x', const float align=0) { + if (is_empty()) return assign(img,false); + if (!img) return *this; + return CImgList(*this,img,true).get_append(axis,align).move_to(*this); + } + + //! Append two images along specified axis \const. + template + CImg<_cimg_Tt> get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align); + } + + //! Append two images along specified axis \specialization. + CImg get_append(const CImg& img, const char axis='x', const float align=0) const { + if (is_empty()) return +img; + if (!img) return +*this; + return CImgList(*this,img,true).get_append(axis,align); + } + + //@} + //--------------------------------------- + // + //! \name Filtering / Transforms + //@{ + //--------------------------------------- + + //! Correlate image by a kernel. + /** + \param kernel = the correlation kernel. + \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }. + \param is_normalized = enable local normalization. + \param channel mode Channel processing mode. Can be { 0=sum inputs | 1=one-for-one | 2=expand } + \param xcenter X-coordinate of the kernel center (~0U means 'centered'). + \param xstart Starting X-coordinate of the instance image. + \param xend Ending X-coordinate of the instance image. + \param xstride Stride along the X-axis. + \param xdilation Dilation along the X-axis. + \param ycenter Y-coordinate of the kernel center (~0U means 'centered'). + \param ystart Starting Y-coordinate of the instance image. + \param yend Ending Y-coordinate of the instance image. + \param ystride Stride along the Y-axis. + \param ydilation Dilation along the Y-axis. + \param zcenter Z-coordinate of the kernel center (~0U means 'centered'). + \param zstart Starting Z-coordinate of the instance image. + \param zend Ending Z-coordinate of the instance image. + \param zstride Stride along the Z-axis. + \param zdilation Dilation along the Z-axis. + \note + - The correlation of the image instance \p *this by the kernel \p kernel is defined to be: + res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j - + c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k). + **/ + template + CImg& correlate(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) { + if (is_empty() || !kernel) return *this; + return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + } + + template + CImg<_cimg_Ttfloat> get_correlate(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, + const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, + const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + } + + //! Correlate image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> _correlate(const CImg& kernel, const unsigned int boundary_conditions, + const bool is_normalized, const unsigned int channel_mode, + const unsigned int xcenter, const unsigned int ycenter, const unsigned int zcenter, + const unsigned int xstart, const unsigned int ystart, const unsigned zstart, + const unsigned int xend, const unsigned int yend, const unsigned int zend, + const float xstride, const float ystride, const float zstride, + const float xdilation, const float ydilation, const float zdilation, + const bool is_convolve) const { + if (is_empty() || !kernel) return *this; + typedef _cimg_Ttfloat Ttfloat; + CImg res; + _cimg_abort_init_openmp; + cimg_abort_init; + + if (xstart>xend || ystart>yend || zstart>zend) + throw CImgArgumentException(_cimg_instance + "%s(): Invalid xyz-start/end arguments (start = (%u,%u,%u), end = (%u,%u,%u)).", + cimg_instance, + is_convolve?"convolve":"correlate", + xstart,ystart,zstart,xend,yend,zend); + if (xstride<=0 || ystride<=0 || zstride<=0) + throw CImgArgumentException(_cimg_instance + "%s(): Invalid stride arguments (%g,%g,%g).", + cimg_instance, + is_convolve?"convolve":"correlate", + xstride,ystride,zstride); + const int + _xstart = (int)std::min(xstart,_width - 1), + _ystart = (int)std::min(ystart,_height - 1), + _zstart = (int)std::min(zstart,_depth - 1), + _xend = (int)std::min(xend,_width - 1), + _yend = (int)std::min(yend,_height - 1), + _zend = (int)std::min(zend,_depth - 1), + nwidth = 1 + (int)std::floor((_xend - _xstart)/xstride), + nheight = 1 + (int)std::floor((_yend - _ystart)/ystride), + ndepth = 1 + (int)std::floor((_zend + _zstart)/zstride), + _xstride = (int)cimg::round(xstride), + _ystride = (int)cimg::round(ystride), + _zstride = (int)cimg::round(zstride); + + const ulongT + res_whd = (ulongT)nwidth*nheight*ndepth, + res_size = res_whd*res._spectrum; + const bool + is_inner_parallel = res_whd>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res_size>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + + int + _xcenter = xcenter==~0U?kernel.width()/2 - 1 + (kernel.width()%2):(int)std::min(xcenter,kernel._width - 1), + _ycenter = ycenter==~0U?kernel.height()/2 - 1 + (kernel.height()%2):(int)std::min(ycenter,kernel._height - 1), + _zcenter = zcenter==~0U?kernel.depth()/2 - 1 + (kernel.depth()%2):(int)std::min(zcenter,kernel._depth - 1), + _xdilation = (int)cimg::round(xdilation), + _ydilation = (int)cimg::round(ydilation), + _zdilation = (int)cimg::round(zdilation); + + const bool is_int_stride_dilation = + xstride==_xstride && ystride==_ystride && zstride==_zstride && + xdilation==_xdilation && ydilation==_ydilation && zdilation==_zdilation; + + CImg _kernel; + if (is_convolve) { // If convolution, go back to correlation + _kernel = CImg(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true). + get_mirror('x').resize(kernel,-1); + _xcenter = kernel.width() - 1 - _xcenter; + _ycenter = kernel.height() - 1 - _ycenter; + _zcenter = kernel.depth() - 1 - _zcenter; + } else _kernel = kernel.get_shared(); + + if (_kernel._width==_kernel._height && _kernel._width>1 && _kernel._height>1 && + ((_kernel._depth==1 && _kernel._width<=5) || (_kernel._depth==_kernel._width && _kernel._width<=3)) && + boundary_conditions<=1 && channel_mode && + _xcenter==_kernel.width()/2 - 1 + (_kernel.width()%2) && + _ycenter==_kernel.height()/2 - 1 + (_kernel.height()%2) && + _zcenter==_kernel.depth()/2 - 1 + (_kernel.depth()%2) && + is_int_stride_dilation && _xdilation>=0 && _ydilation>=0 && _zdilation>=0) { + + // Optimized versions for centered 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 kernels. + const int dw = 1 - (_kernel.width()%2), dh = 1 - (_kernel.height()%2), dd = 1 - (_kernel.depth()%2); + if (dw || dh || dd) // Force kernel size to be odd + _kernel.get_resize(_kernel.width() + dw,_kernel.height() + dh,_kernel.depth() + dd,_kernel.spectrum(), + 0,0,1,1,1).move_to(_kernel.assign()); + + if (!boundary_conditions) { // Dirichlet -> Add a 1px zero border, then use _correlate() with Neumann + const int + dx = _kernel._width==1?0:1, + dy = _kernel._height==1?0:1, + dz = _kernel._depth==1?0:1; + return get_crop(-dx,-dy,-dz,width() - 1 + dx,height() - 1 + dy,depth() - 1 + dz). + _correlate(_kernel,true,is_normalized,channel_mode,_xcenter,_ycenter,_zcenter, + _xstart + dx,_ystart + dy,_zstart + dz,_xend + dx,_yend + dy,_zend + dz, + xstride,ystride,zstride,xdilation,ydilation,zdilation,false); + + } else { // Neumann boundaries + res.assign(nwidth,nheight,ndepth,std::max(_spectrum,_kernel._spectrum)); + + switch (_kernel._depth) { + case 3 : { // 3x3x3 centered kernel + cimg_forC(res,c) { + cimg_abort_test; + const CImg I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + const int w1 = I.width() - 1, h1 = I.height() - 1, d1 = I.depth() - 1; + CImg _res = res.get_shared_channel(c); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,Z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, z = _zstart + _zstride*Z, + px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?z - _zdilation:0, nz = z + _zdilation I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + const int w1 = I.width() - 1, h1 = I.height() - 1; + CImg _res = res.get_shared_channel(c); + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, + px = x - _xdilation>0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, + nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, + ny = y + _ydilation0?x - _xdilation:0, bx = px - _xdilation>0?px - _xdilation:0, + nx = x + _xdilation0?y - _ydilation:0, by = py - _ydilation>0?py - _ydilation:0, + ny = y + _ydilation I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(c%kernel._spectrum); + CImg _res = res.get_shared_channel(c); + const int w1 = I.width() - 1, h1 = I.height() - 1; + if (is_normalized) { + const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(_res.size(),16384)) + cimg_forXYZ(res,X,Y,z) { + const int + x = _xstart + _xstride*X, y = _ystart + _ystride*Y, + px = x - _xdilation>0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation0?x - _xdilation:0, nx = x + _xdilation0?y - _ydilation:0, ny = y + _ydilation res0 = res.get_shared_channel(0); + for (int c = 1; c K = _kernel.get_shared_channel(kc%_kernel._spectrum); + int w2 = 0, h2 = 0, d2 = 0; + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } + if (boundary_conditions>=3) { w2 = 2*width(); h2 = 2*height(); d2 = 2*depth(); } + res.fill(0); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZC(res,x,y,z,c) { + Ttfloat _val, val = 0, N = 0; + + if (is_int_stride_dilation) + cimg_forXYZ(_kernel,p,q,r) { + const int + ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), + iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), + iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); + switch (boundary_conditions) { + case 0 : _val = atXYZ(ix,iy,iz,c,0); break; // Dirichlet + case 1 : _val = _atXYZ(ix,iy,iz,c); break; // Neumann + case 2 : _val = (*this)(cimg::mod(ix,width()),cimg::mod(iy,height()), // Periodic + cimg::mod(iz,depth()),c); break; + default : { // Mirror + const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); + _val = (*this)(mx I = get_shared_channel(c%_spectrum); + const CImg K = _kernel.get_shared_channel(channel_mode==1?c%_kernel._spectrum:c/_spectrum); + int w2 = 0, h2 = 0, d2 = 0; + Ttfloat M = 0, M2 = 0; + if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = M*M; } + if (boundary_conditions>=3) { w2 = 2*I.width(); h2 = 2*I.height(); d2 = 2*I.depth(); } + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + cimg_forXYZ(res,x,y,z) { + Ttfloat _val, val = 0, N = 0; + + if (is_int_stride_dilation) + cimg_forXYZ(_kernel,p,q,r) { + const int + ix = (int)xstart + _xstride*x + _xdilation*(p - _xcenter), + iy = (int)ystart + _ystride*y + _ydilation*(q - _ycenter), + iz = (int)zstart + _zstride*z + _zdilation*(r - _zcenter); + switch (boundary_conditions) { + case 0 : _val = I.atXYZ(ix,iy,iz,0,0); break; // Dirichlet + case 1 : _val = I._atXYZ(ix,iy,iz); break; // Neumann + case 2 : _val = I(cimg::mod(ix,I.width()),cimg::mod(iy,I.height()), // Periodic + cimg::mod(iz,I.depth())); break; + default : { // Mirror + const int mx = cimg::mod(ix,w2), my = cimg::mod(iy,h2), mz = cimg::mod(iz,d2); + _val = I(mx + CImg& convolve(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) { + if (is_empty() || !kernel) return *this; + return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation).move_to(*this); + } + + //! Convolve image by a kernel \newinstance. + template + CImg<_cimg_Ttfloat> get_convolve(const CImg& kernel, const unsigned int boundary_conditions=1, + const bool is_normalized=false, const unsigned int channel_mode=1, + const unsigned int xcenter=~0U, const unsigned int ycenter=~0U, + const unsigned int zcenter=~0U, + const unsigned int xstart=0, const unsigned int ystart=0, const unsigned zstart=0, + const unsigned int xend=~0U, const unsigned int yend=~0U, + const unsigned int zend=~0U, + const float xstride=1, const float ystride=1, const float zstride=1, + const float xdilation=1, const float ydilation=1, const float zdilation=1) const { + return _correlate(kernel,boundary_conditions,is_normalized,channel_mode, + xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend, + xstride,ystride,zstride,xdilation,ydilation,zdilation,true); + } + + //! Cumulate image values, optionally along specified axis. + /** + \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account. + **/ + CImg& cumulate(const char axis=0) { + switch (cimg::lowercase(axis)) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) { + T *ptrd = data(0,y,z,c); + Tlong cumul = (Tlong)0; + cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; } + } + break; + case 'y' : { + const ulongT w = (ulongT)_width; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && + _width*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) { + T *ptrd = data(x,0,z,c); + Tlong cumul = (Tlong)0; + cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; } + } + } break; + case 'z' : { + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && + _width*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) { + T *ptrd = data(x,y,0,c); + Tlong cumul = (Tlong)0; + cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; } + } + } break; + case 'c' : { + const ulongT whd = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16)) + cimg_forXYZ(*this,x,y,z) { + T *ptrd = data(x,y,z,0); + Tlong cumul = (Tlong)0; + cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; } + } + } break; + default : { // Global cumulation + Tlong cumul = (Tlong)0; + cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; } + } + } + return *this; + } + + //! Cumulate image values, optionally along specified axis \newinstance. + CImg get_cumulate(const char axis=0) const { + return CImg(*this,false).cumulate(axis); + } + + //! Cumulate image values, along specified axes. + /** + \param axes Cumulation axes, as a C-string. + \note \c axes may contains multiple characters, e.g. \c "xyz" + **/ + CImg& cumulate(const char *const axes) { + for (const char *s = axes; *s; ++s) cumulate(*s); + return *this; + } + + //! Cumulate image values, along specified axes \newinstance. + CImg get_cumulate(const char *const axes) const { + return CImg(*this,false).cumulate(axes); + } + + //! Erode image by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& erode(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) { + if (is_empty() || !kernel) return *this; + return get_erode(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Erode image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_erode(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) const { + if (is_empty() || !kernel) return *this; + if (!is_real && kernel==0) return CImg(width(),height(),depth(),spectrum(),0); + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + const int + mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2, + mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1, + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_openmp; + cimg_abort_init; + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_openmp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real erosion + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx1 + xm,my1 + ym,mz1 + zm); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); + if (cval::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); + if (cval=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt min_val = cimg::type::max(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx1 + xm,my1 + ym,mz1 + zm)) { + const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + if (cval& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis + const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forYZC(*this,y,z,c) { + T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _height>1) { // Along Y-axis + const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXZC(*this,x,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val1 && _depth>1) { // Along Z-axis + const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXYC(*this,x,y,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).erode(sx,sy,sz); + } + + //! Erode the image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& erode(const unsigned int s) { + return erode(s,s,s); + } + + //! Erode the image by a square structuring element of specified size \newinstance. + CImg get_erode(const unsigned int s) const { + return (+*this).erode(s); + } + + //! Dilate image by a structuring element. + /** + \param kernel Structuring element. + \param boundary_conditions Boundary conditions. + \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false). + **/ + template + CImg& dilate(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) { + if (is_empty() || !kernel) return *this; + return get_dilate(kernel,boundary_conditions,is_real).move_to(*this); + } + + //! Dilate image by a structuring element \newinstance. + template + CImg<_cimg_Tt> get_dilate(const CImg& kernel, const bool boundary_conditions=true, + const bool is_real=false) const { + if (is_empty() || !kernel || (!is_real && kernel==0)) return *this; + typedef _cimg_Tt Tt; + CImg res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum)); + const int + mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2, + mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1, + mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2; + const bool + is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768, + is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768; + cimg::unused(is_inner_parallel,is_outer_parallel); + _cimg_abort_init_openmp; + cimg_abort_init; + cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel)) + cimg_forC(res,c) _cimg_abort_try_openmp { + cimg_abort_test; + const CImg img = get_shared_channel(c%_spectrum); + const CImg K = kernel.get_shared_channel(c%kernel._spectrum); + if (is_real) { // Real dilation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } _cimg_abort_catch_openmp2 + if (boundary_conditions) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) { + const t mval = K(mx2 - xm,my2 - ym,mz2 - zm); + const Tt cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } else { // Binary dilation + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) + for (int z = mz1; z::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img(x + xm,y + ym,z + zm); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } _cimg_abort_catch_openmp2 + if (boundary_conditions) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel)) + cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + for (int x = 0; x=mye || z=mze)?++x:((x=mxe)?++x:(x=mxe))) { + Tt max_val = cimg::type::min(); + for (int zm = -mz1; zm<=mz2; ++zm) + for (int ym = -my1; ym<=my2; ++ym) + for (int xm = -mx1; xm<=mx2; ++xm) + if (K(mx2 - xm,my2 - ym,mz2 - zm)) { + const Tt cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); + if (cval>max_val) max_val = cval; + } + res(x,y,z,c) = max_val; + } + } _cimg_abort_catch_openmp2 + } + } _cimg_abort_catch_openmp + cimg_abort_test; + return res; + } + + //! Dilate image by a rectangular structuring element of specified size. + /** + \param sx Width of the structuring element. + \param sy Height of the structuring element. + \param sz Depth of the structuring element. + **/ + CImg& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) { + if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this; + if (sx>1 && _width>1) { // Along X-axis + const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forYZC(*this,y,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + + if (sy>1 && _height>1) { // Along Y-axis + const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXZC(*this,x,z,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + + if (sz>1 && _depth>1) { // Along Z-axis + const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, + s2 = _s2>L?L:_s2; + CImg buf(L); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288)) + cimg_forXYC(*this,x,y,c) { + T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1; + const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off; + T cur = *ptrs; ptrs+=off; bool is_first = true; + for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) { + const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; } + } + *(ptrd++) = cur; + if (ptrs>=ptrse) { + T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; } + } else { + for (int p = s1; p>0 && ptrd<=ptrde; --p) { + const T val = *ptrs; if (ptrs=cur) { cur = val; is_first = false; } + *(ptrd++) = cur; + } + for (int p = L - s - 1; p>0; --p) { + const T val = *ptrs; ptrs+=off; + if (is_first) { + const T *nptrs = ptrs - off; cur = val; + for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; } + nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false; + } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; } + *(ptrd++) = cur; + } + ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off; + for (int p = s1; p>0 && ptrs>=ptrsb; --p) { + const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; + } + *(ptrd--) = cur; + for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) { + const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; + } + T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; } + } + } + } + return *this; + } + + //! Dilate image by a rectangular structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const { + return (+*this).dilate(sx,sy,sz); + } + + //! Dilate image by a square structuring element of specified size. + /** + \param s Size of the structuring element. + **/ + CImg& dilate(const unsigned int s) { + return dilate(s,s,s); + } + + //! Dilate image by a square structuring element of specified size \newinstance. + CImg get_dilate(const unsigned int s) const { + return (+*this).dilate(s); + } + + //! Compute watershed transform. + /** + \param priority Priority map. + \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity + in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case. + \note Non-zero values of the instance instance are propagated to zero-valued ones according to + specified the priority map. + **/ + template + CImg& watershed(const CImg& priority, const bool is_high_connectivity=false) { +#define _cimg_watershed_init(cond,X,Y,Z) \ + if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds) + +#define _cimg_watershed_propagate(cond,X,Y,Z) \ + if (cond) { \ + if ((*this)(X,Y,Z)) { \ + ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \ + d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \ + if (d labels(_width,_height,_depth,1,0), seeds(64,3); + CImg::type> Q; + unsigned int sizeQ = 0; + int px, nx, py, ny, pz, nz; + bool is_px, is_nx, is_py, is_ny, is_pz, is_nz; + const bool is_3d = _depth>1; + + // Find seed points and insert them in priority queue. + unsigned int nb_seeds = 0; + const T *ptrs = _data; + cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version + if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0); + seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z; + px = x - 1; nx = x + 1; + py = y - 1; ny = y + 1; + pz = z - 1; nz = z + 1; + is_px = px>=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz=0; is_nx = nx=0; is_ny = ny=0; is_nz = nz::inf(); + T nlabel = (T)0; + _cimg_watershed_propagate(is_px,px,y,z); + _cimg_watershed_propagate(is_nx,nx,y,z); + _cimg_watershed_propagate(is_py,x,py,z); + _cimg_watershed_propagate(is_ny,x,ny,z); + if (is_3d) { + _cimg_watershed_propagate(is_pz,x,y,pz); + _cimg_watershed_propagate(is_nz,x,y,nz); + } + if (is_high_connectivity) { + _cimg_watershed_propagate(is_px && is_py,px,py,z); + _cimg_watershed_propagate(is_nx && is_py,nx,py,z); + _cimg_watershed_propagate(is_px && is_ny,px,ny,z); + _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z); + if (is_3d) { + _cimg_watershed_propagate(is_px && is_pz,px,y,pz); + _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz); + _cimg_watershed_propagate(is_px && is_nz,px,y,nz); + _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz); + _cimg_watershed_propagate(is_py && is_pz,x,py,pz); + _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz); + _cimg_watershed_propagate(is_py && is_nz,x,py,nz); + _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz); + _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz); + _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz); + _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz); + _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz); + _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz); + _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz); + _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz); + _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz); + } + } + (*this)(x,y,z) = nlabel; + labels(x,y,z) = ++nmin; + } + return *this; + } + + //! Compute watershed transform \newinstance. + template + CImg get_watershed(const CImg& priority, const bool is_high_connectivity=false) const { + return (+*this).watershed(priority,is_high_connectivity); + } + + // [internal] Insert/Remove items in priority queue, for watershed/distance transforms. + template + bool _priority_queue_insert(CImg& is_queued, unsigned int& siz, const tv value, + const unsigned int x, const unsigned int y, const unsigned int z, + const unsigned int n=1) { + if (is_queued(x,y,z)) return false; + is_queued(x,y,z) = (tq)n; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz - 1,0) = (T)value; + (*this)(siz - 1,1) = (T)x; + (*this)(siz - 1,2) = (T)y; + (*this)(siz - 1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); + cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); + cimg::swap((*this)(pos,3),(*this)(par,3)); + } + return true; + } + + CImg& _priority_queue_remove(unsigned int& siz) { + (*this)(0,0) = (*this)(--siz,0); + (*this)(0,1) = (*this)(siz,1); + (*this)(0,2) = (*this)(siz,2); + (*this)(0,3) = (*this)(siz,3); + const float value = (*this)(0,0); + unsigned int pos = 0, swap = 0; + do { + const unsigned int left = 2*pos + 1, right = left + 1; + if (right(*this)(right,0)?left:right; + else if (left{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + **/ + CImg& deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) { +#define _cimg_deriche_apply \ + CImg Y(N); \ + double *ptrY = Y._data, yb = 0, yp = 0; \ + T xp = (T)0; \ + if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \ + for (int m = 0; m=0; --n) { \ + const T xc = *(ptrX-=off); \ + const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \ + xa = xn; xn = xc; ya = yn; yn = yc; \ + *ptrX = (T)(*(--ptrY)+yc); \ + } + const char naxis = cimg::lowercase(axis); + const double nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width: + naxis=='y'?_height: + naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.1f && !order)) return *this; + const double + nnsigma = nsigma<0.1f?0.1f:nsigma, + alpha = 1.695f/nnsigma, + ema = std::exp(-alpha), + ema2 = std::exp(-2*alpha), + b1 = -2*ema, + b2 = ema2; + double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; + switch (order) { + case 0 : { + const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2); + a0 = k; + a1 = k*(alpha - 1)*ema; + a2 = k*(alpha + 1)*ema; + a3 = -k*ema2; + } break; + case 1 : { + const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema); + a0 = a3 = 0; + a1 = k*ema; + a2 = -a1; + } break; + case 2 : { + const double + ea = std::exp(-alpha), + k = -(ema2 - 1)/(2*alpha*ema), + kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea)); + a0 = kn; + a1 = -kn*(1 + k*alpha)*ema; + a2 = kn*(1 - k*alpha)*ema; + a3 = -kn*ema2; + } break; + default : + throw CImgArgumentException(_cimg_instance + "deriche(): Invalid specified filter order %u " + "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).", + cimg_instance, + order); + } + coefp = (a0 + a1)/(1 + b1 + b2); + coefn = (a2 + a3)/(1 + b1 + b2); + switch (naxis) { + case 'x' : { + const int N = width(); + const ulongT off = 1U; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; } + } break; + case 'y' : { + const int N = height(); + const ulongT off = (ulongT)_width; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; } + } break; + case 'z' : { + const int N = depth(); + const ulongT off = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; } + } break; + default : { + const int N = spectrum(); + const ulongT off = (ulongT)_width*_height*_depth; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; } + } + } + return *this; + } + + //! Apply recursive Deriche filter \newinstance. + CImg get_deriche(const float sigma, const unsigned int order=0, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).deriche(sigma,order,axis,boundary_conditions); + } + + // [internal] Apply a recursive filter (used by CImg::vanvliet()). + /* + \param ptr the pointer of the data + \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3]. + \param N size of the data + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005). + */ + static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off, + const unsigned int order, const bool boundary_conditions) { + double val[4] = { 0 }; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..] + const double + sumsq = filter[0], sum = sumsq * sumsq, + a1 = filter[1], a2 = filter[2], a3 = filter[3], + scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) ); + double M[9]; // Triggs matrix + M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2); + M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1); + M[2] = scaleM * a3 * (a1 + a3 * a2); + M[3] = scaleM * (a1 + a3 * a2); + M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1); + M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.); + M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2); + M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3); + M[8] = scaleM * a3 * (a1 + a3 * a2); + switch (order) { + case 0 : { + const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0); + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0); + } else { + // Apply Triggs boundary conditions + const double + uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3), + unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) val[k] = val[k - 1]; + } + if (!pass) data -= off; + } + } break; + case 1 : { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + } else { data-=off;} + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + case 2: { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + case 3: { + double x[3]; // [front,center,back] + for (int pass = 0; pass<2; ++pass) { + if (!pass) { + for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0); + for (int k = 0; k<4; ++k) val[k] = 0; + } else { + // Apply Triggs boundary conditions + const double + unp = val[1], unp1 = val[2], unp2 = val[3]; + val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum; + val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum; + val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum; + *data = (T)val[0]; + data -= off; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + for (int n = pass; n0; --k) x[k] = x[k - 1]; + for (int k = 3; k>0; --k) val[k] = val[k - 1]; + } + *data = (T)0; + } + } break; + } + } + + //! Van Vliet recursive Gaussian filter. + /** + \param sigma standard deviation of the Gaussian filter + \param order the order of the filter 0,1,2,3 + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \note dirichlet boundary condition has a strange behavior + + I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering. + IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002. + + (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995) + + Boundary conditions (only for order 0) using Triggs matrix, from + B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet + recursive filtering. IEEE Trans. Signal Processing, + vol. 54, pp. 2365-2367, 2006. + **/ + CImg& vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) { + if (is_empty()) return *this; + if (!cimg::type::is_float()) + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this); + const char naxis = cimg::lowercase(axis); + const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + if (is_empty() || (nsigma<0.5f && !order)) return *this; + const double + nnsigma = nsigma<0.5f?0.5f:nsigma, + m0 = 1.16680, m1 = 1.10783, m2 = 1.40586, + m1sq = m1 * m1, m2sq = m2 * m2, + q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)), + qsq = q * q, + scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq), + b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale, + b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale, + b3 = -qsq * q / scale, + B = ( m0 * (m1sq + m2sq) ) / scale; + double filter[4]; + filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3; + switch (naxis) { + case 'x' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) + _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions); + } break; + case 'y' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) + _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions); + } break; + case 'z' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) + _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height, + order,boundary_conditions); + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) + _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth, + order,boundary_conditions); + } + } + return *this; + } + + //! Blur image using Van Vliet recursive Gaussian filter. \newinstance. + CImg get_vanvliet(const float sigma, const unsigned int order, const char axis='x', + const bool boundary_conditions=true) const { + return CImg(*this,false).vanvliet(sigma,order,axis,boundary_conditions); + } + + //! Blur image. + /** + \param sigma_x Standard deviation of the blur, along the X-axis. + \param sigma_y Standard deviation of the blur, along the Y-axis. + \param sigma_z Standard deviation of the blur, along the Z-axis. + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel. + \note + - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian). + - This is a recursive algorithm, not depending on the values of the standard deviations. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=true) { + if (is_empty()) return *this; + if (is_gaussian) { + if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions); + if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions); + if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions); + } else { + if (_width>1) deriche(sigma_x,0,'x',boundary_conditions); + if (_height>1) deriche(sigma_y,0,'y',boundary_conditions); + if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions); + } + return *this; + } + + //! Blur image \newinstance. + CImg get_blur(const float sigma_x, const float sigma_y, const float sigma_z, + const bool boundary_conditions=true, const bool is_gaussian=true) const { + return CImg(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically. + /** + \param sigma Standard deviation of the blur. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise. + \see deriche(), vanvliet(). + **/ + CImg& blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) { + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian); + } + + //! Blur image isotropically \newinstance. + CImg get_blur(const float sigma, const bool boundary_conditions=true, const bool is_gaussian=true) const { + return CImg(*this,false).blur(sigma,boundary_conditions,is_gaussian); + } + + //! Blur image anisotropically, directed by a field of diffusion tensors. + /** + \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing. + \param amplitude Amplitude of the smoothing. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. + Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=1) { + + // Check arguments and init variables + if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6)) + throw CImgArgumentException(_cimg_instance + "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).", + cimg_instance, + G._width,G._height,G._depth,G._spectrum,G._data); + if (is_empty() || dl<0) return *this; + const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100; + unsigned int iamplitude = cimg::round(namplitude); + const bool is_3d = (G._spectrum==6); + T val_min, val_max = max_min(val_min); + _cimg_abort_init_openmp; + cimg_abort_init; + + if (da<=0) { // Iterated oriented Laplacians + CImg velocity(_width,_height,_depth,_spectrum); + for (unsigned int iteration = 0; iterationveloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + else // 2D version + cimg_forZC(*this,z,c) { + cimg_abort_test; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + } + if (veloc_max>0) *this+=(velocity*=dl/veloc_max); + } + } else { // LIC-based smoothing + const ulongT whd = (ulongT)_width*_height*_depth; + const float sqrt2amplitude = (float)std::sqrt(2*namplitude); + const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1; + CImg res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0); + int N = 0; + if (is_3d) { // 3D version + for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) { + const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), + da2 = datmp<1?360.f:datmp; + for (float theta = 0; theta<360; (theta+=da2),++N) { + const float + thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)*std::cos(phir)), + vy = (float)(std::sin(thetar)*std::cos(phir)), + vz = (float)std::sin(phir); + const t + *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2), + *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3); + cimg_forXYZ(G,xg,yg,zg) { + const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++); + const float + u = (float)(a*vx + b*vy + c*vz), + v = (float)(b*vx + d*vy + e*vz), + w = (float)(c*vx + e*vy + f*vz), + n = 1e-5f + cimg::hypot(u,v,w), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)(w*dln); + *(pd3++) = (Tfloat)n; + } + + cimg_abort_test; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2) + firstprivate(val)) + cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 { + cimg_abort_test2; + cimg_forX(*this,x) { + val.fill(0); + const float + n = (float)W(x,y,z,3), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y, + Z = (float)z; + switch (interpolation_type) { + case 0 : { // Nearest neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const int + cx = (int)(X + 0.5f), + cy = (int)(Y + 0.5f), + cz = (int)(Z + 0.5f); + const float + u = (float)W(cx,cy,cz,0), + v = (float)W(cx,cy,cz,1), + w = (float)W(cx,cy,cz,2); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const float + u = (float)(W._linear_atXYZ(X,Y,Z,0)), + v = (float)(W._linear_atXYZ(X,Y,Z,1)), + w = (float)(W._linear_atXYZ(X,Y,Z,2)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + default : { // 2nd order Runge Kutta + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)), + v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)), + w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)), + u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)), + v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)), + w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c)); + S+=coef; + } + X+=u; Y+=v; Z+=w; + } + } break; + } + Tfloat *ptrd = res.data(x,y,z); + if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } + else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; } + } + } _cimg_abort_catch_openmp2 + } + } + } else { // 2D LIC algorithm + for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) { + const float thetar = (float)(theta*cimg::PI/180), + vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); + const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2); + Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2); + cimg_forXY(G,xg,yg) { + const t a = *(pa++), b = *(pb++), c = *(pc++); + const float + u = (float)(a*vx + b*vy), + v = (float)(b*vx + c*vy), + n = std::max(1e-5f,cimg::hypot(u,v)), + dln = dl/n; + *(pd0++) = (Tfloat)(u*dln); + *(pd1++) = (Tfloat)(v*dln); + *(pd2++) = (Tfloat)n; + } + + cimg_abort_test; + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2) + firstprivate(val)) + cimg_forY(*this,y) _cimg_abort_try_openmp2 { + cimg_abort_test2; + cimg_forX(*this,x) { + val.fill(0); + const float + n = (float)W(x,y,0,2), + fsigma = (float)(n*sqrt2amplitude), + fsigma2 = 2*fsigma*fsigma, + length = gauss_prec*fsigma; + float + S = 0, + X = (float)x, + Y = (float)y; + switch (interpolation_type) { + case 0 : { // Nearest-neighbor + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const int + cx = (int)(X + 0.5f), + cy = (int)(Y + 0.5f); + const float + u = (float)W(cx,cy,0,0), + v = (float)W(cx,cy,0,1); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + case 1 : { // Linear interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u = (float)(W._linear_atXY(X,Y,0,0)), + v = (float)(W._linear_atXY(X,Y,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } break; + default : { // 2nd-order Runge-kutta interpolation + for (float l = 0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { + const float + u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)), + v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)), + u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)), + v = (float)(W._linear_atXY(X + u0,Y + v0,0,1)); + if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; } + else { + const float coef = (float)std::exp(-l*l/fsigma2); + cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c)); + S+=coef; + } + X+=u; Y+=v; + } + } + } + Tfloat *ptrd = res.data(x,y); + if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; } + else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; } + } + } _cimg_abort_catch_openmp2 + } + } + const Tfloat *ptrs = res._data; + cimg_for(*this,ptrd,T) { + const Tfloat _val = *(ptrs++)/N; + *ptrd = _valval_max?val_max:(T)_val); + } + } + cimg_abort_test; + return *this; + } + + //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance. + template + CImg get_blur_anisotropic(const CImg& G, + const float amplitude=60, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return CImg(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way. + /** + \param amplitude Amplitude of the smoothing. + \param sharpness Sharpness. + \param anisotropy Anisotropy. + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param dl Spatial discretization. + \param da Angular discretization. + \param gauss_prec Precision of the diffusion process. + \param interpolation_type Interpolation scheme. + Can be { 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30, + const float gauss_prec=2, const unsigned int interpolation_type=0, + const bool is_fast_approx=true) { + const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100; + const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100; + return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3), + amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx); + } + + //! Blur image anisotropically, in an edge-preserving way \newinstance. + CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, + const float da=30, const float gauss_prec=2, + const unsigned int interpolation_type=0, + const bool is_fast_approx=true) const { + return CImg(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec, + interpolation_type,is_fast_approx); + } + + //! Blur image, with the joint bilateral filter. + /** + \param guide Image used to model the smoothing weights. + \param sigma_x Amount of blur along the X-axis. + \param sigma_y Amount of blur along the Y-axis. + \param sigma_z Amount of blur along the Z-axis. + \param sigma_r Amount of blur along the value axis. + \param sampling_x Amount of downsampling along the X-axis used for the approximation. + Defaults (0) to sigma_x. + \param sampling_y Amount of downsampling along the Y-axis used for the approximation. + Defaults (0) to sigma_y. + \param sampling_z Amount of downsampling along the Z-axis used for the approximation. + Defaults (0) to sigma_z. + \param sampling_r Amount of downsampling along the value axis used for the approximation. + Defaults (0) to sigma_r. + \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 + (extended for 3D volumetric images). + It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m + **/ + template + CImg& blur_bilateral(const CImg& guide, + const float sigma_x, const float sigma_y, + const float sigma_z, const float sigma_r, + const float sampling_x, const float sampling_y, + const float sampling_z, const float sampling_r) { + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this; + T edge_min, edge_max = guide.max_min(edge_min); + if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z); + const float + edge_delta = (float)(edge_max - edge_min), + _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100, + _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100, + _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100, + _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100, + _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f), + _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f), + _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f), + _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256), + derived_sigma_x = _sigma_x / _sampling_x, + derived_sigma_y = _sigma_y / _sampling_y, + derived_sigma_z = _sigma_z / _sampling_z, + derived_sigma_r = _sigma_r / _sampling_r; + const int + padding_x = (int)(2*derived_sigma_x) + 1, + padding_y = (int)(2*derived_sigma_y) + 1, + padding_z = (int)(2*derived_sigma_z) + 1, + padding_r = (int)(2*derived_sigma_r) + 1; + const unsigned int + bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x), + by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y), + bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z), + br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r); + if (bx>0 || by>0 || bz>0 || br>0) { + const bool is_3d = (_depth>1); + if (is_3d) { // 3D version of the algorithm + CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); + cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); + bgrid.fill(0); bgridw.fill(0); + cimg_forXYZ(*this,x,y,z) { + const T val = (*this)(x,y,z,c); + const float edge = (float)_guide(x,y,z); + const int + X = (int)cimg::round(x/_sampling_x) + padding_x, + Y = (int)cimg::round(y/_sampling_y) + padding_y, + Z = (int)cimg::round(z/_sampling_z) + padding_z, + R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; + bgrid(X,Y,Z,R)+=(float)val; + bgridw(X,Y,Z,R)+=1; + } + bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); + bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096)) + cimg_forXYZ(*this,x,y,z) { + const float edge = (float)_guide(x,y,z); + const float + X = x/_sampling_x + padding_x, + Y = y/_sampling_y + padding_y, + Z = z/_sampling_z + padding_z, + R = (edge - edge_min)/_sampling_r + padding_r; + const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R); + (*this)(x,y,z,c) = (T)(bval0/bval1); + } + } + } else { // 2D version of the algorithm + CImg bgrid(bx,by,br,2); + cimg_forC(*this,c) { + const CImg _guide = guide.get_shared_channel(c%guide._spectrum); + bgrid.fill(0); + cimg_forXY(*this,x,y) { + const T val = (*this)(x,y,c); + const float edge = (float)_guide(x,y); + const int + X = (int)cimg::round(x/_sampling_x) + padding_x, + Y = (int)cimg::round(y/_sampling_y) + padding_y, + R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r; + bgrid(X,Y,R,0)+=(float)val; + bgrid(X,Y,R,1)+=1; + } + bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096)) + cimg_forXY(*this,x,y) { + const float edge = (float)_guide(x,y); + const float + X = x/_sampling_x + padding_x, + Y = y/_sampling_y + padding_y, + R = (edge - edge_min)/_sampling_r + padding_r; + const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1); + (*this)(x,y,c) = (T)(bval0/bval1); + } + } + } + } + return *this; + } + + //! Blur image, with the joint bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_x, const float sigma_y, + const float sigma_z, const float sigma_r, + const float sampling_x, const float sampling_y, + const float sampling_z, const float sampling_r) const { + return CImg(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r, + sampling_x,sampling_y,sampling_z,sampling_r); + } + + //! Blur image using the joint bilateral filter. + /** + \param guide Image used to model the smoothing weights. + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s. + \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r. + **/ + template + CImg& blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) { + const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100; + return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r); + } + + //! Blur image using the bilateral filter \newinstance. + template + CImg get_blur_bilateral(const CImg& guide, + const float sigma_s, const float sigma_r, + const float sampling_s=0, const float sampling_r=0) const { + return CImg(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r); + } + + // [internal] Apply a box filter (used by CImg::boxfilter() and CImg::blur_box()). + /* + \param ptr the pointer of the data + \param N size of the data + \param boxsize Size of the box filter (can be subpixel). + \param off the offset between two data point + \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + */ + static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off, + const int order, const bool boundary_conditions, + const unsigned int nb_iter) { + // Smooth. + if (boxsize>1 && nb_iter) { + const int w2 = (int)(boxsize - 1)/2; + const unsigned int winsize = 2*w2 + 1U; + const double frac = (boxsize - winsize)/2.; + CImg win(winsize); + for (unsigned int iter = 0; iter=N) return boundary_conditions?ptr[(N - 1)*off]:T(); + return ptr[x*off]; + } + + // Apply box filter of order 0,1,2. + /** + \param boxsize Size of the box window (can be subpixel) + \param order the order of the filter 0,1 or 2. + \param axis Axis along which the filter is computed. Can be { 'x' | 'y' | 'z' | 'c' }. + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }. + \param nb_iter Number of filter iterations. + **/ + CImg& boxfilter(const float boxsize, const int order, const char axis='x', + const bool boundary_conditions=true, + const unsigned int nb_iter=1) { + if (is_empty() || !boxsize || (boxsize<=1 && !order)) return *this; + const char naxis = cimg::lowercase(axis); + const float nboxsize = boxsize>=0?boxsize:-boxsize* + (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100; + switch (naxis) { + case 'x' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forYZC(*this,y,z,c) + _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter); + } break; + case 'y' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXZC(*this,x,z,c) + _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter); + } break; + case 'z' : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYC(*this,x,y,c) + _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter); + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth*_spectrum>=16)) + cimg_forXYZ(*this,x,y,z) + _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth, + order,boundary_conditions,nb_iter); + } + } + return *this; + } + + // Apply box filter of order 0,1 or 2 \newinstance. + CImg get_boxfilter(const float boxsize, const int order, const char axis='x', + const bool boundary_conditions=true, + const unsigned int nb_iter=1) const { + return CImg(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter); + } + + //! Blur image with a box filter. + /** + \param boxsize_x Size of the box window, along the X-axis (can be subpixel). + \param boxsize_y Size of the box window, along the Y-axis (can be subpixel). + \param boxsize_z Size of the box window, along the Z-axis (can be subpixel). + \param boundary_conditions Boundary conditions. Can be { false=dirichlet | true=neumann }. + \param nb_iter Number of filter iterations. + \note + - This is a recursive algorithm, not depending on the values of the box kernel size. + \see blur(). + **/ + CImg& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true, + const unsigned int nb_iter=1) { + if (is_empty()) return *this; + if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter); + if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter); + if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter); + return *this; + } + + //! Blur image with a box filter \newinstance. + CImg get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z, + const bool boundary_conditions=true) const { + return CImg(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions); + } + + //! Blur image with a box filter. + /** + \param boxsize Size of the box window (can be subpixel). + \param boundary_conditions Boundary conditions. Can be { 0=dirichlet | 1=neumann }.a + \see deriche(), vanvliet(). + **/ + CImg& blur_box(const float boxsize, const bool boundary_conditions=true) { + const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100; + return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions); + } + + //! Blur image with a box filter \newinstance. + CImg get_blur_box(const float boxsize, const bool boundary_conditions=true) const { + return CImg(*this,false).blur_box(boxsize,boundary_conditions); + } + + //! Blur image, with the image guided filter. + /** + \param guide Image used to guide the smoothing process. + \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size. + \param regularization Regularization parameter. + If negative, it is expressed as a percentage of the guide value range. + \note This method implements the filtering algorithm described in: + He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence, + IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013 + **/ + template + CImg& blur_guided(const CImg& guide, const float radius, const float regularization) { + return get_blur_guided(guide,radius,regularization).move_to(*this); + } + + //! Blur image, with the image guided filter \newinstance. + template + CImg get_blur_guided(const CImg& guide, const float radius, const float regularization) const { + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || !radius) return *this; + const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100); + float _regularization = regularization; + if (regularization<0) { + T edge_min, edge_max = guide.max_min(edge_min); + if (edge_min==edge_max) return *this; + _regularization = -regularization*(edge_max - edge_min)/100; + } + _regularization = std::max(_regularization,0.01f); + const unsigned int psize = (unsigned int)(1 + 2*_radius); + CImg + mean_p = get_blur_box(psize,true), + mean_I = guide.get_blur_box(psize,true).resize(mean_p), + cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I), + var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(), + &a = cov_Ip.div(var_I+=_regularization), + &b = mean_p-=a.get_mul(mean_I); + a.blur_box(psize,true); + b.blur_box(psize,true); + return a.mul(guide)+=b; + } + + //! Blur image using patch-based space. + /** + \param guide Image used to model the smoothing weights. + \param sigma_s Amount of blur along the XYZ-axes. + \param sigma_r Amount of blur along the value axis. + \param patch_size Size of the patches. + \param lookup_size Size of the window to search similar patches. + \param smoothness Smoothness for the patch comparison. + \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not. + **/ + template + CImg& blur_patch(const CImg& guide, + const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + if (is_empty() || !patch_size || !lookup_size) return *this; + return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this); + } + + //! Blur image using patch-based space \newinstance. + template + CImg get_blur_patch(const CImg& guide, + const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, + const bool is_fast_approx=true) const { + +#define _cimg_blur_patch3d_fast(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ + firstprivate(P,Q)) \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N##x##N(res,x,y,z); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + tfloat sum_weights = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \ + if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))3?0:1; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \ + } \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch3d(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \ + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \ + firstprivate(P,Q)) \ + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N##x##N(res,x,y,z); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \ + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \ + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \ + tfloat sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \ + tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \ + tfloat distance2 = 0; \ + pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \ + alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch2d_fast(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ + firstprivate(P,Q)) \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N(res,x,y); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + tfloat sum_weights = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \ + if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))3?0:1; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \ + } \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } _cimg_abort_catch_openmp } + +#define _cimg_blur_patch2d(N) { \ + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \ + firstprivate(P,Q)) \ + cimg_forXY(res,x,y) _cimg_abort_try_openmp { \ + cimg_abort_test; \ + cimg_def##N##x##N(res,x,y); \ + tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \ + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \ + tfloat sum_weights = 0, weight_max = 0; \ + cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \ + tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \ + tfloat distance2 = 0; \ + pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \ + distance2/=Pnorm; \ + const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \ + alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \ + if (weight>weight_max) weight_max = weight; \ + sum_weights+=weight; \ + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \ + } \ + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \ + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \ + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \ + } _cimg_abort_catch_openmp } + + typedef _cimg_tfloat tfloat; + if (!is_sameXYZ(guide)) + throw CImgArgumentException(_cimg_instance + "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data); + if (is_empty() || !patch_size || !lookup_size) return +*this; + Tfloat val_min, val_max = (Tfloat)max_min(val_min); + _cimg_abort_init_openmp; + cimg_abort_init; + + CImg res(_width,_height,_depth,_spectrum,0); + const CImg + __guide = guide?CImg(guide,guide.pixel_type()==cimg::type::string()): + CImg(*this,pixel_type()==cimg::type::string()), + _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared(); + CImg P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P); + + t guide_min = (t)0, guide_max = (t)0; + if (sigma_r<0) guide_max = guide.max_min(guide_min); + const float + guide_delta = (float)(guide_max - guide_min), + _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100, + _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100, + sigma_s2 = _sigma_s*_sigma_s, + sigma_r2 = _sigma_r*_sigma_r, + sigma_r3 = 3*_sigma_r, + Pnorm = P.size()*sigma_r2; + const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1; + const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size; + cimg::unused(N2,N3); + if (_depth>1) switch (patch_size) { // 3D + case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break; + default : { + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) + firstprivate(P,Q)) + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Fast + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + tfloat sum_weights = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) + if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))3?0:1; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); + } + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } _cimg_abort_catch_openmp + } else { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) + firstprivate(P,Q)) + cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp { // Exact + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, + x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; + tfloat sum_weights = 0, weight_max = 0; + cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { + (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P; + const tfloat + dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r, + distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2), + weight = std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); + } _cimg_abort_catch_openmp + } + } + } else switch (patch_size) { // 2D + case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break; + case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break; + case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break; + case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break; + case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break; + case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break; + case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break; + case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break; + default : { // Fast + const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1; + if (is_fast_approx) { + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) + firstprivate(P,Q)) + cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Fast + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + tfloat sum_weights = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) + if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))3?0:1; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); + } + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } _cimg_abort_catch_openmp + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) + firstprivate(P,Q)) + cimg_forXY(res,x,y) _cimg_abort_try_openmp { // Exact + cimg_abort_test; + P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true); + const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; + tfloat sum_weights = 0, weight_max = 0; + cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { + (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P; + const tfloat + dx = (tfloat)x - p, dy = (tfloat)y - q, + distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2), + weight = std::exp(-distance2); + if (weight>weight_max) weight_max = weight; + sum_weights+=weight; + cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); + } + sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); + if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; + else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); + } _cimg_abort_catch_openmp + } + } + } + return res.cut(val_min,val_max); + } + + //! Blur image using patch-based space \simplification. + CImg& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) { + return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx); + } + + //! Blur image using patch-based space \simplification \newinstance. + CImg get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3, + const unsigned int lookup_size=4, const float smoothness=0, + const bool is_fast_approx=true) const { + return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx); + } + + //! Blur image with the median filter. + /** + \param n Size of the median filter. + \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation. + **/ + CImg& blur_median(const unsigned int n, const float threshold=0) { + if (!n) return *this; + return get_blur_median(n,threshold).move_to(*this); + } + + //! Blur image with the median filter \newinstance. + CImg get_blur_median(const unsigned int n, const float threshold=0) const { + if (is_empty() || n<=1) return +*this; + CImg res(_width,_height,_depth,_spectrum); + T *ptrd = res._data; + cimg::unused(ptrd); + const int hr = (int)n/2, hl = n - hr - 1; + if (res._depth!=1) { // 3D + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(*this,x,y,z,c) { // With threshold + const int + x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; + const Tfloat val0 = (Tfloat)(*this)(x,y,z,c); + CImg values(n*n*n); + unsigned int nb_values = 0; + T *_ptrd = values.data(); + cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r) + if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; } + res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_depth*_spectrum>=4)) + cimg_forXYZC(*this,x,y,z,c) { // Without threshold + const int + x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1; + res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median(); + } + } else { + if (threshold>0) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && + _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { // With threshold + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + const Tfloat val0 = (Tfloat)(*this)(x,y,c); + CImg values(n*n); + unsigned int nb_values = 0; + T *_ptrd = values.data(); + cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q) + if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; } + res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c); + } + else { + const int + w1 = width() - 1, h1 = height() - 1, + w2 = width() - 2, h2 = height() - 2, + w3 = width() - 3, h3 = height() - 3, + w4 = width() - 4, h4 = height() - 4; + switch (n) { // Without threshold + case 3 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(9); + cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]); + cimg_for_borderXY(*this,x,y,1) + res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c, + std::min(w1,x + 1),std::min(h1,y + 1),0,c).median(); + } + } break; + case 5 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(25); + cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4], + I[5],I[6],I[7],I[8],I[9], + I[10],I[11],I[12],I[13],I[14], + I[15],I[16],I[17],I[18],I[19], + I[20],I[21],I[22],I[23],I[24]); + cimg_for_borderXY(*this,x,y,2) + res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c, + std::min(w1,x + 2),std::min(h1,y + 2),0,c).median(); + } + } break; + case 7 : { + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg I(49); + cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T) + res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6], + I[7],I[8],I[9],I[10],I[11],I[12],I[13], + I[14],I[15],I[16],I[17],I[18],I[19],I[20], + I[21],I[22],I[23],I[24],I[25],I[26],I[27], + I[28],I[29],I[30],I[31],I[32],I[33],I[34], + I[35],I[36],I[37],I[38],I[39],I[40],I[41], + I[42],I[43],I[44],I[45],I[46],I[47],I[48]); + cimg_for_borderXY(*this,x,y,3) + res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c, + std::min(w1,x + 3),std::min(h1,y + 3),0,c).median(); + } + } break; + default : { + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4)) + cimg_forXYC(*this,x,y,c) { + const int + x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, + nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, + nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1; + res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median(); + } + } + } + } + } + return res; + } + + //! Sharpen image. + /** + \param amplitude Sharpening amplitude + \param sharpen_type Select sharpening method. Can be { false=inverse diffusion | true=shock filters }. + \param edge Edge threshold (shock filters only). + \param alpha Gradient smoothness (shock filters only). + \param sigma Tensor smoothness (shock filters only). + **/ + CImg& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, + const float alpha=0, const float sigma=0) { + if (is_empty()) return *this; + T val_min, val_max = max_min(val_min); + const float nedge = edge/2; + CImg velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum); + + if (_depth>1) { // 3D + if (sharpen_type) { // Shock filters + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && + _height*_depth>=16)) + cimg_forYZ(G,y,z) { + Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1), + *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3); + CImg val, vec; + cimg_forX(G,x) { + G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + if (val[2]<0) val[2] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = vec(0,2); + *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + u = G(x,y,z,0), + v = G(x,y,z,1), + w = G(x,y,z,2), + amp = G(x,y,z,3), + ixx = Incc + Ipcc - 2*Iccc, + ixy = (Innc + Ippc - Inpc - Ipnc)/4, + ixz = (Incn + Ipcp - Incp - Ipcn)/4, + iyy = Icnc + Icpc - 2*Iccc, + iyz = (Icnn + Icpp - Icnp - Icpn)/4, + izz = Iccn + Iccp - 2*Iccc, + ixf = Incc - Iccc, + ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, + iyb = Iccc - Icpc, + izf = Iccn - Iccc, + izb = Iccc - Iccp, + itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb), + veloc = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else // Inverse diffusion + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else { // 2D + if (sharpen_type) { // Shock filters + CImg G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors()); + if (sigma>0) G.blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 && + _height>=(cimg_openmp_sizefactor)*16)) + cimg_forY(G,y) { + CImg val, vec; + Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2); + cimg_forX(G,x) { + G.get_tensor_at(x,y).symmetric_eigen(val,vec); + if (val[0]<0) val[0] = 0; + if (val[1]<0) val[1] = 0; + *(ptrG0++) = vec(0,0); + *(ptrG1++) = vec(0,1); + *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge); + } + } + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + u = G(x,y,0), + v = G(x,y,1), + amp = G(x,y,2), + ixx = Inc + Ipc - 2*Icc, + ixy = (Inn + Ipp - Inp - Ipn)/4, + iyy = Icn + Icp - 2*Icc, + ixf = Inc - Icc, + ixb = Icc - Ipc, + iyf = Icn - Icc, + iyb = Icc - Icp, + itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, + it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb), + veloc = -amp*cimg::sign(itt)*cimg::abs(it); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } else // Inverse diffusion + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0; + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc; + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } + _veloc_max[c] = veloc_max; + } + } + const Tfloat veloc_max = _veloc_max.max(); + if (veloc_max<=0) return *this; + return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this); + } + + //! Sharpen image \newinstance. + CImg get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, + const float alpha=0, const float sigma=0) const { + return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma); + } + + //! Return image gradient. + /** + \param axes Axes considered for the gradient computation, as a C-string (e.g "xy"). + \param scheme = Numerical scheme used for the gradient computation: + - -1 = Backward finite differences + - 0 = Centered finite differences (default) + - 1 = Forward finite differences + - 2 = Using Sobel kernels + - 3 = Using rotation invariant kernels + - 4 = Using Deriche recursive filter. + - 5 = Using Van Vliet recursive filter. + **/ + CImgList get_gradient(const char *const axes=0, const int scheme=0) const { + CImgList res; + char __axes[4] = { 0 }; + const char *_axes = axes?axes:__axes; + if (!axes) { + unsigned int k = 0; + if (_width>1) __axes[k++] = 'x'; + if (_height>1) __axes[k++] = 'y'; + if (_depth>1) __axes[k++] = 'z'; + } + + CImg grad; + while (*_axes) { + const char axis = cimg::lowercase(*(_axes++)); + if (axis!='x' && axis!='y' && axis!='z') + throw CImgArgumentException(_cimg_instance + "get_gradient(): Invalid specified axes '%s'.", + cimg_instance, + axes); + const longT off = axis=='x'?1:axis=='y'?_width:_width*_height; + if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) { + grad.assign(_width,_height,_depth,_spectrum,0).move_to(res); + continue; + } + + const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme; + switch (_scheme) { + case -1 : { // Backward finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z)) + grad[pos] = 0; + else + grad[pos] = (Tfloat)_data[pos] - _data[pos - off]; + } + grad.move_to(res); + } break; + case 1 : { // Forward finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1)) + grad[pos] = 0; + else + grad[pos] = (Tfloat)_data[pos + off] - _data[pos]; + } + grad.move_to(res); + } break; + case 2 : { // Sobel scheme + grad.assign(_width,_height,_depth,_spectrum); + if (axis=='x') // X-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn; + } + else // Y-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn; + } + grad.move_to(res); + } break; + case 3 : { // Rotation invariant scheme + const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1)); + grad.assign(_width,_height,_depth,_spectrum); + if (axis=='x') // X-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn; + } + else // Y-axis + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn; + } + grad.move_to(res); + } break; + case 4 : // Deriche filter + get_deriche(0,1,axis).move_to(res); + break; + case 5 : // Van Vliet filter + get_vanvliet(0,1,axis).move_to(res); + break; + default : { // Central finite differences + grad.assign(_width,_height,_depth,_spectrum); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384)) + cimg_forXYZC(*this,x,y,z,c) { + const ulongT pos = offset(x,y,z,c); + if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z)) + grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2; + else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1)) + grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2; + else + grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2; + } + grad.move_to(res); + } break; + } + } + return res; + } + + //! Return image hessian. + /** + \param axes Axes considered for the hessian computation, as a C-string (e.g "xy"). + **/ + CImgList get_hessian(const char *const axes=0) const { + CImgList res; + char __axes[12] = { 0 }; + const char *_axes = axes?axes:__axes; + if (!axes) { + unsigned int k = 0; + if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; } + if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; } + if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; } + if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; } + if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; } + if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; } + } + const unsigned int len = (unsigned int)std::strlen(_axes); + if (len%2) + throw CImgArgumentException(_cimg_instance + "get_hessian(): Invalid specified axes '%s'.", + cimg_instance, + axes); + CImg hess; + for (unsigned int k = 0; k=(cimg_openmp_sizefactor)*16384 && + _depth*_spectrum>=2)) + cimg_forZC(*this,z,c) { + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4; + } + else if (axis1=='x' && axis2=='z') // Ixz + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4; + } + else // Iyz + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 && + _spectrum>=2)) + cimg_forC(*this,c) { + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4; + } + hess.move_to(res); + } + return res; + } + + //! Compute image Laplacian. + CImg& laplacian() { + return get_laplacian().move_to(*this); + } + + //! Compute image Laplacian \newinstance. + CImg get_laplacian() const { + if (is_empty()) return CImg(); + CImg res(_width,_height,_depth,_spectrum); + if (_depth>1) { // 3D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc; + } + } else if (_height>1) { // 2D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc; + } + } else { // 1D + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 && + _height*_depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd = res.data(0,0,0,c); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc; + } + } + return res; + } + + //! Compute the structure tensor field of an image. + /** + \param is_fwbw_scheme scheme. Can be { false=centered | true=forward-backward } + **/ + CImg& structure_tensors(const bool is_fwbw_scheme=false) { + return get_structure_tensors(is_fwbw_scheme).move_to(*this); + } + + //! Compute the structure tensor field of an image \newinstance. + CImg get_structure_tensors(const bool is_fwbw_scheme=false) const { + if (is_empty()) return *this; + CImg res; + if (_depth>1) { // 3D + res.assign(_width,_height,_depth,6,0); + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ix = (Incc - Ipcc)/2, + iy = (Icnc - Icpc)/2, + iz = (Iccn - Iccp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=ix*iz; + *(ptrd3++)+=iy*iy; + *(ptrd4++)+=iy*iz; + *(ptrd5++)+=iz*iz; + } + } + } else { // Forward/backward finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 && + _spectrum>=2)) + cimg_forC(*this,c) { + Tfloat + *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2), + *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5); + CImg_3x3x3(I,Tfloat); + cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { + const Tfloat + ixf = Incc - Iccc, ixb = Iccc - Ipcc, + iyf = Icnc - Iccc, iyb = Iccc - Icpc, + izf = Iccn - Iccc, izb = Iccc - Iccp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4; + *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2; + *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4; + *(ptrd5++)+=(izf*izf + izb*izb)/2; + } + } + } + } else { // 2D + res.assign(_width,_height,_depth,3,0); + if (!is_fwbw_scheme) { // Classical central finite differences + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ix = (Inc - Ipc)/2, + iy = (Icn - Icp)/2; + *(ptrd0++)+=ix*ix; + *(ptrd1++)+=ix*iy; + *(ptrd2++)+=iy*iy; + } + } + } else { // Forward/backward finite differences (version 2) + cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 && + _depth*_spectrum>=2)) + cimg_forC(*this,c) { + Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2); + CImg_3x3(I,Tfloat); + cimg_for3x3(*this,x,y,0,c,I,Tfloat) { + const Tfloat + ixf = Inc - Icc, ixb = Icc - Ipc, + iyf = Icn - Icc, iyb = Icc - Icp; + *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2; + *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4; + *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2; + } + } + } + } + return res; + } + + //! Compute field of diffusion tensors for edge-preserving smoothing. + /** + \param sharpness Sharpness + \param anisotropy Anisotropy + \param alpha Standard deviation of the gradient blur. + \param sigma Standard deviation of the structure tensor blur. + \param is_sqrt Tells if the square root of the tensor field is computed instead. + **/ + CImg& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) { + CImg res; + const float + nsharpness = std::max(sharpness,1e-5f), + power1 = (is_sqrt?0.5f:1)*nsharpness, + power2 = power1/(1e-7f + 1 - anisotropy); + blur(alpha).normalize(0,(T)255); + + if (_depth>1) { // 3D + get_structure_tensors().move_to(res).blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height*_depth>=(cimg_openmp_sizefactor)*256)) + cimg_forYZ(*this,y,z) { + Tfloat + *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2), + *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5); + CImg val(3), vec(3,3); + cimg_forX(*this,x) { + res.get_tensor_at(x,y,z).symmetric_eigen(val,vec); + const float + _l1 = val[2], _l2 = val[1], _l3 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0, + ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), + vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), + wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), + n1 = (float)std::pow(1 + l1 + l2 + l3,-power1), + n2 = (float)std::pow(1 + l1 + l2 + l3,-power2); + *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx; + *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy; + *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz; + *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy; + *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz; + *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz; + } + } + } else { // for 2D images + get_structure_tensors().move_to(res).blur(sigma); + cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && + _height>=(cimg_openmp_sizefactor)*256)) + cimg_forY(*this,y) { + Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2); + CImg val(2), vec(2,2); + cimg_forX(*this,x) { + res.get_tensor_at(x,y).symmetric_eigen(val,vec); + const float + _l1 = val[1], _l2 = val[0], + l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, + ux = vec(1,0), uy = vec(1,1), + vx = vec(0,0), vy = vec(0,1), + n1 = (float)std::pow(1 + l1 + l2,-power1), + n2 = (float)std::pow(1 + l1 + l2,-power2); + *(ptrd0++) = n1*ux*ux + n2*vx*vx; + *(ptrd1++) = n1*ux*uy + n2*vx*vy; + *(ptrd2++) = n1*uy*uy + n2*vy*vy; + } + } + } + return res.move_to(*this); + } + + //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance. + CImg get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f, + const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const { + return CImg(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt); + } + + //! Estimate displacement field between two images. + /** + \param source Reference image. + \param smoothness Smoothness of estimated displacement field. + \param precision Precision required for algorithm convergence. + \param nb_scales Number of scales used to estimate the displacement field. + \param iteration_max Maximum number of iterations allowed for one scale. + \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)). + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). + **/ + CImg& displacement(const CImg& source, const float smoothness=0.1f, const float precision=5.f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) { + return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide). + move_to(*this); + } + + //! Estimate displacement field between two images \newinstance. + CImg get_displacement(const CImg& source, + const float smoothness=0.1f, const float precision=5.f, + const unsigned int nb_scales=0, const unsigned int iteration_max=10000, + const bool is_backward=false, + const CImg& guide=CImg::const_empty()) const { + if (is_empty() || !source) return +*this; + if (!is_sameXYZC(source)) + throw CImgArgumentException(_cimg_instance + "displacement(): Instance and source image (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + source._width,source._height,source._depth,source._spectrum,source._data); + if (precision<0) + throw CImgArgumentException(_cimg_instance + "displacement(): Invalid specified precision %g " + "(should be >=0)", + cimg_instance, + precision); + + const bool is_3d = source._depth>1; + const unsigned int constraint = is_3d?3:2; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum0?nb_scales: + (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1); + + const float _precision = (float)std::pow(10.,-(double)precision); + float sm, sM = source.max_min(sm), tm, tM = max_min(tm); + const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm); + + CImg U, V; + floatT bound = 0; + for (int scale = (int)_nb_scales - 1; scale>=0; --scale) { + const float factor = (float)std::pow(1.5,(double)scale); + const unsigned int + _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1, + _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1, + _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1; + if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // Skip too small scales + const CImg + I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta, + I2 = (get_resize(I1,2)-=tm)/=tdelta; + if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V); + if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3); + else { + if (guide) + guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U); + else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0); + } + + float dt = 2, energy = cimg::type::max(); + const CImgList dI = is_backward?I1.get_gradient():I2.get_gradient(); + cimg_abort_init; + + for (unsigned int iteration = 0; iteration=0) // Isotropic regularization + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } + if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints + U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor; + U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor; + U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor; + } + } else { // Anisotropic regularization + const float nsmoothness = -smoothness; + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) + reduction(+:_energy)) + cimg_forYZ(U,y,z) { + const int + _p1y = y?y - 1:0, _n1y = yx) U(x,y,z,0) = (float)x; + if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y; + if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z; + bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound; + bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound; + bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound; + } else { + if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x; + if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y; + if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z; + bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound; + bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound; + bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints + U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor; + U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor; + U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor; + } + } + } + } else { // 2D version + if (smoothness>=0) // Isotropic regularization + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; + if (U(x,y,1)>y) U(x,y,1) = (float)y; + bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; + bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; + } else { + if (U(x,y,0)<-x) U(x,y,0) = -(float)x; + if (U(x,y,1)<-y) U(x,y,1) = -(float)y; + bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; + bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; + } + _energy+=delta_I*delta_I + smoothness*_energy_regul; + } + if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints + U(_x,_y,0) = V(_x,_y,0)/factor; + U(_x,_y,1) = V(_x,_y,1)/factor; + } + } else { // Anisotropic regularization + const float nsmoothness = -smoothness; + cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 && + _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy)) + cimg_forY(U,y) { + const int _p1y = y?y - 1:0, _n1y = yx) U(x,y,0) = (float)x; + if (U(x,y,1)>y) U(x,y,1) = (float)y; + bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound; + bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound; + } else { + if (U(x,y,0)<-x) U(x,y,0) = -(float)x; + if (U(x,y,1)<-y) U(x,y,1) = -(float)y; + bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound; + bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound; + } + _energy+=delta_I*delta_I + nsmoothness*_energy_regul; + } + if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints + U(_x,_y,0) = V(_x,_y,0)/factor; + U(_x,_y,1) = V(_x,_y,1)/factor; + } + } + } + } + const float d_energy = (_energy - energy)/(sw*sh*sd); + if (d_energy<=0 && -d_energy<_precision) break; + if (d_energy>0) dt*=0.5f; + energy = _energy; + } + } + return U; + } + + //! Compute correspondence map between two images, using a patch-matching algorithm. + /** + \param patch_image The image containing the reference patches to match with the instance image. + \param patch_width Width of the patch used for matching. + \param patch_height Height of the patch used for matching. + \param patch_depth Depth of the patch used for matching. + \param nb_iterations Number of patch-match iterations. + \param nb_randoms Number of randomization attempts (per pixel). + \param patch_penalization Penalization factor in score related patch occurrences. + if negative, also tells that identity result is not avoided. + \param guide Image used as the initial correspondence estimate for the algorithm. + 'guide' may have a last channel with boolean values (0=false | other=true) that + tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask). + \param[out] matching_score Returned as the image of matching scores. + **/ + template + CImg& matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + CImg &matching_score) { + return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \newinstance. + template + CImg get_matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + CImg &matching_score) const { + return _matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization, + guide,true,matching_score); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg& matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5, + const float patch_penalization=0, + const CImg &guide=CImg::const_empty()) { + return get_matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this); + } + + //! Compute correspondence map between two images, using the patch-match algorithm \overloading. + template + CImg get_matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations=5, + const unsigned int nb_randoms=5, + const float patch_penalization=0, + const CImg &guide=CImg::const_empty()) const { + CImg matching_score; + return _matchpatch(patch_image,patch_width,patch_height,patch_depth, + nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score); + } + + template + CImg _matchpatch(const CImg& patch_image, + const unsigned int patch_width, + const unsigned int patch_height, + const unsigned int patch_depth, + const unsigned int nb_iterations, + const unsigned int nb_randoms, + const float patch_penalization, + const CImg &guide, + const bool is_matching_score, + CImg &matching_score) const { + if (is_empty()) return CImg::const_empty(); + if (patch_image._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) " + "have different spectrums.", + cimg_instance, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + if (patch_width>_width || patch_height>_height || patch_depth>_depth) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the instance image.", + cimg_instance,patch_width,patch_height,patch_depth); + if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions " + "of the patch image image (%u,%u,%u,%u,%p).", + cimg_instance,patch_width,patch_height,patch_depth, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + const unsigned int + _constraint = patch_image._depth>1?3:2, + constraint = guide._spectrum>_constraint?_constraint:0; + + if (guide && + (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint)) + throw CImgArgumentException(_cimg_instance + "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions " + "considering instance and patch image (%u,%u,%u,%u,%p).", + cimg_instance, + guide._width,guide._height,guide._depth,guide._spectrum,guide._data, + patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum, + patch_image._data); + + CImg a_map(_width,_height,_depth,patch_image._depth>1?3:2); + CImg is_updated(_width,_height,_depth,1,3); + CImg score(_width,_height,_depth); + CImg occ; + const float _patch_penalization = cimg::abs(patch_penalization); + const bool allow_identity = patch_penalization>=0; + if (_patch_penalization!=0) occ.assign(patch_image._width,patch_image._height,patch_image._depth,1,0); + const int + psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1, + psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1, + psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1; + + // Interleave image buffers to speed up patch comparison (cache-friendly). + CImg in_this = get_permute_axes("cxyz"); + in_this._width = _width*_spectrum; + in_this._height = _height; + in_this._depth = _depth; + in_this._spectrum = 1; + CImg in_patch = patch_image.get_permute_axes("cxyz"); + in_patch._width = patch_image._width*patch_image._spectrum; + in_patch._height = patch_image._height; + in_patch._depth = patch_image._depth; + in_patch._spectrum = 1; + + if (_depth>1 || patch_image._depth>1) { // 3D version + + // Initialize correspondence map. + if (guide) + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64)) + cimg_forXYZ(*this,x,y,z) { // User-defined initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for cimg_openmp_collapse(2)) + cimg_forXYZ(*this,x,y,z) { // Random initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } + cimg::srand(rng); + } + + // Start iteration loop. + cimg_abort_init; + for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && + iter0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor + u = a_map(x - 1,y,z,0); + v = a_map(x - 1,y,z,1); + w = a_map(x - 1,y,z,2); + if (u>=cx1 - 1 && u=cy1 && v=cz1 && w0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor + u = a_map(x,y - 1,z,0); + v = a_map(x,y - 1,z,1); + w = a_map(x,y - 1,z,2); + if (u>=cx1 && u=cy1 - 1 && v=cz1 && w0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor + u = a_map(x,y,z - 1,0); + v = a_map(x,y,z - 1,1); + w = a_map(x,y,z - 1,2); + if (u>=cx1 && u=cy1 && v=cz1 - 1 && w=cx1 + 1 && u=cy1 && v=cz1 && w=cx1 && u=cy1 + 1 && v=cz1 && w=cx1 && u=cy1 && v=cz1 + 1 && w::inf()); + } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) { + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + +#if cimg_use_openmp!=0 + rng+=omp_get_thread_num(); +#endif + cimg_pragma_openmp(for) + cimg_forXY(*this,x,y) { // Random initialization + const int + cx1 = x<=psizew1?x:(x::inf()); + } + cimg::srand(rng); + } + + // Start iteration loop. + cimg_abort_init; + for (unsigned int iter = 0; iter=(cimg_openmp_sizefactor)*64 && + iter0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor + u = a_map(x - 1,y,0); + v = a_map(x - 1,y,1); + if (u>=cx1 - 1 && u=cy1 && v0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor + u = a_map(x,y - 1,0); + v = a_map(x,y - 1,1); + if (u>=cx1 && u=cy1 - 1 && v=cx1 + 1 && u=cy1 && v=cx1 && u=cy1 + 1 && v& img1, const CImg& img2, const CImg& occ, + const unsigned int psizew, const unsigned int psizeh, + const unsigned int psized, const unsigned int psizec, + const int x1, const int y1, const int z1, + const int x2, const int y2, const int z2, + const int xc, const int yc, const int zc, + const float patch_penalization, + const bool allow_identity, + const float max_score) { // 3D version + if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2,(float)z1-z2)::inf(); + const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2); + const unsigned int psizewc = psizew*psizec; + const ulongT + offx1 = (ulongT)img1._width - psizewc, + offx2 = (ulongT)img2._width - psizewc, + offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width, + offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width; + float ssd = 0; + for (unsigned int k = 0; kmax_score) return max_score; + p1+=offx1; p2+=offx2; + } + p1+=offy1; p2+=offy2; + } + return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + + patch_penalization*psizewc*psizeh*psized*occ(xc,yc,zc)/100); + } + + static float _matchpatch(const CImg& img1, const CImg& img2, const CImg& occ, + const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec, + const int x1, const int y1, + const int x2, const int y2, + const int xc, const int yc, + const float patch_penalization, + const bool allow_identity, + const float max_score) { // 2D version + if (!allow_identity && cimg::hypot((float)x1-x2,(float)y1-y2)::inf(); + const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2); + const unsigned int psizewc = psizew*psizec; + const ulongT + offx1 = (ulongT)img1._width - psizewc, + offx2 = (ulongT)img2._width - psizewc; + float ssd = 0; + for (unsigned int j = 0; jmax_score) return max_score; + p1+=offx1; p2+=offx2; + } + return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) + + patch_penalization*psizewc*psizeh*occ(xc,yc)/100); + } + + //! Compute Euclidean distance function to a specified value. + /** + \param value Reference value. + \param metric Type of metric. Can be { 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }. + \note + The distance transform implementation has been submitted by A. Meijster, and implements + the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink, + "A general algorithm for computing distance transforms in linear time.", + In: Mathematical Morphology and its Applications to Image and Signal Processing, + J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.' + The submitted code has then been modified to fit CImg coding style and constraints. + **/ + CImg& distance(const T& value, const unsigned int metric=2) { + if (is_empty()) return *this; + if (cimg::type::string()!=pixel_type()) // For datatype < int + return CImg(*this,false).distance((Tint)value,metric). + cut((Tint)cimg::type::min(),(Tint)cimg::type::max()).move_to(*this); + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning) + if (!is_value) return fill(cimg::type::max()); + switch (metric) { + case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev + case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan + case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean + default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean + } + return *this; + } + + //! Compute distance to a specified value \newinstance. + CImg get_distance(const T& value, const unsigned int metric=2) const { + return CImg(*this,false).distance((Tfloat)value,metric); + } + + static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) { + return (u*u - i*i + g[u] - g[i])/(2*(u - i)); + } + + static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) { + return (x - i)*(x - i) + g[i]; + } + + static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) { + return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2); + } + + static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) { + return (x=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; } + if (q<0) { q = 0; s[0] = u; } + else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }} + } + for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan + } + + CImg& _distance_core(longT (*const sep)(const longT, const longT, const longT *const), + longT (*const f)(const longT, const longT, const longT *const)) { + // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why. +#define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9) + + const ulongT wh = (ulongT)_width*_height; +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) +#endif + cimg_forC(*this,c) { + CImg g(_width), dt(_width), s(_width), t(_width); + CImg img = get_shared_channel(c); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 && + _height*_depth>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forYZ(*this,y,z) { // Over X-direction + cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh); + _distance_scan(_width,g,sep,f,s,t,dt); + cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x]; + } + if (_height>1) { + g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forXZ(*this,x,z) { // Over Y-direction + cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh); + _distance_scan(_height,g,sep,f,s,t,dt); + cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y]; + } + } + if (_depth>1) { + g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth); +#if cimg_use_openmp!=0 && !cimg_is_gcc49x + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16) + firstprivate(g,dt,s,t)) +#endif + cimg_forXY(*this,x,y) { // Over Z-direction + cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh); + _distance_scan(_depth,g,sep,f,s,t,dt); + cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z]; + } + } + } + return *this; + } + + //! Compute chamfer distance to a specified value, with a custom metric. + /** + \param value Reference value. + \param metric_mask Metric mask. + \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé. + **/ + template + CImg& distance(const T& value, const CImg& metric_mask) { + if (is_empty()) return *this; + bool is_value = false; + cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999; + if (!is_value) return fill(cimg::type::max()); + const ulongT wh = (ulongT)_width*_height; + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2)) + cimg_forC(*this,c) { + CImg img = get_shared_channel(c); + cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) + cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024)) + cimg_forXYZ(metric_mask,dx,dy,dz) { + const t weight = metric_mask(dx,dy,dz); + if (weight) { + for (int z = dz, nz = 0; z=0; --z,--nz) { // Backward scan + for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) { + for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) { + const T dd = img(nx,ny,nz,0,wh) + weight; + if (dd + CImg get_distance(const T& value, const CImg& metric_mask) const { + return CImg(*this,false).distance(value,metric_mask); + } + + //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm). + /** + \param value Reference value. + \param metric Field of distance potentials. + \param is_high_connectivity Tells if the algorithm uses low or high connectivity. + \param[out] return_path An image containing the nodes of the minimal path. + **/ + template + CImg& distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) { + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this); + } + + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance. + template + CImg::type> + get_distance_dijkstra(const T& value, const CImg& metric, const bool is_high_connectivity, + CImg& return_path) const { + if (is_empty()) return return_path.assign(); + if (!is_sameXYZ(metric)) + throw CImgArgumentException(_cimg_instance + "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) " + "have incompatible dimensions.", + cimg_instance, + metric._width,metric._height,metric._depth,metric._spectrum); + typedef typename cimg::superset::type td; // Type used for computing cumulative distances + CImg result(_width,_height,_depth,_spectrum), Q; + CImg is_queued(_width,_height,_depth,1); + if (return_path) return_path.assign(_width,_height,_depth,_spectrum); + + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + CImg path = return_path?return_path.get_shared_channel(c):CImg(); + unsigned int sizeQ = 0; + + // Detect initial seeds. + is_queued.fill(0); + cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) { + Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z); + res(x,y,z) = 0; + if (path) path(x,y,z) = (to)0; + } + + // Start distance propagation. + while (sizeQ) { + + // Get and remove point with minimal potential from the queue. + const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3); + const td P = (td)-Q(0,0); + Q._priority_queue_remove(sizeQ); + + // Update neighbors. + td npot = 0; + if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) { + res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2; + } + if (x + 1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) { + res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8; + } + if (y + 1=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) { + res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32; + } + if (z + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) { + res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) { + res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9; + } + if (x - 1>=0 && y + 1=0) { // Diagonal neighbors on slice z - 1 + if (x - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) { + res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) { + res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40; + } + if (y + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)), + x - 1,y - 1,z - 1)) { + res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)), + x + 1,y - 1,z - 1)) { + res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41; + } + if (x - 1>=0 && y + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) { + res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) { + res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24; + } + if (y + 1=0 && y - 1>=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)), + x - 1,y - 1,z + 1)) { + res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26; + } + if (x + 1=0 && + Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)), + x + 1,y - 1,z + 1)) { + res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25; + } + if (x - 1>=0 && y + 1 + CImg& distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) { + return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this); + } + + //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance. + template + CImg get_distance_dijkstra(const T& value, const CImg& metric, + const bool is_high_connectivity=false) const { + CImg return_path; + return get_distance_dijkstra(value,metric,is_high_connectivity,return_path); + } + + //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). + /** + \param value Reference value. + \param metric Field of distance potentials. + **/ + template + CImg& distance_eikonal(const T& value, const CImg& metric) { + return get_distance_eikonal(value,metric).move_to(*this); + } + + //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm). + template + CImg get_distance_eikonal(const T& value, const CImg& metric) const { + if (is_empty()) return *this; + if (!is_sameXYZ(metric)) + throw CImgArgumentException(_cimg_instance + "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have " + "incompatible dimensions.", + cimg_instance, + metric._width,metric._height,metric._depth,metric._spectrum); + CImg result(_width,_height,_depth,_spectrum,cimg::type::max()), Q; + CImg state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen + + cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state)) + cimg_forC(*this,c) { + const CImg img = get_shared_channel(c); + const CImg met = metric.get_shared_channel(c%metric._spectrum); + CImg res = result.get_shared_channel(c); + unsigned int sizeQ = 0; + state.fill(-1); + + // Detect initial seeds. + Tfloat *ptr1 = res._data; char *ptr2 = state._data; + cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; } + + // Initialize seeds neighbors. + ptr2 = state._data; + cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) { + if (x - 1>=0 && state(x - 1,y,z)==-1) { + const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z); + } + if (x + 1=0 && state(x,y - 1,z)==-1) { + const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z); + } + if (y + 1=0 && state(x,y,z - 1)==-1) { + const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); + Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1); + } + if (z + 1=0) { + if (x - 1>=0 && state(x - 1,y,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z); + if (dist=0 && state(x,y - 1,z)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z); + if (dist=0 && state(x,y,z - 1)!=1) { + const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1); + if (dist& res, const Tfloat P, + const int x=0, const int y=0, const int z=0) const { + const Tfloat M = (Tfloat)cimg::type::max(); + T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 11) { // 3D + T + T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1=0?res(x,y,z - 1):M,z + 1T2) cimg::swap(T1,T2); + if (T2>T3) cimg::swap(T2,T3); + if (T1>T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T31) { // 2D + T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1T2) cimg::swap(T1,T2); + if (P<=0) return (Tfloat)T1; + if (T2 + void _eik_priority_queue_insert(CImg& state, unsigned int& siz, const t value, + const unsigned int x, const unsigned int y, const unsigned int z) { + if (state(x,y,z)>0) return; + state(x,y,z) = 0; + if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); } + (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z; + for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) { + cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1)); + cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3)); + } + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE. + /** + \param nb_iterations Number of PDE iterations. + \param band_size Size of the narrow band. + \param time_step Time step of the PDE iterations. + **/ + CImg& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) { + if (is_empty()) return *this; + CImg velocity(*this,false); + for (unsigned int iteration = 0; iteration1) { // 3D + CImg_3x3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)0?(Incc - Iccc):(Iccc - Ipcc), + iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc), + iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp), + ng = 1e-5f + cimg::hypot(gx,gy,gz), + ngx = gx/ng, + ngy = gy/ng, + ngz = gz/ng, + veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } else { // 2D version + CImg_3x3(I,Tfloat); + cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)0?(Inc - Icc):(Icc - Ipc), + iy = gy*sgn>0?(Icn - Icc):(Icc - Icp), + ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)), + ngx = gx/ng, + ngy = gy/ng, + veloc = sgn*(ngx*ix + ngy*iy - 1); + *(ptrd++) = veloc; + if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc; + } else *(ptrd++) = 0; + } + if (veloc_max>0) *this+=(velocity*=time_step/veloc_max); + } + return *this; + } + + //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance. + CImg get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, + const float time_step=0.5f) const { + return CImg(*this,false).distance_eikonal(nb_iterations,band_size,time_step); + } + + //! Compute Haar multiscale wavelet transform. + /** + \param axis Axis considered for the transform. + \param invert Set inverse of direct transform. + \param nb_scales Number of scales used for the transform. + **/ + CImg& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(axis,invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const { + if (is_empty() || !nb_scales) return +*this; + CImg res; + const Tfloat sqrt2 = std::sqrt(2.f); + if (nb_scales==1) { + switch (cimg::lowercase(axis)) { // Single scale transform + case 'x' : { + const unsigned int w = _width/2; + if (w) { + if ((w%2) && w!=1) + throw CImgInstanceException(_cimg_instance + "haar(): Sub-image width %u is not even.", + cimg_instance, + w); + + res.assign(_width,_height,_depth,_spectrum); + if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X + for (unsigned int x = 0, xw = w, x2 = 0; x& haar(const bool invert=false, const unsigned int nb_scales=1) { + return get_haar(invert,nb_scales).move_to(*this); + } + + //! Compute Haar multiscale wavelet transform \newinstance. + CImg get_haar(const bool invert=false, const unsigned int nb_scales=1) const { + CImg res; + if (nb_scales==1) { // Single scale transform + if (_width>1) get_haar('x',invert,1).move_to(res); + if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); } + if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); } + if (res) return res; + } else { // Multi-scale transform + if (invert) { // Inverse transform + res.assign(*this,false); + if (_width>1) { + if (_height>1) { + if (_depth>1) { + unsigned int w = _width, h = _height, d = _depth; + for (unsigned int s = 1; w && h && d && s1) { + unsigned int w = _width, d = _depth; + for (unsigned int s = 1; w && d && s1) { + if (_depth>1) { + unsigned int h = _height, d = _depth; + for (unsigned int s = 1; h && d && s1) { + unsigned int d = _depth; + for (unsigned int s = 1; d && s1) { + if (_height>1) { + if (_depth>1) + for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s1) { + if (_depth>1) + for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s1) for (unsigned int s = 1, d = _depth/2; d && s get_FFT(const char axis, const bool is_inverse=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],axis,is_inverse); + return res; + } + + //! Compute n-D Fast Fourier Transform. + /* + \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + CImgList get_FFT(const bool is_inverse=false) const { + CImgList res(*this,CImg()); + CImg::FFT(res[0],res[1],is_inverse); + return res; + } + + //! Compute 1D Fast Fourier Transform, along a specified axis. + /** + \param[in,out] real Real part of the pixel values. + \param[in,out] imag Imaginary part of the pixel values. + \param axis Axis along which the FFT is computed. + \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed. + **/ + static void FFT(CImg& real, CImg& imag, const char axis, const bool is_inverse=false, + const unsigned int nb_threads=0) { + if (!real) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.", + pixel_type()); + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " + "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + const char _axis = cimg::lowercase(axis); + if (_axis!='x' && _axis!='y' && _axis!='z') + throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts " + "(%u,%u,%u,%u) " + "(should be { x | y | z }).", + pixel_type(),axis, + real._width,real._height,real._depth,real._spectrum); + cimg::unused(nb_threads); +#ifdef cimg_use_fftw3 + cimg::mutex(12); +#ifndef cimg_use_fftw3_singlethread + fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus()); +#endif + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) + throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " + "for computing FFT of image (%u,%u,%u,%u) along the X-axis.", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth), + real._width,real._height,real._depth,real._spectrum); + double *const ptrf = (double*)data_in; + fftw_plan data_plan = + _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(), + data_in,0,1,real.width(), + data_in,0,1,real.width(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(), + data_in,0,1,real.height(), + data_in,0,1,real.height(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(), + data_in,0,1,real.depth(), + data_in,0,1,real.depth(), + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + CImg realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c); + switch (_axis) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + break; + case 'y' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + break; + default : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = realc.offset(x,y,z), + j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth); + ptrf[j] = (double)realc[i]; + ptrf[j + 1] = (double)imagc[i]; + } + } + + fftw_execute(data_plan); + + const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0; + switch (_axis) { + case 'x' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + break; + case 'y' : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + break; + default : + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_forXYZ(realc,x,y,z) { + const ulongT + i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth), + j = realc.offset(x,y,z); + realc[j] = (T)(a*ptrf[i]); + imagc[j] = (T)(a*ptrf[i + 1]); + } + } + } + + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#ifndef cimg_use_fftw3_singlethread + fftw_cleanup_threads(); +#endif + cimg::mutex(12,0); +#else + switch (_axis) { + case 'x' : { // Fourier along X, using built-in functions + const unsigned int N = real._width, N2 = N>>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the X-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forYZC(real,y,z,c) { + cimg::swap(real(i,y,z,c),real(j,y,z,c)); + cimg::swap(imag(i,y,z,c),imag(j,y,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = delta>>1; + for (unsigned int i = 0; i>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the Y-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXZC(real,x,z,c) { + cimg::swap(real(x,i,z,c),real(x,j,z,c)); + cimg::swap(imag(x,i,z,c),imag(x,j,z,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i>1; + if (((N - 1)&N) && N!=1) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) " + "have non 2^N dimension along the Z-axis.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum); + + for (unsigned int i = 0, j = 0; ii) cimg_forXYC(real,x,y,c) { + cimg::swap(real(x,y,i,c),real(x,y,j,c)); + cimg::swap(imag(x,y,i,c),imag(x,y,j,c)); + if (j=m; j-=m, m = n, n>>=1) {} + } + for (unsigned int delta = 2; delta<=N; delta<<=1) { + const unsigned int delta2 = (delta>>1); + for (unsigned int i = 0; i& real, CImg& imag, const bool is_inverse=false, + const unsigned int nb_threads=0) { + if (!real) + throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.", + pixel_type()); + if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0); + if (!real.is_sameXYZC(imag)) + throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and " + "imaginary part (%u,%u,%u,%u,%p) have different dimensions.", + pixel_type(), + real._width,real._height,real._depth,real._spectrum,real._data, + imag._width,imag._height,imag._depth,imag._spectrum,imag._data); + cimg::unused(nb_threads); +#ifdef cimg_use_fftw3 + cimg::mutex(12); +#ifndef cimg_use_fftw3_singlethread + fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus()); +#endif + fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth); + if (!data_in) + throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) " + "for computing FFT of image (%u,%u,%u,%u).", + pixel_type(), + cimg::strbuffersize(sizeof(fftw_complex)*real._width* + real._height*real._depth*real._spectrum), + real._width,real._height,real._depth,real._spectrum); + double *const ptrf = (double*)data_in; + fftw_plan data_plan = + real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE): + fftw_plan_dft_1d(real._width,data_in,data_in, + is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE); + cimg_forC(real,c) { + CImg realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; } + fftw_execute(data_plan); + if (is_inverse) { + const double a = 1.0/(real.width()*real.height()*real.depth()); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); } + } else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000)) + cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; } + } + fftw_destroy_plan(data_plan); + fftw_free(data_in); +#ifndef cimg_use_fftw3_singlethread + fftw_cleanup_threads(); +#endif + cimg::mutex(12,0); +#else + if (real._depth>1) FFT(real,imag,'z',is_inverse); + if (real._height>1) FFT(real,imag,'y',is_inverse); + if (real._width>1) FFT(real,imag,'x',is_inverse); +#endif + } + + //@} + //------------------------------------- + // + //! \name 3D Objects Management + //@{ + //------------------------------------- + + //! Rotate 3D object's vertices. + /** + \param x X-coordinate of the rotation axis, or first quaternion coordinate. + \param y Y-coordinate of the rotation axis, or second quaternion coordinate. + \param z Z-coordinate of the rotation axis, or second quaternion coordinate. + \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate. + \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w). + **/ + CImg& rotate_object3d(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) { + return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this); + } + + CImg get_rotate_object3d(const float x, const float y, const float z, const float w, + const bool is_quaternion=false) const { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "rotate_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + return CImg::rotation_matrix(x,y,z,w,is_quaternion)**this; + } + + //! Shift 3D object's vertices. + /** + \param tx X-coordinate of the 3D displacement vector. + \param ty Y-coordinate of the 3D displacement vector. + \param tz Z-coordinate of the 3D displacement vector. + **/ + CImg& shift_object3d(const float tx, const float ty=0, const float tz=0) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz; + return *this; + } + + //! Shift 3D object's vertices \newinstance. + CImg get_shift_object3d(const float tx, const float ty=0, const float tz=0) const { + return CImg(*this,false).shift_object3d(tx,ty,tz); + } + + //! Shift 3D object's vertices, so that it becomes centered. + /** + \note The object center is computed as its barycenter. + **/ + CImg& shift_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "shift_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2; + return *this; + } + + //! Shift 3D object's vertices, so that it becomes centered \newinstance. + CImg get_shift_object3d() const { + return CImg(*this,false).shift_object3d(); + } + + //! Resize 3D object. + /** + \param sx Width of the 3D object's bounding box. + \param sy Height of the 3D object's bounding box. + \param sz Depth of the 3D object's bounding box. + **/ + CImg& resize_object3d(const float sx, const float sy=-100, const float sz=-100) { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + if (xm0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; } + if (ym0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; } + if (zm0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; } + return *this; + } + + //! Resize 3D object \newinstance. + CImg get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const { + return CImg(*this,false).resize_object3d(sx,sy,sz); + } + + //! Resize 3D object to unit size. + CImg resize_object3d() { + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "resize_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + CImg xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2); + float + xm, xM = (float)xcoords.max_min(xm), + ym, yM = (float)ycoords.max_min(ym), + zm, zM = (float)zcoords.max_min(zm); + const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz); + if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; } + return *this; + } + + //! Resize 3D object to unit size \newinstance. + CImg get_resize_object3d() const { + return CImg(*this,false).resize_object3d(); + } + + //! Merge two 3D objects together. + /** + \param[in,out] primitives Primitives data of the current 3D object. + \param obj_vertices Vertices data of the additional 3D object. + \param obj_primitives Primitives data of the additional 3D object. + **/ + template + CImg& append_object3d(CImgList& primitives, const CImg& obj_vertices, + const CImgList& obj_primitives) { + if (!obj_vertices || !obj_primitives) return *this; + if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a " + "set of 3D vertices.", + cimg_instance, + obj_vertices._width,obj_vertices._height, + obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data); + + if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); } + if (_height!=3 || _depth>1 || _spectrum>1) + throw CImgInstanceException(_cimg_instance + "append_object3d(): Instance is not a set of 3D vertices.", + cimg_instance); + + const unsigned int P = _width; + append(obj_vertices,'x'); + const unsigned int N = primitives._width; + primitives.insert(obj_primitives); + for (unsigned int i = N; i &p = primitives[i]; + switch (p.size()) { + case 1 : p[0]+=P; break; // Point + case 5 : p[0]+=P; p[1]+=P; break; // Sphere + case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment + case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle + case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle + } + } + return *this; + } + + //! Texturize primitives of a 3D object. + /** + \param[in,out] primitives Primitives data of the 3D object. + \param[in,out] colors Colors data of the 3D object. + \param texture Texture image to map to 3D object. + \param coords Texture-mapping coordinates. + **/ + template + const CImg& texturize_object3d(CImgList& primitives, CImgList& colors, + const CImg& texture, const CImg& coords=CImg::const_empty()) const { + if (is_empty()) return *this; + if (_height!=3) + throw CImgInstanceException(_cimg_instance + "texturize_object3d(): image instance is not a set of 3D points.", + cimg_instance); + if (coords && (coords._width!=_width || coords._height!=2)) + throw CImgArgumentException(_cimg_instance + "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).", + cimg_instance, + coords._width,coords._height,coords._depth,coords._spectrum,coords._data); + CImg _coords; + if (!coords) { // If no texture coordinates specified, do a default XY-projection + _coords.assign(_width,2); + float + xmin, xmax = (float)get_shared_row(0).max_min(xmin), + ymin, ymax = (float)get_shared_row(1).max_min(ymin), + dx = xmax>xmin?xmax-xmin:1, + dy = ymax>ymin?ymax-ymin:1; + cimg_forX(*this,p) { + _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx); + _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy); + } + } else _coords = coords; + + int texture_ind = -1; + cimglist_for(primitives,l) { + CImg &p = primitives[l]; + const unsigned int siz = p.size(); + switch (siz) { + case 1 : { // Point + const unsigned int i0 = (unsigned int)p[0]; + const int x0 = _coords(i0,0), y0 = _coords(i0,1); + texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, + y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]); + } break; + case 2 : case 6 : { // Line + const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,x0,y0,x1,y1).move_to(p); + } break; + case 3 : case 9 : { // Triangle + const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1), + x2 = _coords(i2,0), y2 = _coords(i2,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p); + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3]; + const int + x0 = _coords(i0,0), y0 = _coords(i0,1), + x1 = _coords(i1,0), y1 = _coords(i1,1), + x2 = _coords(i2,0), y2 = _coords(i2,1), + x3 = _coords(i3,0), y3 = _coords(i3,1); + if (texture_ind<0) colors[texture_ind=l].assign(texture,false); + else colors[l].assign(colors[texture_ind],true); + CImg::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p); + } break; + } + } + return *this; + } + + //! Generate a 3D elevation of the image instance. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param[out] colors The returned list of the 3D object colors. + \param elevation The input elevation map. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + CImgList colors3d; + const CImg points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2); + CImg().display_object3d("Elevation3d",points3d,faces3d,colors3d); + \endcode + \image html ref_elevation3d.jpg + **/ + template + CImg get_elevation3d(CImgList& primitives, CImgList& colors, const CImg& elevation) const { + if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1) + throw CImgArgumentException(_cimg_instance + "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) " + "have incompatible dimensions.", + cimg_instance, + elevation._width,elevation._height,elevation._depth, + elevation._spectrum,elevation._data); + if (is_empty()) return *this; + float m, M = (float)max_min(m); + if (M==m) ++M; + colors.assign(); + const unsigned int size_x1 = _width - 1, size_y1 = _height - 1; + for (unsigned int y = 0; y1?((*this)(x,y,1) - m)*255/(M-m):r), + b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r); + CImg::vector((tc)r,(tc)g,(tc)b).move_to(colors); + } + const typename CImg::_functor2d_int func(elevation); + return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height); + } + + //! Generate the 3D projection planes of the image instance. + /** + \param[out] primitives Primitives data of the returned 3D object. + \param[out] colors Colors data of the returned 3D object. + \param x0 X-coordinate of the projection point. + \param y0 Y-coordinate of the projection point. + \param z0 Z-coordinate of the projection point. + \param normalize_colors Tells if the created textures have normalized colors. + **/ + template + CImg get_projections3d(CImgList& primitives, CImgList& colors, + const unsigned int x0, const unsigned int y0, const unsigned int z0, + const bool normalize_colors=false) const { + float m = 0, M = 0, delta = 1; + if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); } + const unsigned int + _x0 = (x0>=_width)?_width - 1:x0, + _y0 = (y0>=_height)?_height - 1:y0, + _z0 = (z0>=_depth)?_depth - 1:z0; + CImg img_xy, img_xz, img_yz; + if (normalize_colors) { + ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy); + ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1). + move_to(img_xz); + ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1). + move_to(img_yz); + } else { + get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy); + get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz); + get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz); + } + CImg points(12,3,1,1, + 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0, + 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0, + _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1); + primitives.assign(); + CImg::vector(0,1,2,3,0,0,img_xy._width - 1,0,img_xy._width - 1,img_xy._height - 1,0,img_xy._height - 1). + move_to(primitives); + CImg::vector(4,5,6,7,0,0,img_xz._width - 1,0,img_xz._width - 1,img_xz._height - 1,0,img_xz._height - 1). + move_to(primitives); + CImg::vector(8,9,10,11,0,0,img_yz._width - 1,0,img_yz._width - 1,img_yz._height - 1,0,img_yz._height - 1). + move_to(primitives); + colors.assign(); + img_xy.move_to(colors); + img_xz.move_to(colors); + img_yz.move_to(colors); + return points; + } + + //! Generate a isoline of the image instance as a 3D object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3D object colors. + \param size_x The number of subdivisions along the X-axis. + \param size_y The number of subdisivions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img("reference.jpg"); + CImgList faces3d; + const CImg points3d = img.get_isoline3d(faces3d,100); + CImg().display_object3d("Isoline3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isoline3d.jpg + **/ + template + CImg get_isoline3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a scalar image.", + cimg_instance); + if (_depth>1) + throw CImgInstanceException(_cimg_instance + "get_isoline3d(): Instance is not a 2D image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) { + const _functor2d_int func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height()); + } else { + const _functor2d_float func(*this); + vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y); + } + return vertices; + } + + //! Compute isolines of a function, as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Elevation functor. Must have operator()(x,y) defined. + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static CImg isoline3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + CImgList vertices; + primitives.assign(); + typename CImg::_functor_isoline3d add_vertex(vertices); + typename CImg::_functor_isoline3d add_segment(primitives); + isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y); + return vertices>'x'; + } + + //! Compute isolines of a function, as a 3D object. + /** + \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex. + \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param isovalue Isovalue to extract from function. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + \note Use the marching squares algorithm for extracting the isolines. + **/ + template + static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x, const int size_y) { + static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, + 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; + static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, + { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, + { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, + { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; + const unsigned int + _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nxm1 = nx - 1, + nym1 = ny - 1; + + if (!nxm1 || !nym1) return; + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1; + CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); + CImg values1(nx), values2(nx); + float X = x0, Y = y0, nX = X + dx, nY = Y + dy; + int nb_vertices = 0; + + // Fill first line with values + cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; } + + // Run the marching squares algorithm + for (unsigned int yi = 0, nyi = 1; yi + static CImg isoline3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y); + } + + template + static int _isoline3d_index(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int nx) { + switch (edge) { + case 0 : return (int)indices1(x,0); + case 1 : return (int)indices1(nx,1); + case 2 : return (int)indices2(x,0); + case 3 : return (int)indices1(x,1); + } + return 0; + } + + //! Generate an isosurface of the image instance as a 3D object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param isovalue The returned list of the 3D object colors. + \param size_x Number of subdivisions along the X-axis. + \param size_y Number of subdisivions along the Y-axis. + \param size_z Number of subdisivions along the Z-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + const CImg img = CImg("reference.jpg").resize(-100,-100,20); + CImgList faces3d; + const CImg points3d = img.get_isosurface3d(faces3d,100); + CImg().display_object3d("Isosurface3d",points3d,faces3d,colors3d); + \endcode + \image html ref_isosurface3d.jpg + **/ + template + CImg get_isosurface3d(CImgList& primitives, const float isovalue, + const int size_x=-100, const int size_y=-100, const int size_z=-100) const { + if (_spectrum>1) + throw CImgInstanceException(_cimg_instance + "get_isosurface3d(): Instance is not a scalar image.", + cimg_instance); + primitives.assign(); + if (is_empty()) return *this; + CImg vertices; + if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) { + const _functor3d_int func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, + width(),height(),depth()); + } else { + const _functor3d_float func(*this); + vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f, + size_x,size_y,size_z); + } + return vertices; + } + + //! Compute isosurface of a function, as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static CImg isosurface3d(CImgList& primitives, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x=32, const int size_y=32, const int size_z=32) { + CImgList vertices; + primitives.assign(); + typename CImg::_functor_isosurface3d add_vertex(vertices); + typename CImg::_functor_isosurface3d add_triangle(primitives); + isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z); + return vertices>'x'; + } + + //! Compute isosurface of a function, as a 3D object. + /** + \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex. + \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment. + \param func Implicit function. Is of type float (*func)(const float x, const float y, const float z). + \param isovalue Isovalue to extract. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param size_x Resolution of the elevation function along the X-axis. + \param size_y Resolution of the elevation function along the Y-axis. + \param size_z Resolution of the elevation function along the Z-axis. + \note Use the marching cubes algorithm for extracting the isosurface. + **/ + template + static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int size_x, const int size_y, const int size_z) { + static const unsigned int edges[256] = { + 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 + }; + + static const int triangles[256][16] = { + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 }, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 }, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 }, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 }, + { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 }, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, + { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 }, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, + { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, + { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, + { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 }, + { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, + { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 }, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, + { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 }, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 }, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, + { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 }, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 }, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, + { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 }, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, + { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 }, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, + { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 }, + { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 }, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 }, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, + { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 }, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, + { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 }, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 }, + { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, + { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 }, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, + { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 }, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, + { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 }, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, + { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 }, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 }, + { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 }, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, + { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 }, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 }, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, + { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 }, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, + { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, + { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, + { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 }, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 }, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, + { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 }, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 }, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, + { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 }, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, + { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 }, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, + { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 }, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 }, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, + { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, + { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 }, + { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, + { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 }, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, + { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, + { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, + { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 }, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 }, + { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 }, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, + { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 }, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 }, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, + { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 }, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 }, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, + { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 }, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, + { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 }, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, + { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, + { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, + { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 }, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, + { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 }, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, + { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 }, + { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, + { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 }, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, + { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 }, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, + { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 }, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 }, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, + { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } + }; + + const unsigned int + _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)), + _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)), + _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)), + nx = _nx?_nx:1, + ny = _ny?_ny:1, + nz = _nz?_nz:1, + nxm1 = nx - 1, + nym1 = ny - 1, + nzm1 = nz - 1; + if (!nxm1 || !nym1 || !nzm1) return; + const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1; + CImg indices1(nx,ny,1,3,-1), indices2(indices1); + CImg values1(nx,ny), values2(nx,ny); + float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; + int nb_vertices = 0; + + // Fill the first plane with function values + Y = y0; + cimg_forY(values1,y) { + X = x0; + cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; } + Y+=dy; + } + + // Run Marching Cubes algorithm + Z = z0; nZ = Z + dz; + for (unsigned int zi = 0; zi + static CImg isosurface3d(CImgList& primitives, const char *const expression, const float isovalue, + const float x0, const float y0, const float z0, + const float x1, const float y1, const float z1, + const int dx=32, const int dy=32, const int dz=32) { + const _functor3d_expr func(expression); + return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz); + } + + template + static int _isosurface3d_index(const unsigned int edge, const CImg& indices1, const CImg& indices2, + const unsigned int x, const unsigned int y, + const unsigned int nx, const unsigned int ny) { + switch (edge) { + case 0 : return indices1(x,y,0); + case 1 : return indices1(nx,y,1); + case 2 : return indices1(x,ny,0); + case 3 : return indices1(x,y,1); + case 4 : return indices2(x,y,0); + case 5 : return indices2(nx,y,1); + case 6 : return indices2(x,ny,0); + case 7 : return indices2(x,y,1); + case 8 : return indices1(x,y,2); + case 9 : return indices1(nx,y,2); + case 10 : return indices1(nx,ny,2); + case 11 : return indices1(x,ny,2); + } + return 0; + } + + // Define functors for accessing image values (used in previous functions). + struct _functor2d_int { + const CImg& ref; + _functor2d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref((int)x,(int)y); + } + }; + + struct _functor2d_float { + const CImg& ref; + _functor2d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y) const { + return (float)ref._linear_atXY(x,y); + } + }; + + struct _functor2d_expr { + _cimg_math_parser *mp; + ~_functor2d_expr() { mp->end(); delete mp; } + _functor2d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } + float operator()(const float x, const float y) const { + return (float)(*mp)(x,y,0,0); + } + }; + + struct _functor3d_int { + const CImg& ref; + _functor3d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref((int)x,(int)y,(int)z); + } + }; + + struct _functor3d_float { + const CImg& ref; + _functor3d_float(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z) const { + return (float)ref._linear_atXYZ(x,y,z); + } + }; + + struct _functor3d_expr { + _cimg_math_parser *mp; + ~_functor3d_expr() { mp->end(); delete mp; } + _functor3d_expr(const char *const expr):mp(0) { + mp = new _cimg_math_parser(expr,0,CImg::const_empty(),0); + } + float operator()(const float x, const float y, const float z) const { + return (float)(*mp)(x,y,z,0); + } + }; + + struct _functor4d_int { + const CImg& ref; + _functor4d_int(const CImg& pref):ref(pref) {} + float operator()(const float x, const float y, const float z, const unsigned int c) const { + return (float)ref((int)x,(int)y,(int)z,c); + } + }; + + struct _functor_isoline3d { + CImgList& list; + _functor_isoline3d(CImgList& _list):list(_list) {} + template + void operator()(const t x, const t y, const t z) { CImg::vector((T)x,(T)y,(T)z).move_to(list); } + template + void operator()(const t i, const t j) { CImg::vector((T)i,(T)j).move_to(list); } + }; + + struct _functor_isosurface3d { + CImgList& list; + _functor_isosurface3d(CImgList& _list):list(_list) {} + template + void operator()(const t x, const t y, const t z) { CImg::vector((T)x,(T)y,(T)z).move_to(list); } + }; + + //! Compute 3D elevation of a function as a 3D object. + /** + \param[out] primitives Primitives data of the resulting 3D object. + \param func Elevation function. Is of type float (*func)(const float x,const float y). + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param size_x Resolution of the function along the X-axis. + \param size_y Resolution of the function along the Y-axis. + **/ + template + static CImg elevation3d(CImgList& primitives, const tfunc& func, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const float + nx0 = x0=0?size_x:(nx1-nx0)*-size_x/100), + nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1, + _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), + nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1; + if (nsize_x<2 || nsize_y<2) + throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).", + pixel_type(), + nsize_x,nsize_y); + + CImg vertices(nsize_x*nsize_y,3); + floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2); + for (unsigned int y = 0; y + static CImg elevation3d(CImgList& primitives, const char *const expression, + const float x0, const float y0, const float x1, const float y1, + const int size_x=256, const int size_y=256) { + const _functor2d_expr func(expression); + return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y); + } + + //! Generate a 3D box object. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the box (dimension along the X-axis). + \param size_y The height of the box (dimension along the Y-axis). + \param size_z The depth of the box (dimension along the Z-axis). + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::box3d(faces3d,10,20,30); + CImg().display_object3d("Box3d",points3d,faces3d); + \endcode + \image html ref_box3d.jpg + **/ + template + static CImg box3d(CImgList& primitives, + const float size_x=200, const float size_y=100, const float size_z=100) { + primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5); + return CImg(8,3,1,1, + 0.,size_x,size_x, 0., 0.,size_x,size_x, 0., + 0., 0.,size_y,size_y, 0., 0.,size_y,size_y, + 0., 0., 0., 0.,size_z,size_z,size_z,size_z); + } + + //! Generate a 3D cone. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cone basis. + \param size_z The cone's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cone3d(faces3d,50); + CImg().display_object3d("Cone3d",points3d,faces3d); + \endcode + \image html ref_cone3d.jpg + **/ + template + static CImg cone3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,size_z, + 0.,0.,0.); + for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices); + } + const unsigned int nbr = vertices._width - 2; + for (unsigned int p = 0; p::vector(1,next,curr).move_to(primitives); + CImg::vector(0,curr,next).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D cylinder. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the cylinder basis. + \param size_z The cylinder's height. + \param subdivisions The number of basis angular subdivisions. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::cylinder3d(faces3d,50); + CImg().display_object3d("Cylinder3d",points3d,faces3d); + \endcode + \image html ref_cylinder3d.jpg + **/ + template + static CImg cylinder3d(CImgList& primitives, + const float radius=50, const float size_z=100, const unsigned int subdivisions=24) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImgList vertices(2,1,3,1,1, + 0.,0.,0., + 0.,0.,size_z); + for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) { + const float a = (float)(angle*cimg::PI/180); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices); + CImg::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices); + } + const unsigned int nbr = (vertices._width - 2)/2; + for (unsigned int p = 0; p::vector(0,next,curr).move_to(primitives); + CImg::vector(1,curr + 1,next + 1).move_to(primitives); + CImg::vector(curr,next,next + 1,curr + 1).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D torus. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius1 The large radius. + \param radius2 The small radius. + \param subdivisions1 The number of angular subdivisions for the large radius. + \param subdivisions2 The number of angular subdivisions for the small radius. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::torus3d(faces3d,20,4); + CImg().display_object3d("Torus3d",points3d,faces3d); + \endcode + \image html ref_torus3d.jpg + **/ + template + static CImg torus3d(CImgList& primitives, + const float radius1=100, const float radius2=30, + const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) { + primitives.assign(); + if (!subdivisions1 || !subdivisions2) return CImg(); + CImgList vertices; + for (unsigned int v = 0; v::vector(x,y,z).move_to(vertices); + } + } + for (unsigned int vv = 0; vv::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives); + } + } + return vertices>'x'; + } + + //! Generate a 3D XY-plane. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param size_x The width of the plane (dimension along the X-axis). + \param size_y The height of the plane (dimensions along the Y-axis). + \param subdivisions_x The number of planar subdivisions along the X-axis. + \param subdivisions_y The number of planar subdivisions along the Y-axis. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::plane3d(faces3d,100,50); + CImg().display_object3d("Plane3d",points3d,faces3d); + \endcode + \image html ref_plane3d.jpg + **/ + template + static CImg plane3d(CImgList& primitives, + const float size_x=100, const float size_y=100, + const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) { + primitives.assign(); + if (!subdivisions_x || !subdivisions_y) return CImg(); + CImgList vertices; + const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1; + const float fx = (float)size_x/w, fy = (float)size_y/h; + for (unsigned int y = 0; y::vector(fx*x,fy*y,0).move_to(vertices); + for (unsigned int y = 0; y::vector(off1,off4,off3,off2).move_to(primitives); + } + return vertices>'x'; + } + + //! Generate a 3D sphere. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param radius The radius of the sphere (dimension along the X-axis). + \param subdivisions The number of recursive subdivisions from an initial icosahedron. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg points3d = CImg::sphere3d(faces3d,100,4); + CImg().display_object3d("Sphere3d",points3d,faces3d); + \endcode + \image html ref_sphere3d.jpg + **/ + template + static CImg sphere3d(CImgList& primitives, + const float radius=50, const unsigned int subdivisions=3) { + + // Create initial icosahedron + primitives.assign(); + const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a; + CImgList vertices(12,1,3,1,1, b,a,0., -b,a,0., -b,-a,0., b,-a,0., a,0.,b, a,0.,-b, + -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a); + primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6, + 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3, + 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2); + // edge - length/2 + float he = (float)a; + + // Recurse subdivisions + for (unsigned int i = 0; i::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; } + if (i1<0) { CImg::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; } + if (i2<0) { CImg::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; } + primitives.remove(0); + CImg::vector(p0,i0,i1).move_to(primitives); + CImg::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives); + CImg::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives); + CImg::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives); + } + } + return (vertices>'x')*=radius; + } + + //! Generate a 3D ellipsoid. + /** + \param[out] primitives The returned list of the 3D object primitives + (template type \e tf should be at least \e unsigned \e int). + \param tensor The tensor which gives the shape and size of the ellipsoid. + \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron. + \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg image (0<=i<=N - 1). + \par Example + \code + CImgList faces3d; + const CImg tensor = CImg::diagonal(10,7,3), + points3d = CImg::ellipsoid3d(faces3d,tensor,4); + CImg().display_object3d("Ellipsoid3d",points3d,faces3d); + \endcode + \image html ref_ellipsoid3d.jpg + **/ + template + static CImg ellipsoid3d(CImgList& primitives, + const CImg& tensor, const unsigned int subdivisions=3) { + primitives.assign(); + if (!subdivisions) return CImg(); + CImg S, V; + tensor.symmetric_eigen(S,V); + const float orient = + (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) + + (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) + + (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2); + if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); } + const float l0 = S[0], l1 = S[1], l2 = S[2]; + CImg vertices = sphere3d(primitives,1.,subdivisions); + vertices.get_shared_row(0)*=l0; + vertices.get_shared_row(1)*=l1; + vertices.get_shared_row(2)*=l2; + return V*vertices; + } + + //! Convert 3D object into a CImg3d representation. + /** + \param primitives Primitives data of the 3D object. + \param colors Colors data of the 3D object. + \param opacities Opacities data of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + **/ + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg& object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) { + return get_object3dtoCImg3d(primitives,full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \overloading. + CImg& object3dtoCImg3d(const bool full_check=true) { + return get_object3dtoCImg3d(full_check).move_to(*this); + } + + //! Convert 3D object into a CImg3d representation \newinstance. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool full_check=true) const { + CImg error_message(1024); + if (!is_object3d(primitives,colors,opacities,full_check,error_message)) + throw CImgInstanceException(_cimg_instance + "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,_width,primitives._width,error_message.data()); + CImg res(1,_size_object3dtoCImg3d(primitives,colors,opacities)); + float *ptrd = res._data; + + // Put magick number. + *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f; + *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f; + + // Put number of vertices and primitives. + *(ptrd++) = cimg::uint2float(_width); + *(ptrd++) = cimg::uint2float(primitives._width); + + // Put vertex data. + if (is_empty() || !primitives) return res; + const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2); + cimg_forX(*this,p) { + *(ptrd++) = (float)*(ptrx++); + *(ptrd++) = (float)*(ptry++); + *(ptrd++) = (float)*(ptrz++); + } + + // Put primitive data. + cimglist_for(primitives,p) { + *(ptrd++) = (float)primitives[p].size(); + const tp *ptrp = primitives[p]._data; + cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++)); + } + + // Put color/texture data. + const unsigned int csiz = std::min(colors._width,primitives._width); + for (int c = 0; c<(int)csiz; ++c) { + const CImg& color = colors[c]; + const tc *ptrc = color._data; + if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; } + else { + *(ptrd++) = -128.f; + int shared_ind = -1; + if (color.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImgList& opacities, float *ptrd) const { + cimglist_for(opacities,o) { + const CImg& opacity = opacities[o]; + const to *ptro = opacity._data; + if (opacity.size()==1) *(ptrd++) = (float)*ptro; + else { + *(ptrd++) = -128.f; + int shared_ind = -1; + if (opacity.is_shared()) for (int i = 0; i + float* _object3dtoCImg3d(const CImg& opacities, float *ptrd) const { + const to *ptro = opacities._data; + cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++); + return ptrd; + } + + template + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImgList& opacities) const { + unsigned int siz = 8U + 3*_width; + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { + if (colors[c].is_shared()) siz+=4; + else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; } + } + if (colors._width + unsigned int _size_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const CImg& opacities) const { + unsigned int siz = 8U + 3*_width; + cimglist_for(primitives,p) siz+=primitives[p].size() + 1; + for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) { + const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; + } + if (colors._width + CImg get_object3dtoCImg3d(const CImgList& primitives, + const CImgList& colors, + const bool full_check=true) const { + CImgList opacities; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert 3D object into a CImg3d representation \overloading. + template + CImg get_object3dtoCImg3d(const CImgList& primitives, + const bool full_check=true) const { + CImgList colors, opacities; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert 3D object into a CImg3d representation \overloading. + CImg get_object3dtoCImg3d(const bool full_check=true) const { + CImgList opacities, colors; + CImgList primitives(width(),1,1,1,1); + cimglist_for(primitives,p) primitives(p,0) = p; + return get_object3dtoCImg3d(primitives,colors,opacities,full_check); + } + + //! Convert CImg3d representation into a 3D object. + /** + \param[out] primitives Primitives data of the 3D object. + \param[out] colors Colors data of the 3D object. + \param[out] opacities Opacities data of the 3D object. + \param full_check Tells if full checking of the 3D object must be performed. + **/ + template + CImg& CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) { + return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this); + } + + //! Convert CImg3d representation into a 3D object \newinstance. + template + CImg get_CImg3dtoobject3d(CImgList& primitives, + CImgList& colors, + CImgList& opacities, + const bool full_check=true) const { + CImg error_message(1024); + if (!is_CImg3d(full_check,error_message)) + throw CImgInstanceException(_cimg_instance + "CImg3dtoobject3d(): image instance is not a CImg3d (%s).", + cimg_instance,error_message.data()); + const T *ptrs = _data + 6; + const unsigned int + nb_points = cimg::float2uint((float)*(ptrs++)), + nb_primitives = cimg::float2uint((float)*(ptrs++)); + const CImg points = CImg(ptrs,3,nb_points,1,1,true).get_transpose(); + ptrs+=3*nb_points; + primitives.assign(nb_primitives); + cimglist_for(primitives,p) { + const unsigned int nb_inds = (unsigned int)*(ptrs++); + primitives[p].assign(1,nb_inds); + tp *ptrp = primitives[p]._data; + for (unsigned int i = 0; i::max(),(T)cimg::type::max()); \ + const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \ + const ulongT _sc_whd = (ulongT)_width*_height*_depth; \ + cimg::unused(_sc_maxval); + +#define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \ + _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval) + + // [internal] The following _draw_scanline() routines are *non user-friendly functions*, + // used only for internal purpose. + // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid. + template + CImg& _draw_scanline(const int x0, const int x1, const int y, + const tc *const color, const float opacity, + const float brightness, + const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) { + const int nx0 = x0>0?x0:0, nx1 = x1=0) { + const tc *col = color; + const ulongT off = whd - dx - 1; + T *ptrd = data(nx0,y); + if (opacity>=1) { // ** Opaque drawing ** + if (brightness==1) { // Brightness==1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)*(col++); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)*(col++); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } else if (brightness<1) { // Brightness<1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)(*(col++)*brightness); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } else { // Brightness>1 + if (sizeof(T)!=1) cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval); + for (int x = dx; x>=0; --x) *(ptrd++) = val; + ptrd+=off; + } else cimg_forC(*this,c) { + const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval); + std::memset(ptrd,(int)val,dx + 1); + ptrd+=whd; + } + } + } else { // ** Transparent drawing ** + if (brightness==1) { // Brightness==1 + cimg_forC(*this,c) { + const Tfloat val = *(col++)*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else if (brightness<=1) { // Brightness<1 + cimg_forC(*this,c) { + const Tfloat val = *(col++)*brightness*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } else { // Brightness>1 + cimg_forC(*this,c) { + const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity; + for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; } + ptrd+=off; + } + } + } + } + return *this; + } + + //! Draw a 3D point. + /** + \param x0 X-coordinate of the point. + \param y0 Y-coordinate of the point. + \param z0 Z-coordinate of the point. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_point(50,50,color); + \endcode + **/ + template + CImg& draw_point(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_point(): Specified color is (null).", + cimg_instance); + if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } + } + return *this; + } + + //! Draw a 2D point \simplification. + template + CImg& draw_point(const int x0, const int y0, + const tc *const color, const float opacity=1) { + return draw_point(x0,y0,0,color,opacity); + } + + // Draw a points cloud. + /** + \param points Image of vertices coordinates. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_point(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity); + } break; + default : { + cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity); + } + } + return *this; + } + + //! Draw a 2D line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + \note + - Line routine uses Bresenham's algorithm. + - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,color); + \endcode + **/ + template + CImg& draw_line(int x0, int y0, + int x1, int y1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !opacity || !pattern || + std::min(y0,y1)>=height() || std::max(y0,y1)<0 || + std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1); + dx01*=-1; dy01*=-1; + } + + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + const int + step = y0<=y1?1:-1,hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + T *const ptrd = is_horizontal?data(y,x):data(x,y); + cimg_forC(*this,c) { + const T val = color[c]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a 2D line, with z-buffering. + /** + \param zbuffer Zbuffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if a reinitialization of the hash state must be done. + **/ + template + CImg& draw_line(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_line(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float diz01 = iz1 - iz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1); + dx01*=-1; dy01*=-1; diz01*=-1; + } + + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float iz = iz0 + diz01*yy0/dy01; + tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y); + + if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) { + *ptrz = (tz)iz; + T *const ptrd = is_horizontal?data(y,x):data(x,y); + cimg_forC(*this,c) { + const T val = color[c]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line. + /** + \param x0 X-coordinate of the starting line point. + \param y0 Y-coordinate of the starting line point. + \param x1 X-coordinate of the ending line point. + \param y1 Y-coordinate of the ending line point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + \note + - Line routine uses the well known Bresenham's algorithm. + \par Example: + \code + CImg img(100,100,1,3,0), texture("texture256x256.ppm"); + const unsigned char color[] = { 255,128,64 }; + img.draw_line(40,40,80,70,texture,0,0,255,255); + \endcode + **/ + template + CImg& draw_line(int x0, int y0, + int x1, int y1, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + + if (is_empty() || !opacity || !pattern) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + int + dtx01 = tx1 - tx0, dty01 = ty1 - ty0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1); + dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy01ty = dy01*cimg::sign(dty01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01, + tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01, + ty = ty0 + (dty01*yy0 + hdy01ty)/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line, with perspective correction. + /** + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(int x0, int y0, const float z0, + int x1, int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float + diz01 = iz1 - iz0, + txz0 = tx0*iz0, txz1 = tx1*iz1, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, + dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1); + dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float + iz = iz0 + diz01*yy0/dy01, + txz = txz0 + dtxz01*yy0/dy01, + tyz = tyz0 + dtyz01*yy0/dy01; + if (x>=0 && x<=w1 && pattern&hatch) { + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a textured 2D line, with perspective correction and z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the starting point. + \param y0 Y-coordinate of the starting point. + \param z0 Z-coordinate of the starting point + \param x1 X-coordinate of the ending point. + \param y1 Y-coordinate of the ending point. + \param z1 Z-coordinate of the ending point. + \param texture Texture image defining the pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch Tells if the hash variable must be reinitialized. + **/ + template + CImg& draw_line(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + const CImg& texture, + const int tx0, const int ty0, + const int tx1, const int ty1, + const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch); + + if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this; + + float iz0 = 1/z0, iz1 = 1/z1; + int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dy01 = y1 - y0; + float + diz01 = iz1 - iz0, + txz0 = tx0*iz0, txz1 = tx1*iz1, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, + dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0; + + const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01); + if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01); + if (pattern==~0U && y0>y1) { + cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1); + dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1; + } + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + static unsigned int hatch = ~0U - (~0U>>1); + if (init_hatch) hatch = ~0U - (~0U>>1); + cimg_init_scanline(opacity); + + const int + step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2, + cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step; + dy01+=dy01?0:1; + + for (int y = cy0; y!=cy1; y+=step) { + const int + yy0 = y - y0, + x = x0 + (dx01*yy0 + hdy01)/dy01; + const float + iz = iz0 + diz01*yy0/dy01, + txz = txz0 + dtxz01*yy0/dy01, + tyz = tyz0 + dtyz01*yy0/dy01; + tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y); + + if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) { + *ptrz = (tz)iz; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + T *const ptrd = is_horizontal?data(y,x):data(x,y); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const T val = color[c*twhd]; + ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + if (!(hatch>>=1)) hatch = ~0U - (~0U>>1); + } + return *this; + } + + //! Draw a set of consecutive lines. + /** + \param points Coordinates of vertices, stored as a list of vectors. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If set to true, init hatch motif. + \note + - This function uses several call to the single CImg::draw_line() procedure, + depending on the vectors size in \p points. + **/ + template + CImg& draw_line(const CImg& points, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + default : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + int ox = x0, oy = y0; + for (unsigned int i = 1; i + CImg& draw_arrow(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1, + const float angle=30, const float length=-10, + const unsigned int pattern=~0U) { + if (is_empty()) return *this; + const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v, + deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f, + l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; + if (sq>0) { + const float + cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg), + cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg); + const int + xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl), + xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr), + xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2; + draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); + } else draw_point(x0,y0,color,opacity); + return *this; + } + + //! Draw a 2D spline. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + \note + - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points + and corresponding velocity vectors. + - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the + average number of pixels in each drawn segment. + - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), + (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point + and (\p xa,\p ya), (\p xb,\p yb) are two + \e control points. + The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from + the control points as + \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). + \par Example: + \code + CImg img(100,100,1,3,0); + const unsigned char color[] = { 255,255,255 }; + img.draw_spline(30,30,0,100,90,40,0,-100,color); + \endcode + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const tc *const color, const float opacity=1, + const float precision=0.25, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Specified color is (null).", + cimg_instance); + if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); + int ox = x0, oy = y0; + for (float t = 0; t<1; t+=_precision) { + const float t2 = t*t, t3 = t2*t; + const int + nx = (int)(ax*t3 + bx*t2 + u0*t + x0), + ny = (int)(ay*t3 + by*t2 + v0*t + y0); + draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch); + ninit_hatch = false; + ox = nx; oy = ny; + } + return draw_line(ox,oy,x1,y1,color,opacity,pattern,false); + } + + //! Draw a textured 2D spline. + /** + \param x0 X-coordinate of the starting curve point + \param y0 Y-coordinate of the starting curve point + \param u0 X-coordinate of the starting velocity + \param v0 Y-coordinate of the starting velocity + \param x1 X-coordinate of the ending curve point + \param y1 Y-coordinate of the ending curve point + \param u1 X-coordinate of the ending velocity + \param v1 Y-coordinate of the ending velocity + \param texture Texture image defining line pixel colors. + \param tx0 X-coordinate of the starting texture point. + \param ty0 Y-coordinate of the starting texture point. + \param tx1 X-coordinate of the ending texture point. + \param ty1 Y-coordinate of the ending texture point. + \param precision Curve drawing precision. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch if \c true, reinit hatch motif. + **/ + template + CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, + const int x1, const int y1, const float u1, const float v1, + const CImg& texture, + const int tx0, const int ty0, const int tx1, const int ty1, + const float opacity=1, + const float precision=4, const unsigned int pattern=~0U, + const bool init_hatch=true) { + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_empty()) return *this; + if (is_overlapped(texture)) + return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch); + if (x0==x1 && y0==y1) + return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0, + y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(), + opacity); + bool ninit_hatch = init_hatch; + const float + ax = u0 + u1 + 2*(x0 - x1), + bx = 3*(x1 - x0) - 2*u0 - u1, + ay = v0 + v1 + 2*(y0 - y1), + by = 3*(y1 - y0) - 2*v0 - v1, + _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1)); + int ox = x0, oy = y0, otx = tx0, oty = ty0; + for (float t1 = 0; t1<1; t1+=_precision) { + const float t2 = t1*t1, t3 = t2*t1; + const int + nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0), + ny = (int)(ay*t3 + by*t2 + v0*t1 + y0), + ntx = tx0 + (int)((tx1 - tx0)*t1), + nty = ty0 + (int)((ty1 - ty0)*t1); + draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch); + ninit_hatch = false; + ox = nx; oy = ny; otx = ntx; oty = nty; + } + return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false); + } + + //! Draw a set of consecutive splines. + /** + \param points Vertices data. + \param tangents Tangents data. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param is_closed_set Tells if the drawn spline set is closed. + \param precision Precision of the drawing. + \param pattern An integer whose bits describe the line pattern. + \param init_hatch If \c true, init hatch motif. + **/ + template + CImg& draw_spline(const CImg& points, const CImg& tangents, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this; + bool ninit_hatch = init_hatch; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + + default : { + const int x0 = (int)points(0,0), y0 = (int)points(0,1); + const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); + int ox = x0, oy = y0; + float ou = u0, ov = v0; + for (unsigned int i = 1; i + CImg& draw_spline(const CImg& points, + const tc *const color, const float opacity=1, + const bool is_closed_set=false, const float precision=4, + const unsigned int pattern=~0U, const bool init_hatch=true) { + if (is_empty() || !points || points._width<2) return *this; + CImg tangents; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum,points._data); + case 2 : { + tangents.assign(points._width,points._height); + cimg_forX(points,p) { + const unsigned int + p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0), + p1 = is_closed_set?(p + 1)%points.width():(p + 1 + CImg& _draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, const float opacity, + const float brightness) { + if (y0>y1) cimg::swap(x0,x1,y0,y1); + if (y0>y2) cimg::swap(x0,x2,y0,y2); + if (y1>y2) cimg::swap(x1,x2,y1,y2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM); + cimg_draw_scanline(xm,xM,y,color,opacity,cbs); + } + return *this; + } + + //! Draw a filled 2D triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1); + return *this; + } + + //! Draw a outlined 2D triangle. + /** + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + draw_line(x0,y0,x1,y1,color,opacity,pattern,true). + draw_line(x1,y1,x2,y2,color,opacity,pattern,false). + draw_line(x2,y2,x0,y0,color,opacity,pattern,false); + return *this; + } + + //! Draw a filled 2D triangle, with z-buffering. + /** + \param zbuffer Z-buffer image. + \param x0 X-coordinate of the first vertex. + \param y0 Y-coordinate of the first vertex. + \param z0 Z-coordinate of the first vertex. + \param x1 X-coordinate of the second vertex. + \param y1 Y-coordinate of the second vertex. + \param z1 Z-coordinate of the second vertex. + \param x2 X-coordinate of the third vertex. + \param y2 Y-coordinate of the third vertex. + \param z2 Z-coordinate of the third vertex. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + \param brightness Brightness factor. + **/ + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; + + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param bs0 Brightness factor of the first vertex (in [0,2]). + \param bs1 brightness factor of the second vertex (in [0,2]). + \param bs2 brightness factor of the third vertex (in [0,2]). + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, + float bs0, + float bs1, + float bs2, + float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a color-interpolated 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex. + \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex. + \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(const int x0, const int y0, + const int x1, const int y1, + const int x2, const int y2, + const tc1 *const color1, + const tc2 *const color2, + const tc3 *const color3, + const float opacity=1) { + const unsigned char one = 1; + cimg_forC(*this,c) + get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity); + return *this; + } + + //! Draw a textured 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param opacity Drawing opacity. + \param brightness Brightness factor of the drawing (in [0,2]). + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2; + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM; + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a 2D textured triangle, with perspective correction. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured 2D triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const float opacity=1, + const float brightness=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + const float cbs = cimg::cut(brightness,0,2); + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a Phong-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const tc *const color, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + + const ulongT lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,lxm,lxM,lym,lyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dlxmM = lxM - lxm, dlymM = lyM - lym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a Phong-shaded 2D triangle, with z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const tc *const color, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Specified color is (null).", + cimg_instance); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1; + + const ulongT lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dlxmM = lxM - lxm, dlymM = lyM - lym; + const float dizmM = izM - izm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const int + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const tc col = color[c]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param bs0 Brightness factor of the first vertex. + \param bs1 Brightness factor of the second vertex. + \param bs2 Brightness factor of the third vertex. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + bs0,bs1,bs2,opacity); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2; + const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym; + const float dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM; + const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + bs0,bs1,bs2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + float bs0, + float bs1, + float bs2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1; + + const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2); + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz); + const tc *const color = &texture._atXY(tx,ty); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle. + /** + \param x0 X-coordinate of the first vertex in the image instance. + \param y0 Y-coordinate of the first vertex in the image instance. + \param x1 X-coordinate of the second vertex in the image instance. + \param y1 Y-coordinate of the second vertex in the image instance. + \param x2 X-coordinate of the third vertex in the image instance. + \param y2 Y-coordinate of the third vertex in the image instance. + \param texture Texture image used to fill the triangle. + \param tx0 X-coordinate of the first vertex in the texture image. + \param ty0 Y-coordinate of the first vertex in the texture image. + \param tx1 X-coordinate of the second vertex in the texture image. + \param ty1 Y-coordinate of the second vertex in the texture image. + \param tx2 X-coordinate of the third vertex in the texture image. + \param ty2 Y-coordinate of the third vertex in the texture image. + \param light Light image. + \param lx0 X-coordinate of the first vertex in the light image. + \param ly0 Y-coordinate of the first vertex in the light image. + \param lx1 X-coordinate of the second vertex in the light image. + \param ly1 Y-coordinate of the second vertex in the light image. + \param lx2 X-coordinate of the third vertex in the light image. + \param ly2 Y-coordinate of the third vertex in the light image. + \param opacity Drawing opacity. + **/ + template + CImg& draw_triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty()) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2, + dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1, + dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1, + hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2, + hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2, + dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1, + dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1, + hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2, + hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int + dxmM = std::max(1,xM - xm), hdxmM = dxmM/2, + dtxmM = txM - txm, dtymM = tyM - tym, + dlxmM = lxM - lxm, dlymM = lyM - lym; + + for (int x = cxm; x<=cxM; ++x) { + const int + xxm = x - xm, + tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM, + ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM, + lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM, + ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM; + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle, with perspective correction. + template + CImg& draw_triangle(int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2, + light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2, + +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2, + lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2, + dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1, + dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float + dizmM = izM - izm, + dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, + dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float + iz = izm + dizmM*xxm/dxmM, + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + lxz = lxzm + dlxzmM*xxm/dxmM, + lyz = lyzm + dlyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz), + lx = (int)cimg::round(lxz/iz), + ly = (int)cimg::round(lyz/iz); + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + ++ptrd; + } + } + } + return *this; + } + + //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering. + template + CImg& draw_triangle(CImg& zbuffer, + int x0, int y0, const float z0, + int x1, int y1, const float z1, + int x2, int y2, const float z2, + const CImg& texture, + int tx0, int ty0, + int tx1, int ty1, + int tx2, int ty2, + const CImg& light, + int lx0, int ly0, + int lx1, int ly1, + int lx2, int ly2, + const float opacity=1) { + if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this; + if (!is_sameXY(zbuffer)) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have " + "different dimensions.", + cimg_instance, + zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data); + if (texture._depth>1 || texture._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).", + cimg_instance, + texture._width,texture._height,texture._depth,texture._spectrum,texture._data); + if (light._depth>1 || light._spectrum<_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).", + cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data); + if (is_overlapped(texture)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + if (is_overlapped(light)) + return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2, + texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + + float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2; + if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1); + if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2); + if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2); + if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this; + + const int + w1 = width() - 1, h1 = height() - 1, + dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1, + dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1), + cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1), + hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2; + const float + diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1, + txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2, + tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2, + dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1, + dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1, + lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2, + lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2, + dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1, + dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1; + + const ulongT + twhd = (ulongT)texture._width*texture._height*texture._depth, + lwhd = (ulongT)light._width*light._height*light._depth; + cimg_init_scanline(opacity); + + for (int y = cy0; y<=cy2; ++y) { + const int yy0 = y - y0, yy1 = y - y1; + int + xm = yxM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM); + if (xM>=0 && xm<=w1) { + const int + cxm = cimg::cut(xm,0,w1), + cxM = cimg::cut(xM,0,w1); + T *ptrd = data(cxm,y); + tz *ptrz = zbuffer.data(cxm,y); + const int dxmM = std::max(1,xM - xm); + const float + dizmM = izM - izm, + dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, + dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm; + + for (int x = cxm; x<=cxM; ++x) { + const int xxm = x - xm; + const float iz = izm + dizmM*xxm/dxmM; + if (iz>=*ptrz) { + *ptrz = (tz)iz; + const float + txz = txzm + dtxzmM*xxm/dxmM, + tyz = tyzm + dtyzmM*xxm/dxmM, + lxz = lxzm + dlxzmM*xxm/dxmM, + lyz = lyzm + dlyzmM*xxm/dxmM; + const int + tx = (int)cimg::round(txz/iz), + ty = (int)cimg::round(tyz/iz), + lx = (int)cimg::round(lxz/iz), + ly = (int)cimg::round(lyz/iz); + const tc *const color = &texture._atXY(tx,ty); + const tl *const lig = &light._atXY(lx,ly); + cimg_forC(*this,c) { + const tc col = color[c*twhd]; + const float cbs = cimg::cut((float)lig[c*lwhd],0,2); + const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval; + ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity); + } + } + ++ptrd; ++ptrz; + } + } + } + return *this; + } + + //! Draw a filled 4D rectangle. + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param z0 Z-coordinate of the upper-left rectangle corner. + \param c0 C-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param z1 Z-coordinate of the lower-right rectangle corner. + \param c1 C-coordinate of the lower-right rectangle corner. + \param val Scalar value used to fill the rectangle area. + \param opacity Drawing opacity. + **/ + CImg& draw_rectangle(const int x0, const int y0, const int z0, const int c0, + const int x1, const int y1, const int z1, const int c1, + const T val, const float opacity=1) { + if (is_empty()) return *this; + const int + nx0 = x0=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0), + ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0), + lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0), + lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0); + const ulongT + offX = (ulongT)_width - lx, + offY = (ulongT)_width*(_height - ly), + offZ = (ulongT)_width*_height*(_depth - lz); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0); + if (lx>0 && ly>0 && lz>0 && lc>0) + for (int v = 0; v=1) { + if (sizeof(T)!=1) { for (int x = 0; x + CImg& draw_rectangle(const int x0, const int y0, const int z0, + const int x1, const int y1, const int z1, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_rectangle(): Specified color is (null).", + cimg_instance); + cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity); + return *this; + } + + //! Draw a filled 2D rectangle. + /** + \param x0 X-coordinate of the upper-left rectangle corner. + \param y0 Y-coordinate of the upper-left rectangle corner. + \param x1 X-coordinate of the lower-right rectangle corner. + \param y1 Y-coordinate of the lower-right rectangle corner. + \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity=1) { + return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity); + } + + //! Draw a outlined 2D rectangle \overloading. + template + CImg& draw_rectangle(const int x0, const int y0, + const int x1, const int y1, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (is_empty()) return *this; + if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true); + if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true); + const int + nx0 = x0 + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity=1) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + if (points.height()!=2) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum); + if (points._width==1) return draw_point(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)),color,opacity); + if (points._width==2) return draw_line(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)), + cimg::uiround(points(1,0)),cimg::uiround(points(1,1)),color,opacity); + if (points._width==3) return draw_triangle(cimg::uiround(points(0,0)),cimg::uiround(points(0,1)), + cimg::uiround(points(1,0)),cimg::uiround(points(1,1)), + cimg::uiround(points(2,0)),cimg::uiround(points(2,1)),color,opacity); + cimg_init_scanline(opacity); + int + xmin = 0, ymin = 0, + xmax = points.get_shared_row(0).max_min(xmin), + ymax = points.get_shared_row(1).max_min(ymin); + if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this; + if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity); + + ymin = std::max(0,ymin); + ymax = std::min(height() - 1,ymax); + CImg Xs(points._width,ymax - ymin + 1); + CImg count(Xs._height,1,1,1,0); + unsigned int n = 0, nn = 1; + bool go_on = true; + + while (go_on) { + unsigned int an = (nn + 1)%points._width; + const int + x0 = cimg::uiround(points(n,0)), + y0 = cimg::uiround(points(n,1)); + if (points(nn,1)==y0) while (points(an,1)==y0) { nn = an; (an+=1)%=points._width; } + const int + x1 = cimg::uiround(points(nn,0)), + y1 = cimg::uiround(points(nn,1)); + unsigned int tn = an; + while (points(tn,1)==y1) (tn+=1)%=points._width; + + if (y0!=y1) { + const int + y2 = cimg::uiround(points(tn,1)), + x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1, + step = cimg::sign(y01), + tmax = std::max(1,cimg::abs(y01)), htmax = tmax*cimg::sign(x01)/2, + tend = tmax - (step==cimg::sign(y12)); + unsigned int y = (unsigned int)y0 - ymin; + for (int t = 0; t<=tend; ++t, y+=step) + if (yn; + n = nn; + nn = an; + } + + cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512)) + cimg_forY(Xs,y) { + const CImg Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort(); + int px = width(); + for (unsigned int k = 0; k + CImg& draw_polygon(const CImg& points, + const tc *const color, const float opacity, const unsigned int pattern) { + if (is_empty() || !points) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Specified color is (null).", + cimg_instance); + if (points._width==1) return draw_point((int)points(0,0),(int)points(0,1),color,opacity); + if (points._width==2) return draw_line((int)points(0,0),(int)points(0,1), + (int)points(1,0),(int)points(1,1),color,opacity,pattern); + bool ninit_hatch = true; + switch (points._height) { + case 0 : case 1 : + throw CImgArgumentException(_cimg_instance + "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).", + cimg_instance, + points._width,points._height,points._depth,points._spectrum); + default : { + CImg npoints(points._width,2); + int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1); + unsigned int nb_points = 1; + for (unsigned int p = 1; p + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity=1) { + return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true); + } + + //! Draw a filled 2D ellipse \overloading. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param tensor Diffusion tensor describing the ellipse. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity=1) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity); + } + + //! Draw an outlined 2D ellipse. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param r1 First radius of the ellipse. + \param r2 Second radius of the ellipse. + \param angle Angle of the first radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle, + const tc *const color, const float opacity, const unsigned int pattern) { + if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false); + return *this; + } + + //! Draw an outlined 2D ellipse \overloading. + /** + \param x0 X-coordinate of the ellipse center. + \param y0 Y-coordinate of the ellipse center. + \param tensor Diffusion tensor describing the ellipse. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern An integer whose bits describe the outline pattern. + **/ + template + CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, + const tc *const color, const float opacity, + const unsigned int pattern) { + CImgList eig = tensor.get_symmetric_eigen(); + const CImg &val = eig[0], &vec = eig[1]; + return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)), + std::atan2(vec(0,1),vec(0,0))*180/cimg::PI, + color,opacity,pattern); + } + + template + CImg& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle, + const tc *const color, const float opacity, + const unsigned int pattern, const bool is_filled) { + if (is_empty() || (!is_filled && !pattern)) return *this; + const float radiusM = std::max(radius1,radius2); + if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_ellipse(): Specified color is (null).", + cimg_instance); + const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2); + if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity); + if (iradius1==iradius2) { + if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity); + else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern); + } + const float ang = (float)(angle*cimg::PI/180); + + if (!is_filled) { // Outlined + const float ca = std::cos(ang), sa = std::sin(ang); + CImg points((unsigned int)cimg::round(6*radiusM),2); + cimg_forX(points,k) { + const float + _ang = (float)(2*cimg::PI*k/points._width), + X = (float)(radius1*std::cos(_ang)), + Y = (float)(radius2*std::sin(_ang)); + points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa)); + points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca)); + } + draw_polygon(points,color,opacity,pattern); + } else { // Filled + cimg_init_scanline(opacity); + const float + ca = std::cos(ang), + sa = -std::sin(ang), + ca2 = ca*ca, + sa2 = sa*sa, + casa = ca*sa, + i1 = 1/cimg::sqr(radius1), + i2 = 1/cimg::sqr(radius2), + t1 = i1*ca2 + i2*sa2, + t2 = (i2 - i1)*casa, + t3 = i2*ca2 + i1*sa2, + t12 = t1*2; + const int + _ymin = (int)std::floor(y0 - radiusM), + _ymax = (int)std::ceil(y0 + radiusM), + ymin = _ymin<0?0:_ymin, + ymax = _ymax>=height()?height() - 1:_ymax; + for (int y = ymin; y<=ymax; ++y) { + const float + Y = y - y0 + 0.5f, + B = 2*t2*Y, + C = t3*Y*Y - 1, + D = B*B - 4*t1*C; + if (D>=0) { + const float sD = std::sqrt(D); + const int + xmin = (int)(x0 + cimg::round((-B - sD)/t12)), + xmax = (int)(x0 + cimg::round((-B + sD)/t12)); + cimg_draw_scanline(xmin,xmax,y,color,opacity,1); + } + } + } + return *this; + } + + //! Draw a filled 2D circle. + /** + \param x0 X-coordinate of the circle center. + \param y0 Y-coordinate of the circle center. + \param radius Circle radius. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \note + - Circle version of the Bresenham's algorithm is used. + **/ + template + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (!radius) return draw_point(x0,y0,color,opacity); + cimg_init_scanline(opacity); + if (y0>=0 && y0=0) { + const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y; + if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 + CImg& draw_circle(const int x0, const int y0, int radius, + const tc *const color, const float opacity, + const unsigned int pattern) { + if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern); + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_circle(): Specified color is (null).", + cimg_instance); + if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this; + if (!radius) return draw_point(x0,y0,color,opacity); + + draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity). + draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity); + if (radius==1) return *this; + for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x=0) { f+=(ddFy+=2); --y; } + ++x; ++(f+=(ddFx+=2)); + if (x!=y + 1) { + const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x, + x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y; + draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). + draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); + if (x!=y) + draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). + draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); + } + } + return *this; + } + + //! Draw an image. + /** + \param sprite Sprite image. + \param x0 X-coordinate of the sprite position. + \param y0 Y-coordinate of the sprite position. + \param z0 Z-coordinate of the sprite position. + \param c0 C-coordinate of the sprite position. + \param opacity Drawing opacity. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) + return assign(sprite,false); + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c=1) for (int x = 0; x& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const float opacity=1) { + if (is_empty() || !sprite) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity); + if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared()) + return assign(sprite,false); + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + const ulongT slx = lx*sizeof(T); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c=1) std::memcpy(ptrd,ptrs,slx); + else for (int x = 0; x + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,z0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,y0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const float opacity=1) { + return draw_image(x0,0,sprite,opacity); + } + + //! Draw an image \overloading. + template + CImg& draw_image(const CImg& sprite, const float opacity=1) { + return draw_image(0,sprite,opacity); + } + + //! Draw a masked image. + /** + \param sprite Sprite image. + \param mask Mask image. + \param x0 X-coordinate of the sprite position in the image instance. + \param y0 Y-coordinate of the sprite position in the image instance. + \param z0 Z-coordinate of the sprite position in the image instance. + \param c0 C-coordinate of the sprite position in the image instance. + \param mask_max_value Maximum pixel value of the mask image \c mask. + \param opacity Drawing opacity. + \note + - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite. + - Dimensions along x,y and z of \p sprite and \p mask must be the same. + **/ + template + CImg& draw_image(const int x0, const int y0, const int z0, const int c0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + if (is_empty() || !sprite || !mask) return *this; + if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value); + if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value); + if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth) + throw CImgArgumentException(_cimg_instance + "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data, + mask._width,mask._height,mask._depth,mask._spectrum,mask._data); + + const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0; + const int + dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0, + sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0, + lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0), + ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0), + lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0), + lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0); + const ulongT msize = mask.size(); + + if (lx>0 && ly>0 && lz>0 && lc>0) { + for (int c = 0; c + CImg& draw_image(const int x0, const int y0, const int z0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, const int y0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a image \overloading. + template + CImg& draw_image(const int x0, + const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(x0,0,sprite,mask,opacity,mask_max_value); + } + + //! Draw an image. + template + CImg& draw_image(const CImg& sprite, const CImg& mask, const float opacity=1, + const float mask_max_value=1) { + return draw_image(0,sprite,mask,opacity,mask_max_value); + } + + //! Draw a text string. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color. + \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color. + \param opacity Drawing opacity. + \param font Font used for drawing text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \note A transparent background is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \note A transparent foreground is used for the text. + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity, const CImgList& font, ...) { + if (!font) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font,false); + } + + //! Draw a text string \overloading. + /** + \param x0 X-coordinate of the text in the image instance. + \param y0 Y-coordinate of the text in the image instance. + \param text Format of the text ('printf'-style format string). + \param foreground_color Array of spectrum() values of type \c T, + defining the foreground color (0 means 'transparent'). + \param background_color Array of spectrum() values of type \c T, + defining the background color (0 means 'transparent'). + \param opacity Drawing opacity. + \param font_height Height of the text font (exact match for 13,23,53,103, interpolated otherwise). + **/ + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + const CImgList& font = CImgList::font(font_height,true); + _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true); + return *this; + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const tc *const foreground_color, const int background_color=0, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + cimg::unused(background_color); + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data); + } + + //! Draw a text string \overloading. + template + CImg& draw_text(const int x0, const int y0, + const char *const text, + const int, const tc *const background_color, + const float opacity=1, const unsigned int font_height=13, ...) { + if (!font_height) return *this; + CImg tmp(2048); + std::va_list ap; va_start(ap,font_height); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data); + } + + template + CImg& _draw_text(const int x0, const int y0, + const char *const text, + const tc1 *const foreground_color, const tc2 *const background_color, + const float opacity, const CImgList& font, + const bool is_native_font) { + if (!text) return *this; + if (!font) + throw CImgArgumentException(_cimg_instance + "draw_text(): Empty specified font.", + cimg_instance); + + const unsigned int text_length = (unsigned int)std::strlen(text); + const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4; + unsigned char o_ch, ch = 0; + int x, y, w; + CImg left_paddings(text_length,1,1,1,0); + const CImg empty = CImg::empty(); + + if (is_empty() || is_native_font) { + // Pre-compute necessary size of the image as well as left paddings of each character. + x = y = w = 0; + o_ch = 0; + for (unsigned int i = 0; iw) w = x; x = 0; break; + case '\t' : x+=4*font[(int)' ']._width; break; + case ' ' : x+=font[(int)' ']._width; break; + default : if (ch'9')) || o_ch==';' || o_ch==':' || o_ch=='!') + left_padding = 4*padding_x; + else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') && + ((ch>='0' && ch<='9') || + (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') || + (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) || + o_ch=='.' || o_ch=='\'' || ch=='\'') + left_padding = padding_x; + else if ((o_ch<'0' || o_ch>'9') && ch!='-') { + const CImg &mask = ch + 256U' ' && o_ch>' ' && mask._height>13) { + const CImg &o_mask = o_ch + 256U13) { + const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0; + left_padding = -10; + cimg_forY(mask,k) { + const int + lpad = o_mask(w1,k)>=8?0: + o_mask._width<=2 || o_mask(w2,k)>=8?-1: + o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3, + rpad = mask(0,k)>=8?0: + mask._width<=2 || mask(1,k)>=8?-1: + mask._width<=3 || mask(2,k)>=8?-2:-3; + left_padding = std::max(left_padding,lpad + rpad); + } + } + } + } + left_paddings[i] = left_padding; + } + x+=left_padding + font[ch]._width + padding_x; + o_ch = ch; + } + } + } + if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; } + if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0); + } + + // Draw font characters on image. + x = x0; y = y0; + for (unsigned int i = 0; i letter = font[ch]; + if (letter) { + const CImg &mask = ch + 256Uletter._spectrum) + letter.assign(letter.get_resize(-100,-100,1,_spectrum,0,2),false); + const unsigned int cmin = std::min(_spectrum,letter._spectrum); + if (foreground_color) + for (unsigned int c = 0; c& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) { + CImg tmp(2048); + std::va_list ap; + va_start(ap,is_down); + cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap); + CImg a_label, a_labelmask; + const unsigned char a_labelcolor = 255; + unsigned int ofs = font_size, fs = ofs; + do { // Determine best font size + a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data); + if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) { + font_size = fs; break; + } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) { + ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f)); + } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) { + ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f)); + } else { font_size = fs; break; } + } while (true); + a_label.normalize(0,255); + a_label+=(255 - a_label.get_dilate(3)).normalize(0,80); + a_label.resize(-100,-100,1,3,1); + return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f); + } + + //! Draw a 2D vector field. + /** + \param flow Image of 2D vectors used as input data. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const t2 *const color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + return draw_quiver(flow,CImg(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern); + } + + //! Draw a 2D vector field, using a field of colors. + /** + \param flow Image of 2D vectors used as input data. + \param color Image of spectrum()-D vectors corresponding to the color of each arrow. + \param opacity Opacity of the drawing. + \param sampling Length (in pixels) between each arrow. + \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). + \param is_arrow Tells if arrows must be drawn, instead of oriented segments. + \param pattern Used pattern to draw lines. + \note Clipping is supported. + **/ + template + CImg& draw_quiver(const CImg& flow, + const CImg& color, const float opacity=1, + const unsigned int sampling=25, const float factor=-20, + const bool is_arrow=true, const unsigned int pattern=~0U) { + if (is_empty()) return *this; + if (!flow || flow._spectrum!=2) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).", + cimg_instance, + flow._width,flow._height,flow._depth,flow._spectrum,flow._data); + if (sampling<=0) + throw CImgArgumentException(_cimg_instance + "draw_quiver(): Invalid sampling value %g " + "(should be >0)", + cimg_instance, + sampling); + const bool colorfield = (color._width==flow._width && color._height==flow._height && + color._depth==1 && color._spectrum==_spectrum); + if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern); + float vmax,fact; + if (factor<=0) { + float m, M = (float)flow.get_norm(2).max_min(m); + vmax = (float)std::max(cimg::abs(m),cimg::abs(M)); + if (!vmax) vmax = 1; + fact = -factor; + } else { fact = factor; vmax = 1; } + + for (unsigned int y = sampling/2; y<_height; y+=sampling) + for (unsigned int x = sampling/2; x<_width; x+=sampling) { + const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height; + float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax; + if (is_arrow) { + const int xx = (int)(x + u), yy = (int)(y + v); + if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern); + else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern); + } else { + if (colorfield) + draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), + color.get_vector_at(X,Y)._data,opacity,pattern); + else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v), + color._data,opacity,pattern); + } + } + return *this; + } + + //! Draw a labeled horizontal axis. + /** + \param values_x Values along the horizontal axis. + \param y Y-coordinate of the horizontal axis in the image instance. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const CImg& values_x, const int y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true, const float round_x=0) { + if (is_empty()) return *this; + const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height; + const int siz = (int)values_x.size() - 1; + CImg txt(32); + CImg a_label; + if (siz<=0) { // Degenerated case + draw_line(0,y,_width - 1,y,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x); + a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _xt = (width() - a_label.width())/2, + xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt; + draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case + if (values_x[0]=width() - 2?width() - 3 - a_label.width():_xt; + draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw a labeled vertical axis. + /** + \param x X-coordinate of the vertical axis in the image instance. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern Drawing pattern. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axis(const int x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern=~0U, const unsigned int font_height=13, + const bool allow_zero=true, const float round_y=0) { + if (is_empty()) return *this; + int siz = (int)values_y.size() - 1; + CImg txt(32); + CImg a_label; + if (siz<=0) { // Degenerated case + draw_line(x,0,x,_height - 1,color,opacity,pattern); + if (!siz) { + cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y); + a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height); + const int + _yt = (height() - a_label.height())/2, + yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt, + _xt = x - 2 - a_label.width(), + xt = _xt>=0?_xt:x + 3; + draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } else { // Regular case + if (values_y[0]=height()?height() - 1 - a_label.height():_yt, + _xt = x - 2 - a_label.width(), + xt = _xt>=0?_xt:x + 3; + draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity); + if (allow_zero || *txt!='0' || txt[1]!=0) + draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height); + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes. + /** + \param values_x Values along the X-axis. + \param values_y Values along the Y-axis. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for the X-axis. + \param pattern_y Drawing pattern for the Y-axis. + \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise). + \param allow_zero Enable/disable the drawing of label '0' if found. + **/ + template + CImg& draw_axes(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13, const bool allow_zero=true, + const float round_x=0, const float round_y=0) { + if (is_empty()) return *this; + const CImg nvalues_x(values_x._data,values_x.size(),1,1,1,true); + const int sizx = (int)values_x.size() - 1, wm1 = width() - 1; + if (sizx>=0) { + float ox = (float)*nvalues_x; + for (unsigned int x = sizx?1U:0U; x<_width; ++x) { + const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1); + if (nx*ox<=0) { + draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y); + break; + } + ox = nx; + } + } + const CImg nvalues_y(values_y._data,values_y.size(),1,1,1,true); + const int sizy = (int)values_y.size() - 1, hm1 = height() - 1; + if (sizy>0) { + float oy = (float)nvalues_y[0]; + for (unsigned int y = sizy?1U:0U; y<_height; ++y) { + const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1); + if (ny*oy<=0) { + draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x); + break; + } + oy = ny; + } + } + return *this; + } + + //! Draw labeled horizontal and vertical axes \overloading. + template + CImg& draw_axes(const float x0, const float x1, const float y0, const float y1, + const tc *const color, const float opacity=1, + const int subdivisionx=-60, const int subdivisiony=-60, + const float precisionx=0, const float precisiony=0, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U, + const unsigned int font_height=13) { + if (is_empty()) return *this; + const bool allow_zero = (x0*x1>0) || (y0*y1>0); + const float + dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0), + px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx, + py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony; + if (x0!=x1 && y0!=y1) + draw_axes(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1), + CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1), + color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py); + else if (x0==x1 && y0!=y1) + draw_axis((int)x0,CImg::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1), + color,opacity,pattern_y,font_height,py); + else if (x0!=x1 && y0==y1) + draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0, + color,opacity,pattern_x,font_height,px); + return *this; + } + + //! Draw 2D grid. + /** + \param values_x X-coordinates of the vertical lines. + \param values_y Y-coordinates of the horizontal lines. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + \param pattern_x Drawing pattern for vertical lines. + \param pattern_y Drawing pattern for horizontal lines. + **/ + template + CImg& draw_grid(const CImg& values_x, const CImg& values_y, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + if (values_x) cimg_foroff(values_x,x) { + const int xi = (int)values_x[x]; + if (xi>=0 && xi=0 && yi + CImg& draw_grid(const float delta_x, const float delta_y, + const float offsetx, const float offsety, + const bool invertx, const bool inverty, + const tc *const color, const float opacity=1, + const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) { + if (is_empty()) return *this; + CImg seqx, seqy; + if (delta_x!=0) { + const float dx = delta_x>0?delta_x:_width*-delta_x/100; + const unsigned int nx = (unsigned int)(_width/dx); + seqx = CImg::sequence(1 + nx,0,(unsigned int)(dx*nx)); + if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width); + if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x); + } + if (delta_y!=0) { + const float dy = delta_y>0?delta_y:_height*-delta_y/100; + const unsigned int ny = (unsigned int)(_height/dy); + seqy = CImg::sequence(1 + ny,0,(unsigned int)(dy*ny)); + if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height); + if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y); + } + return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y); + } + + //! Draw 1D graph. + /** + \param data Image containing the graph values I = f(x). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + + \param plot_type Define the type of the plot: + - 0 = No plot. + - 1 = Plot using segments. + - 2 = Plot using cubic splines. + - 3 = Plot with bars. + \param vertex_type Define the type of points: + - 0 = No points. + - 1 = Point. + - 2 = Straight cross. + - 3 = Diagonal cross. + - 4 = Filled circle. + - 5 = Outlined circle. + - 6 = Square. + - 7 = Diamond. + \param ymin Lower bound of the y-range. + \param ymax Upper bound of the y-range. + \param pattern Drawing pattern. + \note + - if \c ymin==ymax==0, the y-range is computed automatically from the input samples. + **/ + template + CImg& draw_graph(const CImg& data, + const tc *const color, const float opacity=1, + const unsigned int plot_type=1, const int vertex_type=1, + const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) { + if (is_empty() || _height<=1) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_graph(): Specified color is (null).", + cimg_instance); + + // Create shaded colors for displaying bar plots. + CImg color1, color2; + if (plot_type==3) { + color1.assign(_spectrum); color2.assign(_spectrum); + cimg_forC(*this,c) { + color1[c] = (tc)std::min((float)cimg::type::max(),(float)color[c]*1.2f); + color2[c] = (tc)(color[c]*0.4f); + } + } + + // Compute min/max and normalization factors. + const ulongT + siz = data.size(), + _siz1 = siz - (plot_type!=3), + siz1 = _siz1?_siz1:1; + const unsigned int + _width1 = _width - (plot_type!=3), + width1 = _width1?_width1:1; + double m = ymin, M = ymax; + if (ymin==ymax) m = (double)data.max_min(M); + if (m==M) { --m; ++M; } + const float ca = (float)(M-m)/(_height - 1); + bool init_hatch = true; + + // Draw graph edges + switch (plot_type%4) { + case 1 : { // Segments + int oX = 0, oY = (int)cimg::round((data[0] - m)/ca); + if (siz==1) { + const int Y = (int)cimg::round((*data - m)/ca); + draw_line(0,Y,width() - 1,Y,color,opacity,pattern); + } else { + const float fx = (float)_width/siz1; + for (ulongT off = 1; off ndata(data._data,siz,1,1,1,true); + int oY = (int)cimg::round((data[0] - m)/ca); + cimg_forX(*this,x) { + const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca); + if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch); + init_hatch = false; + oY = Y; + } + } break; + case 3 : { // Bars + const int Y0 = (int)cimg::round(-m/ca); + const float fx = (float)_width/siz1; + int oX = 0; + cimg_foroff(data,off) { + const int + X = (int)cimg::round((off + 1)*fx) - 1, + Y = (int)cimg::round((data[off] - m)/ca); + draw_rectangle(oX,Y0,X,Y,color,opacity). + draw_line(oX,Y,oX,Y0,color2.data(),opacity). + draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity). + draw_line(X,Y,X,Y0,color1.data(),opacity). + draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity); + oX = X + 1; + } + } break; + default : break; // No edges + } + + // Draw graph points + const unsigned int wb2 = plot_type==3?_width1/(2*siz):0; + const float fx = (float)_width1/siz1; + switch (vertex_type%8) { + case 1 : { // Point + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_point(X,Y,color,opacity); + } + } break; + case 2 : { // Straight Cross + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity); + } + } break; + case 3 : { // Diagonal Cross + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity); + } + } break; + case 4 : { // Filled Circle + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity); + } + } break; + case 5 : { // Outlined circle + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_circle(X,Y,3,color,opacity,~0U); + } + } break; + case 6 : { // Square + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U); + } + } break; + case 7 : { // Diamond + cimg_foroff(data,off) { + const int + X = (int)cimg::round(off*fx + wb2), + Y = (int)cimg::round((data[off]-m)/ca); + draw_line(X,Y - 4,X + 4,Y,color,opacity). + draw_line(X + 4,Y,X,Y + 4,color,opacity). + draw_line(X,Y + 4,X - 4,Y,color,opacity). + draw_line(X - 4,Y,X,Y - 4,color,opacity); + } + } break; + default : break; // No points + } + return *this; + } + + bool _draw_fill(const int x, const int y, const int z, + const CImg& ref, const float tolerance2) const { + const T *ptr1 = data(x,y,z), *ptr2 = ref._data; + const ulongT off = _width*_height*_depth; + float diff = 0; + cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; } + return diff<=tolerance2; + } + + //! Draw filled 3D region with the flood fill algorithm. + /** + \param x0 X-coordinate of the starting point of the region to fill. + \param y0 Y-coordinate of the starting point of the region to fill. + \param z0 Z-coordinate of the starting point of the region to fill. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param[out] region Image that will contain the mask of the filled region mask, as an output. + \param tolerance Tolerance concerning neighborhood values. + \param opacity Opacity of the drawing. + \param is_high_connectivity Tells if 8-connexity must be used. + \return \c region is initialized with the binary mask of the filled region. + **/ + template + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity, + CImg ®ion, + const float tolerance = 0, + const bool is_high_connectivity = false) { +#define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \ + stack[N] = x; stack(N,1) = y; stack(N++,2) = z +#define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2) +#define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2) + + if (!containsXYZC(x0,y0,z0,0)) return *this; + const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f); + const float tolerance2 = cimg::sqr(tolerance); + const CImg ref = get_vector_at(x0,y0,z0); + CImg stack(256,1,1,3); + CImg _region(_width,_height,_depth,1,0); + unsigned int N = 0; + int x, y, z; + + _draw_fill_push(x0,y0,z0); + while (N>0) { + _draw_fill_pop(x,y,z); + if (!_region(x,y,z)) { + const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1; + int xl = x, xr = x; + + // Using these booleans reduces the number of pushes drastically. + bool is_yp = false, is_yn = false, is_zp = false, is_zn = false; + for (int step = -1; step<2; step+=2) { + while (x>=0 && x=0 && _draw_fill_is_inside(x,yp,z)) { + if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; } + } else is_yp = false; + if (yn1) { + if (zp>=0 && _draw_fill_is_inside(x,y,zp)) { + if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; } + } else is_zp = false; + if (zn=0 && !is_yp) { + if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) { + _draw_fill_push(xp,yp,z); if (step<0) is_yp = true; + } + if (xn0) is_yp = true; + } + } + if (yn=0 && _draw_fill_is_inside(xp,yn,z)) { + _draw_fill_push(xp,yn,z); if (step<0) is_yn = true; + } + if (xn0) is_yn = true; + } + } + if (depth()>1) { + if (zp>=0 && !is_zp) { + if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) { + _draw_fill_push(xp,y,zp); if (step<0) is_zp = true; + } + if (xn0) is_zp = true; + } + + if (yp>=0 && !is_yp) { + if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); } + if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); } + if (xn=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); } + if (xn=0 && _draw_fill_is_inside(xp,y,zn)) { + _draw_fill_push(xp,y,zn); if (step<0) is_zn = true; + } + if (xn0) is_zn = true; + } + + if (yp>=0 && !is_yp) { + if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); } + if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); } + if (xn=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); } + if (xn + CImg& draw_fill(const int x0, const int y0, const int z0, + const tc *const color, const float opacity=1, + const float tolerance=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity); + } + + //! Draw filled 2D region with the flood fill algorithm \simplification. + template + CImg& draw_fill(const int x0, const int y0, + const tc *const color, const float opacity=1, + const float tolerance=0, const bool is_high_connexity=false) { + CImg tmp; + return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity); + } + + //! Draw a random plasma texture. + /** + \param alpha Alpha-parameter. + \param beta Beta-parameter. + \param scale Scale-parameter. + \note Use the mid-point algorithm to render. + **/ + CImg& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) { + if (is_empty()) return *this; + const int w = width(), h = height(); + const Tfloat m = (Tfloat)cimg::type::min(), M = (Tfloat)cimg::type::max(); + cimg_uint64 rng = (cimg::_rand(),cimg::rng()); + cimg_forZC(*this,z,c) { + CImg ref = get_shared_slice(z,c); + for (int delta = 1<1; delta>>=1) { + const int delta2 = delta>>1; + const float r = alpha*delta + beta; + + // Square step. + for (int y0 = 0; y0M?M:val); + } + + // Diamond steps. + for (int y = -delta2; yM?M:val); + } + for (int y0 = 0; y0M?M:val); + } + for (int y = -delta2; yM?M:val); + } + } + } + cimg::srand(rng); + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2D fractal. + /** + \param x0 X-coordinate of the upper-left pixel. + \param y0 Y-coordinate of the upper-left pixel. + \param x1 X-coordinate of the lower-right pixel. + \param y1 Y-coordinate of the lower-right pixel. + \param colormap Colormap. + \param opacity Drawing opacity. + \param z0r Real part of the upper-left fractal vertex. + \param z0i Imaginary part of the upper-left fractal vertex. + \param z1r Real part of the lower-right fractal vertex. + \param z1i Imaginary part of the lower-right fractal vertex. + \param iteration_max Maximum number of iterations for each estimated point. + \param is_normalized_iteration Tells if iterations are normalized. + \param is_julia_set Tells if the Mandelbrot or Julia set is rendered. + \param param_r Real part of the Julia set parameter. + \param param_i Imaginary part of the Julia set parameter. + \note Fractal rendering is done by the Escape Time Algorithm. + **/ + template + CImg& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1, + const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + if (is_empty()) return *this; + CImg palette; + if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true); + if (palette && palette._spectrum!=_spectrum) + throw CImgArgumentException(_cimg_instance + "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have " + "incompatible dimensions.", + cimg_instance, + colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data); + + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.); + const int + _x0 = cimg::cut(x0,0,width() - 1), + _y0 = cimg::cut(y0,0,height() - 1), + _x1 = cimg::cut(x1,0,width() - 1), + _y1 = cimg::cut(y1,0,height() - 1); + + cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) + cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048)) + for (int q = _y0; q<=_y1; ++q) + for (int p = _x0; p<=_x1; ++p) { + unsigned int iteration = 0; + const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height; + double zr, zi, cr, ci; + if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; } + else { zr = param_r; zi = param_i; cr = x; ci = y; } + for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) { + const double temp = zr*zr - zi*zi + cr; + zi = 2*zr*zi + ci; + zr = temp; + } + if (iteration>iteration_max) { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity); + } + } else if (is_normalized_iteration) { + const float + normz = (float)cimg::abs(zr*zr + zi*zi), + niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2); + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c); + else cimg_forC(*this,c) + (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } else { + if (palette) { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c); + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity); + } else { + if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration; + else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity); + } + } + } + return *this; + } + + //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading. + template + CImg& draw_mandelbrot(const CImg& colormap, const float opacity=1, + const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2, + const unsigned int iteration_max=255, + const bool is_normalized_iteration=false, + const bool is_julia_set=false, + const double param_r=0, const double param_i=0) { + return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity, + z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i); + } + + //! Draw a 1D gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param sigma Standard variation of the gaussian distribution. + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float sigma, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + cimg_forX(*this,x) { + const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2); + T *ptrd = data(x,0,0,0); + if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 2D gaussian function. + /** + \param xc X-coordinate of the gaussian center. + \param yc Y-coordinate of the gaussian center. + \param tensor Covariance matrix (must be 2x2). + \param color Pointer to \c spectrum() consecutive values, defining the drawing color. + \param opacity Drawing opacity. + **/ + template + CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + if (!color) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified color is (null).", + cimg_instance); + typedef typename CImg::Tfloat tfloat; + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + float dy = -yc; + cimg_forY(*this,y) { + float dx = -xc; + cimg_forX(*this,x) { + const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); + T *ptrd = data(x,y,0,0); + if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + ++dx; + } + ++dy; + } + return *this; + } + + //! Draw a 2D gaussian function \overloading. + template + CImg& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv, + const tc *const color, const float opacity=1) { + const double + a = r1*ru*ru + r2*rv*rv, + b = (r1-r2)*ru*rv, + c = r1*rv*rv + r2*ru*ru; + const CImg tensor(2,2,1,1, a,b,b,c); + return draw_gaussian(xc,yc,tensor,color,opacity); + } + + //! Draw a 2D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); + } + + //! Draw a 3D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, + const tc *const color, const float opacity=1) { + if (is_empty()) return *this; + typedef typename CImg::Tfloat tfloat; + if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1) + throw CImgArgumentException(_cimg_instance + "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.", + cimg_instance, + tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data); + + const CImg invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.; + const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2); + const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f); + const ulongT whd = (ulongT)_width*_height*_depth; + const tc *col = color; + cimg_forXYZ(*this,x,y,z) { + const float + dx = (x - xc), dy = (y - yc), dz = (z - zc), + val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); + T *ptrd = data(x,y,z,0); + if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; } + else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; } + col-=_spectrum; + } + return *this; + } + + //! Draw a 3D gaussian function \overloading. + template + CImg& draw_gaussian(const float xc, const float yc, const float zc, const float sigma, + const tc *const color, const float opacity=1) { + return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); + } + + //! Draw a 3D object. + /** + \param x0 X-coordinate of the 3D object position + \param y0 Y-coordinate of the 3D object position + \param z0 Z-coordinate of the 3D object position + \param vertices Image Nx3 describing 3D point coordinates + \param primitives List of P primitives + \param colors List of P color (or textures) + \param opacities Image or list of P opacities + \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) + \param is_double_sided Tells if object faces have two sides or are oriented. + \param focale length of the focale (0 for parallel projection) + \param lightx X-coordinate of the light + \param lighty Y-coordinate of the light + \param lightz Z-coordinate of the light + \param specular_lightness Amount of specular light. + \param specular_shininess Shininess of the object + \param g_opacity Global opacity of the object. + **/ + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImg& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } +#endif + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type, + is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, const CImgList& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities, + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,1); + } +#endif + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + //! Draw a 3D object \simplification. + template + CImg& draw_object3d(const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,zbuffer); + } + +#ifdef cimg_use_board + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type=4, + const bool is_double_sided=false, const float focale=700, + const float lightx=0, const float lighty=0, const float lightz=-5e8, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const float g_opacity=1) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,CImg::empty()); + } + + template + CImg& draw_object3d(LibBoard::Board& board, + const float x0, const float y0, const float z0, + const CImg& vertices, const CImgList& primitives, + const CImgList& colors, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, CImg& zbuffer) { + return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg::const_empty(), + render_type,is_double_sided,focale,lightx,lighty,lightz, + specular_lightness,specular_shininess,g_opacity,zbuffer); + } +#endif + + template + static float __draw_object3d(const CImgList& opacities, const unsigned int n_primitive, CImg& opacity) { + if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; } + if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); } + opacity.assign(opacities[n_primitive],true); + return 1.f; + } + + template + static float __draw_object3d(const CImg& opacities, const unsigned int n_primitive, CImg& opacity) { + opacity.assign(); + return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive]; + } + + template + static float ___draw_object3d(const CImgList& opacities, const unsigned int n_primitive) { + return n_primitive + static float ___draw_object3d(const CImg& opacities, const unsigned int n_primitive) { + return n_primitive + CImg& _draw_object3d(void *const pboard, CImg& zbuffer, + const float X, const float Y, const float Z, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const unsigned int render_type, + const bool is_double_sided, const float focale, + const float lightx, const float lighty, const float lightz, + const float specular_lightness, const float specular_shininess, + const float g_opacity, const float sprite_scale) { + typedef typename cimg::superset2::type tpfloat; + typedef typename to::value_type _to; + if (is_empty() || !vertices || !primitives) return *this; + CImg error_message(1024); + if (!vertices.is_object3d(primitives,colors,opacities,false,error_message)) + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); +#ifndef cimg_use_board + if (pboard) return *this; +#endif + if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety + + const float + nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)), + nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess), + nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1), + nsl2 = 1 - 2*nsl1*nspec, + nsl3 = nspec2 - nsl1 - nsl2; + + // Create light texture for phong-like rendering. + CImg light_texture; + if (render_type==5) { + if (colors._width>primitives._width) { + static CImg default_light_texture; + static const tc *lptr = 0; + static tc ref_values[64] = { 0 }; + const CImg& img = colors.back(); + bool is_same_texture = (lptr==img._data); + if (is_same_texture) + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) { + is_same_texture = false; break; + } + if (!is_same_texture || default_light_texture._spectrum<_spectrum) { + (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum); + lptr = colors.back().data(); + for (unsigned int r = 0, j = 0; j<8; ++j) + for (unsigned int i = 0; i<8; ++i) + ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum); + } + light_texture.assign(default_light_texture,true); + } else { + static CImg default_light_texture; + static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0; + if (!default_light_texture || + lightx!=olightx || lighty!=olighty || lightz!=olightz || + specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) { + default_light_texture.assign(512,512); + const float + dlx = lightx - X, + dly = lighty - Y, + dlz = lightz - Z, + nl = cimg::hypot(dlx,dly,dlz), + nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl), + nly = (default_light_texture._height - 1)/2*(1 + dly/nl), + white[] = { 1 }; + default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white); + cimg_forXY(default_light_texture,x,y) { + const float factor = default_light_texture(x,y); + if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3); + } + default_light_texture.resize(-100,-100,1,_spectrum); + olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess; + } + light_texture.assign(default_light_texture,true); + } + } + + // Compute 3D to 2D projection. + CImg projections(vertices._width,2); + tpfloat parallzmin = cimg::type::max(); + const float absfocale = focale?cimg::abs(focale):0; + if (absfocale) { + cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) + cimg_forX(projections,l) { // Perspective projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + const tpfloat projectedz = z + Z + absfocale; + projections(l,1) = Y + absfocale*y/projectedz; + projections(l,0) = X + absfocale*x/projectedz; + } + } else { + cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096)) + cimg_forX(projections,l) { // Parallel projection + const tpfloat + x = (tpfloat)vertices(l,0), + y = (tpfloat)vertices(l,1), + z = (tpfloat)vertices(l,2); + if (z visibles(primitives._width,1,1,1,~0U); + CImg zrange(primitives._width); + const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type::min(); + bool is_forward = zbuffer?true:false; + + cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096)) + cimglist_for(primitives,l) { + const CImg& primitive = primitives[l]; + switch (primitive.size()) { + case 1 : { // Point + CImg<_to> _opacity; + __draw_object3d(opacities,l,_opacity); + if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false; + const unsigned int i0 = (unsigned int)primitive(0); + const tpfloat z0 = Z + vertices(i0,2); + if (z0>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = z0; + } + } break; + case 5 : { // Sphere + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)), + Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)), + Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)), + _zc = Z + Zc, + zc = _zc + _focale, + xc = X + Xc*(absfocale?absfocale/zc:1), + yc = Y + Yc*(absfocale?absfocale/zc:1), + radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0), + vertices(i1,1) - vertices(i0,1), + vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1), + xm = xc - radius, + ym = yc - radius, + xM = xc + radius, + yM = yc + radius; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = _zc; + } + is_forward = false; + } break; + case 2 : case 6 : { // Segment + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2); + tpfloat xm, xM, ym, yM; + if (x0=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1)/2; + } + } break; + case 3 : case 9 : { // Triangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (y0yM) yM = y2; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) { + const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0); + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2)/3; + } + } + } break; + case 4 : case 12 : { // Quadrangle + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = (unsigned int)primitive(3); + const tpfloat + x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2), + x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2), + x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2), + x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2); + tpfloat xm, xM, ym, yM; + if (x0xM) xM = x2; + if (x3xM) xM = x3; + if (y0yM) yM = y2; + if (y3yM) yM = y3; + if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) { + const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0); + if (is_double_sided || d<0) { + visibles(l) = (unsigned int)l; + zrange(l) = (z0 + z1 + z2 + z3)/4; + } + } + } break; + default : + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Invalid primitive[%u] with size %u " + "(should have size 1,2,3,4,5,6,9 or 12).", + cimg_instance, + l,primitive.size()); + } + } + + // Force transparent primitives to be drawn last when zbuffer is activated + // (and if object contains no spheres or sprites). + if (is_forward) + cimglist_for(primitives,l) + if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l); + + // Sort only visibles primitives. + unsigned int *p_visibles = visibles._data; + tpfloat *p_zrange = zrange._data; + const tpfloat *ptrz = p_zrange; + cimg_for(visibles,ptr,unsigned int) { + if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; } + ++ptrz; + } + const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data); + if (!nb_visibles) { + if (render_type==5) cimg::mutex(10,0); + return *this; + } + CImg permutations; + CImg(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward); + + // Compute light properties + CImg lightprops; + switch (render_type) { + case 3 : { // Flat Shading + lightprops.assign(nb_visibles); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const CImg& primitive = primitives(visibles(permutations(l))); + const unsigned int psize = (unsigned int)primitive.size(); + if (psize==3 || psize==4 || psize==9 || psize==12) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2); + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nx = dy1*dz2 - dz1*dy2, + ny = dz1*dx2 - dx1*dz2, + nz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + cimg::hypot(nx,ny,nz), + lx = X + (x0 + x1 + x2)/3 - lightx, + ly = Y + (y0 + y1 + y2)/3 - lighty, + lz = Z + (z0 + z1 + z2)/3 - lightz, + nl = 1e-5f + cimg::hypot(lx,ly,lz), + factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } else lightprops[l] = 1; + } + } break; + + case 4 : // Gouraud Shading + case 5 : { // Phong-Shading + CImg vertices_normals(vertices._width,6,1,1,0); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + for (int l = 0; l<(int)nb_visibles; ++l) { + const CImg& primitive = primitives[visibles(l)]; + const unsigned int psize = (unsigned int)primitive.size(); + const bool + triangle_flag = (psize==3) || (psize==9), + quadrangle_flag = (psize==4) || (psize==12); + if (triangle_flag || quadrangle_flag) { + const unsigned int + i0 = (unsigned int)primitive(0), + i1 = (unsigned int)primitive(1), + i2 = (unsigned int)primitive(2), + i3 = quadrangle_flag?(unsigned int)primitive(3):0; + const tpfloat + x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2), + x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2), + x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2), + dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0, + dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0, + nnx = dy1*dz2 - dz1*dy2, + nny = dz1*dx2 - dx1*dz2, + nnz = dx1*dy2 - dy1*dx2, + norm = 1e-5f + cimg::hypot(nnx,nny,nnz), + nx = nnx/norm, + ny = nny/norm, + nz = nnz/norm; + unsigned int ix = 0, iy = 1, iz = 2; + if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; } + vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz; + vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz; + vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz; + if (quadrangle_flag) { + vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz; + } + } + } + + if (is_double_sided) cimg_forX(vertices_normals,p) { + const float + nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2), + nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5), + n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1; + if (n1>n0) { + vertices_normals(p,0) = -nx1; + vertices_normals(p,1) = -ny1; + vertices_normals(p,2) = -nz1; + } + } + + if (render_type==4) { + lightprops.assign(vertices._width); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = 1e-5f + cimg::hypot(nx,ny,nz), + lx = X + vertices(l,0) - lightx, + ly = Y + vertices(l,1) - lighty, + lz = Z + vertices(l,2) - lightz, + nl = 1e-5f + cimg::hypot(lx,ly,lz), + factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0); + lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3); + } + } else { + const unsigned int + lw2 = light_texture._width/2 - 1, + lh2 = light_texture._height/2 - 1; + lightprops.assign(vertices._width,2); + cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096)) + cimg_forX(lightprops,l) { + const tpfloat + nx = vertices_normals(l,0), + ny = vertices_normals(l,1), + nz = vertices_normals(l,2), + norm = 1e-5f + cimg::hypot(nx,ny,nz), + nnx = nx/norm, + nny = ny/norm; + lightprops(l,0) = lw2*(1 + nnx); + lightprops(l,1) = lh2*(1 + nny); + } + } + } break; + } + + // Draw visible primitives + const CImg default_color(1,_spectrum,1,1,(tc)200); + CImg<_to> _opacity; + + for (unsigned int l = 0; l& primitive = primitives[n_primitive]; + const CImg + &__color = n_primitive(), + _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)? + __color.get_resize(-100,-100,-100,_spectrum,0):CImg(), + &color = _color?_color:(__color?__color:default_color); + const tc *const pcolor = color._data; + float opacity = __draw_object3d(opacities,n_primitive,_opacity); + if (_opacity.is_empty()) opacity*=g_opacity; + +#ifdef cimg_use_board + LibBoard::Board &board = *(LibBoard::Board*)pboard; +#endif + + switch (primitive.size()) { + case 1 : { // Colored point or sprite + const unsigned int n0 = (unsigned int)primitive[0]; + const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)); + + if (_opacity.is_empty()) { // Scalar opacity + + if (color.size()==_spectrum) { // Colored point + draw_point(x0,y0,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height()-(float)y0); + } +#endif + } else { // Sprite + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(color._width*factor), + _sh = (unsigned int)(color._height*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && + (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + draw_image(nx0,ny0,sprite,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); + } +#endif + } + } + } else { // Opacity mask + const tpfloat z = Z + vertices(n0,2); + const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1); + const unsigned int + _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor), + _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor), + sw = _sw?_sw:1, sh = _sh?_sh:1; + const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2; + if (sw<=3*_width/2 && sh<=3*_height/2 && + (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2=0 || ny0 - (int)sh/2 + _sprite = (sw!=color._width || sh!=color._height)? + color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg(), + &sprite = _sprite?_sprite:color; + const CImg<_to> + _nopacity = (sw!=_opacity._width || sh!=_opacity._height)? + _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(), + &nopacity = _nopacity?_nopacity:_opacity; + draw_image(nx0,ny0,sprite,nopacity,g_opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128); + board.setFillColor(LibBoard::Color::Null); + board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh); + } +#endif + } + } + } break; + case 2 : { // Colored line + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity); + else draw_line(x0,y0,x1,y1,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1); + } +#endif + } else { + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + } +#endif + } + } break; + case 5 : { // Colored sphere + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + is_wireframe = (unsigned int)primitive[2], + is_radius = (unsigned int)primitive[3]; + float Xc,Yc,Zc,radius; + if (is_radius) { + Xc = (float)vertices(n0,0); + Yc = (float)vertices(n0,1); + Zc = (float)vertices(n0,2); + radius = cimg::hypot(vertices(n1,0) - vertices(n0,0), + vertices(n1,1) - vertices(n0,1), + vertices(n1,2) - vertices(n0,2)); + } else { + Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)); + Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)); + Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)); + radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0), + vertices(n1,1) - vertices(n0,1), + vertices(n1,2) - vertices(n0,2)); + } + const float + zc = Z + Zc + _focale, + af = absfocale?absfocale/zc:1, + xc = X + Xc*af, + yc = Y + Yc*af; + radius*=af; + + switch (render_type) { + case 0 : + draw_point((int)xc,(int)yc,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot(xc,height() - yc); + } +#endif + break; + case 1 : + draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.setFillColor(LibBoard::Color::Null); + board.drawCircle(xc,height() - yc,radius); + } +#endif + break; + default : + if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U); + else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + if (!is_wireframe) board.fillCircle(xc,height() - yc,radius); + else { + board.setFillColor(LibBoard::Color::Null); + board.drawCircle(xc,height() - yc,radius); + } + } +#endif + break; + } + } break; + case 6 : { // Textured line + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for line primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1]; + const int + tx0 = (int)primitive[2], ty0 = (int)primitive[3], + tx1 = (int)primitive[4], ty1 = (int)primitive[5], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale; + if (render_type) { + if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + } +#endif + } else { + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + } +#endif + } + } break; + case 3 : { // Colored triangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity). + draw_line(x1,y1,x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 3 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)); + else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), + (float)x1,height() - (float)y1,lightprops(n1), + (float)x2,height() - (float)y2,lightprops(n2)); + } +#endif + break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), + (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), + (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), + (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + } +#endif + } break; + } + } break; + case 4 : { // Colored quadrangle + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)), + x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)), + xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4; + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale, + zc = (z0 + z1 + z2 + z3)/4; + + switch (render_type) { + case 0 : + draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity). + draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + board.drawDot((float)x3,height() - (float)y3); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity); + else + draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity). + draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); + board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity); + else + draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l)); + else + _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)). + _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(color[0]*lp), + (unsigned char)(color[1]*lp), + (unsigned char)(color[2]*lp),(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3), + lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4; + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity). + draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity). + draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity). + draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity); + else + draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity). + draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity). + draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity). + draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x1,height() - (float)y1,lightprop1, + (float)x2,height() - (float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x2,height() - (float)y2,lightprop2, + (float)x3,height() - (float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)), + lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)), + lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4; + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). + draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). + draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). + draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); + else + draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity). + draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity). + draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity). + draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); + board.setPenColorRGBi((unsigned char)(color[0]), + (unsigned char)(color[1]), + (unsigned char)(color[2]), + (unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x2,height() - (float)y2,l2, + (float)x3,height() - (float)y3,l3); + } +#endif + } break; + } + } break; + case 9 : { // Textured triangle + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for triangle primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2]; + const int + tx0 = (int)primitive[3], ty0 = (int)primitive[4], + tx1 = (int)primitive[5], ty1 = (int)primitive[6], + tx2 = (int)primitive[7], ty2 = (int)primitive[8], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale; + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, + ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity); +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + } +#endif + break; + case 2 : + if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); + else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + } +#endif + break; + case 4 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprops(n0),lightprops(n1),lightprops(n2),opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0), + (float)x1,height() - (float)y1,lightprops(n1), + (float)x2,height() - (float)y2,lightprops(n2)); + } +#endif + break; + case 5 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), + opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, + (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1), + (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1), + (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1), + opacity); + +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))), + (int)(light_texture.height()/2*(1 + lightprops(n0,1)))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))), + (int)(light_texture.height()/2*(1 + lightprops(n1,1)))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))), + (int)(light_texture.height()/2*(1 + lightprops(n2,1)))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + } +#endif + break; + } + } break; + case 12 : { // Textured quadrangle + if (!__color) { + if (render_type==5) cimg::mutex(10,0); + throw CImgArgumentException(_cimg_instance + "draw_object3d(): Undefined texture for quadrangle primitive [%u].", + cimg_instance,n_primitive); + } + const unsigned int + n0 = (unsigned int)primitive[0], + n1 = (unsigned int)primitive[1], + n2 = (unsigned int)primitive[2], + n3 = (unsigned int)primitive[3]; + const int + tx0 = (int)primitive[4], ty0 = (int)primitive[5], + tx1 = (int)primitive[6], ty1 = (int)primitive[7], + tx2 = (int)primitive[8], ty2 = (int)primitive[9], + tx3 = (int)primitive[10], ty3 = (int)primitive[11], + x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)), + x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)), + x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)), + x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)); + const float + z0 = vertices(n0,2) + Z + _focale, + z1 = vertices(n1,2) + Z + _focale, + z2 = vertices(n2,2) + Z + _focale, + z3 = vertices(n3,2) + Z + _focale; + + switch (render_type) { + case 0 : + draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0, + ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity). + draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1, + ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity). + draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2, + ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity). + draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3, + ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawDot((float)x0,height() - (float)y0); + board.drawDot((float)x1,height() - (float)y1); + board.drawDot((float)x2,height() - (float)y2); + board.drawDot((float)x3,height() - (float)y3); + } +#endif + break; + case 1 : + if (zbuffer) + draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + else + draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity). + draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity). + draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity). + draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1); + board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2); + board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3); + board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0); + } +#endif + break; + case 2 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 3 : + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l)); + +#ifdef cimg_use_board + if (pboard) { + const float lp = std::min(lightprops(l),1.f); + board.setPenColorRGBi((unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(128*lp), + (unsigned char)(opacity*255)); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x1,height() - (float)y1, + (float)x2,height() - (float)y2); + board.fillTriangle((float)x0,height() - (float)y0, + (float)x2,height() - (float)y2, + (float)x3,height() - (float)y3); + } +#endif + break; + case 4 : { + const float + lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), + lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + lightprop0,lightprop1,lightprop2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + lightprop0,lightprop2,lightprop3,opacity); + +#ifdef cimg_use_board + if (pboard) { + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0, + (float)x1,height() - (float)y1,lightprop1, + (float)x2,height() - (float)y2,lightprop2); + board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0, + (float)x2,height() - (float)y2,lightprop2, + (float)x3,height() - (float)y3,lightprop3); + } +#endif + } break; + case 5 : { + const unsigned int + lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)), + lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)), + lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)), + lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)); + if (zbuffer) + draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); + else + draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2, + light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity). + draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3, + light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity); +#ifdef cimg_use_board + if (pboard) { + const float + l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))), + l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))), + l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))), + l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3))); + board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255)); + board.fillGouraudTriangle((float)x0,height() - (float)y0,l0, + (float)x1,height() - (float)y1,l1, + (float)x2,height() - (float)y2,l2); + board.fillGouraudTriangle((float)x0,height() -(float)y0,l0, + (float)x2,height() - (float)y2,l2, + (float)x3,height() - (float)y3,l3); + } +#endif + } break; + } + } break; + } + } + if (render_type==5) cimg::mutex(10,0); + return *this; + } + + //@} + //--------------------------- + // + //! \name Data Input + //@{ + //--------------------------- + + //! Launch simple interface to select a shape from an image. + /** + \param disp Display window to use. + \param feature_type Type of feature to select. Can be { 0=point | 1=line | 2=rectangle | 3=ellipse }. + \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images. + \param exit_on_anykey Exit function when any key is pressed. + **/ + CImg& select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); + } + + //! Simple interface to select a shape from an image \overloading. + CImg& select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) { + return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(CImgDisplay &disp, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); + } + + //! Simple interface to select a shape from an image \newinstance. + CImg get_select(const char *const title, + const unsigned int feature_type=2, unsigned int *const XYZ=0, + const bool exit_on_anykey=false, + const bool is_deep_selection_default=false) const { + CImgDisplay disp; + return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default); + } + + CImg _select(CImgDisplay &disp, const char *const title, + const unsigned int feature_type, unsigned int *const XYZ, + const int origX, const int origY, const int origZ, + const bool exit_on_anykey, + const bool reset_view3d, + const bool force_display_z_coord, + const bool is_deep_selection_default) const { + if (is_empty()) return CImg(1,feature_type==0?3:6,1,1,-1); + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + } else { + if (title) disp.set_title("%s",title); + disp.move_inside_screen(); + } + + CImg thumb; + if (width()>disp.screen_width() || height()>disp.screen_height()) + get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb); + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).set_wheel().show_mouse(); + + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + int area = 0, area_started = 0, area_clicked = 0, phase = 0, + X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), + Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), + Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth), + X1 =-1, Y1 = -1, Z1 = -1, + X3d = -1, Y3d = -1, + oX3d = X3d, oY3d = -1, + omx = -1, omy = -1; + float X = -1, Y = -1, Z = -1; + unsigned int key = 0, font_size = 32; + + bool is_deep_selection = is_deep_selection_default, + shape_selected = false, text_down = false, visible_cursor = true; + static CImg pose3d; + static bool is_view3d = false, is_axes = true; + if (reset_view3d) { pose3d.assign(); is_view3d = false; } + CImg points3d, opacities3d, sel_opacities3d; + CImgList primitives3d, sel_primitives3d; + CImgList colors3d, sel_colors3d; + CImg visu, visu0, view3d; + CImg text(1024); *text = 0; + + while (!key && !disp.is_closed() && !shape_selected) { + + // Handle mouse motion and selection + int + mx = disp.mouse_x(), + my = disp.mouse_y(); + + const float + mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height(); + + area = 0; + if (mX>=0 && mY>=0 && mX=0 && mX=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); } + if (mY>=0 && mX>=width() && mY=width() && mY>=height()) area = 4; + if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0; + + CImg filename(32); + + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break; + case cimg::keyPAGEDOWN : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break; + case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp); + visu0.save(filename); + (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + } + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp); + save(filename); + (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + } + + switch (area) { + + case 0 : // When mouse is out of image range + mx = my = -1; X = Y = Z = -1; + break; + + case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections + const unsigned int but = disp.button(); + const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4); + + if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step) + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; + } + if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes) + switch (area_started) { + case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break; + case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break; + case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break; + } + } + if (b2 && area_clicked==area) { // When moving through the image/volume + if (phase) { + if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign(); + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; + } else { + if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign(); + X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z; + } + } + if (b3) { // Reset selection + X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0; + visu0.assign(); + } + if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel) + if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && + !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) { + switch (area) { + case 1 : + if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel()); + visu0.assign(); break; + case 2 : + if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel()); + visu0.assign(); break; + case 3 : + if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel()); + visu0.assign(); break; + } + disp.set_wheel(); + } else key = ~0U; + } + + if ((phase==0 && b1) || + (phase==1 && !b1) || + (phase==2 && b1)) switch (phase) { // Detect change of phase + case 0 : + if (area==area_clicked) { + X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase; + } break; + case 1 : + if (area==area_started) { + X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase; + if (_depth>1) { + if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default; + if (is_deep_selection) ++phase; + } + } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); } + break; + case 2 : ++phase; break; + } + } break; + + case 4 : // When mouse is over the 3D view + if (is_view3d && points3d) { + X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0)); + Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0)); + if (oX3d<0) { oX3d = X3d; oY3d = Y3d; } + // Left + right buttons: reset. + if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } + else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate + const float + R = 0.45f*std::min(view3d._width,view3d._height), + R2 = R*R, + u0 = (float)(oX3d - view3d.width()/2), + v0 = (float)(oY3d - view3d.height()/2), + u1 = (float)(X3d - view3d.width()/2), + v1 = (float)(Y3d - view3d.height()/2), + n0 = cimg::hypot(u0,v0), + n1 = cimg::hypot(u1,v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), + u = nv0*nw1 - nw0*nv1, + v = nw0*nu1 - nu0*nw1, + w = nv0*nu1 - nu0*nv1, + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + pose3d.draw_image(CImg::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2)); + view3d.assign(); + } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom + pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign(); + } + if (disp.wheel()) { // Wheel: zoom + pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel(); + } + if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift + pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign(); + } + oX3d = X3d; oY3d = Y3d; + } + mx = my = -1; X = Y = Z = -1; + break; + } + + if (phase) { + if (!feature_type) shape_selected = phase?true:false; + else { + if (_depth>1) shape_selected = (phase==3)?true:false; + else shape_selected = (phase==2)?true:false; + } + } + + if (X0<0) X0 = 0; + if (X0>=width()) X0 = width() - 1; + if (Y0<0) Y0 = 0; + if (Y0>=height()) Y0 = height() - 1; + if (Z0<0) Z0 = 0; + if (Z0>=depth()) Z0 = depth() - 1; + if (X1<1) X1 = 0; + if (X1>=width()) X1 = width() - 1; + if (Y1<0) Y1 = 0; + if (Y1>=height()) Y1 = height() - 1; + if (Z1<0) Z1 = 0; + if (Z1>=depth()) Z1 = depth() - 1; + + // Draw visualization image on the display + if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) { + + if (!visu0) { // Create image of projected planes + if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0); + visu0.resize(disp); + view3d.assign(); + points3d.assign(); + } + + if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images + const unsigned int + _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1), + _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1), + x3d = _x3d>=visu0._width?visu0._width - 1:_x3d, + y3d = _y3d>=visu0._height?visu0._height - 1:_y3d; + CImg(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3). + move_to(view3d); + if (!points3d) { + get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d); + points3d.append(CImg(8,3,1,1, + 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0, + 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1, + 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x'); + CImg::vector(12,13).move_to(primitives3d); CImg::vector(13,14).move_to(primitives3d); + CImg::vector(14,15).move_to(primitives3d); CImg::vector(15,12).move_to(primitives3d); + CImg::vector(16,17).move_to(primitives3d); CImg::vector(17,18).move_to(primitives3d); + CImg::vector(18,19).move_to(primitives3d); CImg::vector(19,16).move_to(primitives3d); + CImg::vector(12,16).move_to(primitives3d); CImg::vector(13,17).move_to(primitives3d); + CImg::vector(14,18).move_to(primitives3d); CImg::vector(15,19).move_to(primitives3d); + colors3d.insert(12,CImg::vector(255,255,255)); + opacities3d.assign(primitives3d.width(),1,1,1,0.5f); + if (!phase) { + opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f; + sel_primitives3d.assign(); + sel_colors3d.assign(); + sel_opacities3d.assign(); + } else { + if (feature_type==2) { + points3d.append(CImg(8,3,1,1, + X0,X1,X1,X0,X0,X1,X1,X0, + Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1, + Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x'); + sel_primitives3d.assign(); + CImg::vector(20,21).move_to(sel_primitives3d); + CImg::vector(21,22).move_to(sel_primitives3d); + CImg::vector(22,23).move_to(sel_primitives3d); + CImg::vector(23,20).move_to(sel_primitives3d); + CImg::vector(24,25).move_to(sel_primitives3d); + CImg::vector(25,26).move_to(sel_primitives3d); + CImg::vector(26,27).move_to(sel_primitives3d); + CImg::vector(27,24).move_to(sel_primitives3d); + CImg::vector(20,24).move_to(sel_primitives3d); + CImg::vector(21,25).move_to(sel_primitives3d); + CImg::vector(22,26).move_to(sel_primitives3d); + CImg::vector(23,27).move_to(sel_primitives3d); + } else { + points3d.append(CImg(2,3,1,1, + X0,X1, + Y0,Y1, + Z0,Z1),'x'); + sel_primitives3d.assign(CImg::vector(20,21)); + } + sel_colors3d.assign(sel_primitives3d._width,CImg::vector(255,255,255)); + sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f); + } + points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d(); + points3d*=0.75f*std::min(view3d._width,view3d._height); + } + + if (!pose3d) CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d); + CImg zbuffer3d(view3d._width,view3d._height,1,1,0); + const CImg rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d; + if (sel_primitives3d) + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d, + 2,true,500,0,0,0,0,0,1,zbuffer3d); + view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width, + pose3d(3,1) + 0.5f*view3d._height, + pose3d(3,2), + rotated_points3d,primitives3d,colors3d,opacities3d, + 2,true,500,0,0,0,0,0,1,zbuffer3d); + visu0.draw_image(x3d,y3d,view3d); + } + visu = visu0; + + if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} + else { + if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }} + else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }} + const int d = (depth()>1)?depth():0; + int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z; + if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; } + int + w = disp.width(), W = width() + d, + h = disp.height(), H = height() + d, + _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX), + _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY), + _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1), + _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1), + _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()), + _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()), + _zxn = (int)((_vZ + width() + 1.f)*w/W - 1), + zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1), + _zyn = (int)((_vZ + height() + 1.f)*h/H - 1), + zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1), + _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()), + _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()), + xc = (xp + xn)/2, + yc = (yp + yn)/2, + zxc = (zxp + zxn)/2, + zyc = (zyp + zyn)/2, + xf = (int)(X*w/W), + yf = (int)(Y*h/H), + zxf = (int)((Z + width())*w/W), + zyf = (int)((Z + height())*h/H); + + if (is_axes) { // Draw axes + visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00). + draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF). + draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00). + draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF); + if (_depth>1) + visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00). + draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF). + draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00). + draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF); + } + + // Draw box cursor. + if (xn - xp>=4 && yn - yp>=4) + visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f). + draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555); + if (_depth>1) { + if (yn - yp>=4 && zxn - zxp>=4) + visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f). + draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555); + if (xn - xp>=4 && zyn - zyp>=4) + visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f). + draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA). + draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555); + } + + // Draw selection. + if (phase && (phase!=1 || area_started==area)) { + const int + _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0), + _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0), + _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1), + _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1), + _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()), + _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()), + _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1), + zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1), + _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1), + zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1), + xc0 = (xp0 + xn0)/2, + yc0 = (yp0 + yn0)/2, + zxc0 = (zxp0 + zxn0)/2, + zyc0 = (zyp0 + zyn0)/2; + + switch (feature_type) { + case 1 : { // Vector + visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333). + draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC); + if (d) { + visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333). + draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC). + draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333). + draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC); + } + } break; + case 2 : { // Box + visu.draw_rectangle(X0=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false; + if (!feature_type || !phase) { + if (X>=0 && Y>=0 && Z>=0 && X1 || force_display_z_coord) + cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z); + else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y); + CImg values = get_vector_at((int)X,(int)Y,(int)Z); + const bool is_large_spectrum = values._height>8; + if (is_large_spectrum) + values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0); + char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512; + for (unsigned int c = 0; c::format_s(), + cimg::type::format(values[c])); + ctext += std::strlen(ctext); + if (c==3 && is_large_spectrum) { + cimg_snprintf(ctext,24," ..."); + ctext += std::strlen(ctext); + } + *(ctext++) = ' '; *ctext = 0; + } + std::strcpy(text._data + std::strlen(text),"] "); + } + } else switch (feature_type) { + case 1 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length); + else if (_width!=1 && _height!=1) + cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length, + cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1)); + else + cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ", + origX + X0,origY + Y0,origX + X1,origY + Y1,length); + } break; + case 2 : { + const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), + length = cimg::round(cimg::hypot(dX,dY,dZ),0.1); + if (_depth>1 || force_display_z_coord) + cimg_snprintf(text,text._width, + " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ", + origX + (X01 || force_display_z_coord) + cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ", + origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1, + 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1)); + else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ", + origX + X0,origY + Y0,origX + X1,origY + Y1, + 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1)); + } + if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data); + } + + disp.display(visu); + } + if (!shape_selected) disp.wait(); + if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); } + omx = mx; omy = my; + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + + // Return result. + CImg res(1,feature_type==0?3:6,1,1,-1); + if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } + if (shape_selected) { + if (feature_type==2) { + if (is_deep_selection) switch (area_started) { + case 1 : Z0 = 0; Z1 = _depth - 1; break; + case 2 : Y0 = 0; Y1 = _height - 1; break; + case 3 : X0 = 0; X1 = _width - 1; break; + } + if (X0>X1) cimg::swap(X0,X1); + if (Y0>Y1) cimg::swap(Y0,Y1); + if (Z0>Z1) cimg::swap(Z0,Z1); + } + if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; + switch (feature_type) { + case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break; + case 3 : + res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0); + res[0] = X0; res[1] = Y0; res[2] = Z0; + break; + default : res[0] = X0; res[1] = Y0; res[2] = Z0; + } + } + if (!exit_on_anykey || !(disp.button()&4)) disp.set_button(); + if (!visible_cursor) disp.show_mouse(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + if (key!=~0U) disp.set_key(key); + return res; + } + + // Return a visualizable uchar8 image for display routines. + CImg _get_select(const CImgDisplay& disp, const int normalization, + const int x, const int y, const int z) const { + if (is_empty()) return CImg(1,1,1,1,0); + const CImg crop = get_shared_channels(0,std::min(2,spectrum() - 1)); + CImg img2d; + if (_depth>1) { + const int mdisp = std::min(disp.screen_width(),disp.screen_height()); + if (depth()>mdisp) { + crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d); + img2d.projections2d(x,y,z*img2d._depth/_depth); + } else crop.get_projections2d(x,y,z).move_to(img2d); + } else CImg(crop,false).move_to(img2d); + + // Check for inf and NaN values. + if (cimg::type::is_float() && normalization) { + bool is_inf = false, is_nan = false; + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) { is_inf = true; break; } + else if (cimg::type::is_nan(*ptr)) { is_nan = true; break; } + if (is_inf || is_nan) { + Tint m0 = (Tint)cimg::type::max(), M0 = (Tint)cimg::type::min(); + if (!normalization) { m0 = 0; M0 = 255; } + else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; } + else + cimg_for(img2d,ptr,Tuchar) + if (!cimg::type::is_inf(*ptr) && !cimg::type::is_nan(*ptr)) { + if (*ptr<(Tuchar)m0) m0 = *ptr; + if (*ptr>(Tuchar)M0) M0 = *ptr; + } + const T + val_minf = (T)(normalization==1 || normalization==3?m0 - (M0 - m0)*20 - 1:m0), + val_pinf = (T)(normalization==1 || normalization==3?M0 + (M0 - m0)*20 + 1:M0); + if (is_nan) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values + if (is_inf) + cimg_for(img2d,ptr,Tuchar) + if (cimg::type::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values + } + } + + switch (normalization) { + case 1 : img2d.normalize((ucharT)0,(ucharT)255); break; + case 2 : { + const float m = disp._min, M = disp._max; + (img2d-=m)*=255.f/(M - m>0?M - m:1); + } break; + case 3 : + if (cimg::type::is_float()) img2d.normalize((ucharT)0,(ucharT)255); + else { + const float + m = (float)cimg::type::min(), + M = (float)cimg::type::max(); + (img2d-=m)*=255.f/(M - m>0?M - m:1); + } break; + } + if (img2d.spectrum()==2) img2d.channels(0,2); + return img2d; + } + + //! Select sub-graph in a graph. + CImg get_select_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "select_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). + set_title("CImg<%s>",pixel_type()); + const ulongT siz = (ulongT)_width*_height*_depth; + const unsigned int old_normalization = disp.normalization(); + disp.show().set_button().set_wheel()._normalization = 0; + + double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax; + if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; } + if (nymin==nymax) { --nymin; ++nymax; } + if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; } + + static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 }; + static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 }; + + CImg colormap(3,_spectrum); + if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; } + else { + colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10; + if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; } + if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; } + if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10; } + if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10; colormap(2,4) = 220; } + if (_spectrum>5) { colormap(0,5) = 10; colormap(1,5) = 220; colormap(2,5) = 220; } + if (_spectrum>6) { + cimg_uint64 rng = 10; + cimg_for_inY(colormap,6,colormap.height()-1,k) { + colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng)); + } + } + } + + CImg visu0, visu, graph, text, axes; + int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2; + const unsigned int one = plot_type==3?0U:1U; + unsigned int okey = 0, obutton = 0, font_size = 32; + CImg message(1024); + CImg_3x3(I,unsigned char); + + for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) { + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + const unsigned int key = disp.key(), button = disp.button(); + + // Generate graph representation. + if (!visu0) { + visu0.assign(disp.width(),disp.height(),1,3,220); + const int gdimx = disp.width() - 32, gdimy = disp.height() - 32; + if (gdimx>0 && gdimy>0) { + graph.assign(gdimx,gdimy,1,3,255); + if (siz<32) { + if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0, + false,true,black,0.2f,0x33333333,0x33333333); + } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333); + cimg_forC(*this,c) + graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f, + plot_type,vertex_type,nymax,nymin); + + axes.assign(gdimx,gdimy,1,1,0); + const float + dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin), + px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.), + py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.); + const CImg + seqx = dx<=0?CImg::vector(nxmin): + CImg::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz), + seqy = CImg::sequence(1 + gdimy/60,nymax,nymin); + + const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0); + axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py); + if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px); + if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px); + if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py); + if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py); + + cimg_for3x3(axes,x,y,0,0,I,unsigned char) + if (Icc) { + if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0; + else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3); + } + else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) + cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3); + + visu0.draw_image(16,16,graph); + visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2). + draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white); + } else graph.assign(); + text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3); + visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text); + text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3); + visu0.draw_image(1,(visu0.height() - text.height())/2,~text); + visu.assign(); + } + + // Generate and display current view. + if (!visu) { + visu.assign(visu0); + if (graph && x0>=0 && x1>=0) { + const int + nx0 = x0<=x1?x0:x1, + nx1 = x0<=x1?x1:x0, + ny0 = y0<=y1?y0:y1, + ny1 = y0<=y1?y1:y0, + sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)), + sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)), + sy0 = 16 + ny0, + sy1 = 16 + ny1; + if (y0>=0 && y1>=0) + visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU); + else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f). + draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU). + draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU); + } + if (mouse_x>=16 && mouse_y>=16 && mouse_x=7) + cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx, + (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2), + (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3), + (double)(*this)(x,0,0,_spectrum - 1)); + else { + cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx); + cimg_forC(*this,c) cimg_sprintf(message._data + std::strlen(message),"%g ",(double)(*this)(x,0,0,c)); + cimg_sprintf(message._data + std::strlen(message),")"); + } + if (x0>=0 && x1>=0) { + const unsigned int + nx0 = (unsigned int)(x0<=x1?x0:x1), + nx1 = (unsigned int)(x0<=x1?x1:x0), + ny0 = (unsigned int)(y0<=y1?y0:y1), + ny1 = (unsigned int)(y0<=y1?y1:y0); + const double + cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), + cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1), + cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32), + cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32); + if (y0>=0 && y1>=0) + cimg_sprintf(message._data + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )", + x0,cx0,cy0,x1 + one,cx1,cy1); + else + cimg_sprintf(message._data + std::strlen(message)," - Range [ %u:%g - %u:%g ]", + x0,cx0,x1 + one,cx1); + } + text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3); + visu.draw_image((visu.width() - text.width())/2,1,~text); + } + visu.display(disp); + } + + // Test keys. + CImg filename(32); + switch (okey = key) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + CImgDisplay::screen_height()/2,1),false)._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp); + screen.save(filename); + (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + if (visu || visu0) { + CImg &screen = visu?visu:visu0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp); + save(filename); + (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp); + } + disp.set_key(key,false); okey = 0; + } break; + } + + // Handle mouse motion and mouse buttons. + if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) { + visu.assign(); + if (disp.mouse_x()>=0 && disp.mouse_y()>=0) { + const int + mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32), + cx = cimg::cut(mx,0,(int)(siz - 1 - one)), + my = mouse_y - 16, + cy = cimg::cut(my,0,disp.height() - 32); + if (button&1) { + if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; } + } + else if (button&2) { + if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; } + } + else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; } + } else if (!button && obutton) selected = true; + obutton = button; omouse_x = mouse_x; omouse_y = mouse_y; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (visu && visu0) disp.wait(); + if (!exit_on_anykey && okey && okey!=cimg::keyESC && + (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + okey = 0; + } + } + + disp._normalization = old_normalization; + if (x1>=0 && x1(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1); + } + + //! Load image from a file. + /** + \param filename Filename, as a C-string. + \note The extension of \c filename defines the file format. If no filename + extension is provided, CImg::get_load() will try to load the file as a .cimg or .cimgz file. + **/ + CImg& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load(): Specified filename is (null).", + cimg_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg filename_local(256); + load(cimg::load_network(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + bool is_loaded = true; + try { +#ifdef cimg_load_plugin + cimg_load_plugin(filename); +#endif +#ifdef cimg_load_plugin1 + cimg_load_plugin1(filename); +#endif +#ifdef cimg_load_plugin2 + cimg_load_plugin2(filename); +#endif +#ifdef cimg_load_plugin3 + cimg_load_plugin3(filename); +#endif +#ifdef cimg_load_plugin4 + cimg_load_plugin4(filename); +#endif +#ifdef cimg_load_plugin5 + cimg_load_plugin5(filename); +#endif +#ifdef cimg_load_plugin6 + cimg_load_plugin6(filename); +#endif +#ifdef cimg_load_plugin7 + cimg_load_plugin7(filename); +#endif +#ifdef cimg_load_plugin8 + cimg_load_plugin8(filename); +#endif + // Text formats + if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename); + else if (!cimg::strcasecmp(ext,"csv") || + !cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) load_dlm(filename); + else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename); + + // 2D binary formats + else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) load_jpeg(filename); + else if (!cimg::strcasecmp(ext,"png")) load_png(filename); + else if (!cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"pnm") || + !cimg::strcasecmp(ext,"pbm") || + !cimg::strcasecmp(ext,"pnk")) load_pnm(filename); + else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename); + else if (!cimg::strcasecmp(ext,"cr2") || + !cimg::strcasecmp(ext,"crw") || + !cimg::strcasecmp(ext,"dcr") || + !cimg::strcasecmp(ext,"mrw") || + !cimg::strcasecmp(ext,"nef") || + !cimg::strcasecmp(ext,"orf") || + !cimg::strcasecmp(ext,"pix") || + !cimg::strcasecmp(ext,"ptx") || + !cimg::strcasecmp(ext,"raf") || + !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"heic") || + !cimg::strcasecmp(ext,"avif")) load_heif(filename); + + // 3D binary formats + else if (!cimg::strcasecmp(ext,"dcm") || + !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) load_analyze(filename); + else if (!cimg::strcasecmp(ext,"par") || + !cimg::strcasecmp(ext,"rec")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename); + else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) return load_cimg(filename); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + + // Image sequences + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + + // If nothing loaded, try to guess file format from magic number in file. + if (!is_loaded) { + std::FILE *file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load(): Failed to open file '%s'.", + cimg_instance, + filename); + } + + const char *const f_type = cimg::ftype(file,filename); + cimg::fclose(file); + is_loaded = true; + try { + if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename); + else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename); + else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename); + else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename); + else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename); + else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename); + else if (!cimg::strcasecmp(f_type,"png")) load_png(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + } + + // If nothing loaded, try to load file with other means. + if (!is_loaded) { + try { + load_other(filename); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load image from a file \newinstance. + static CImg get_load(const char *const filename) { + return CImg().load(filename); + } + + //! Load image from an ascii file. + /** + \param filename Filename, as a C -string. + **/ + CImg& load_ascii(const char *const filename) { + return _load_ascii(0,filename); + } + + //! Load image from an ascii file \inplace. + static CImg get_load_ascii(const char *const filename) { + return CImg().load_ascii(filename); + } + + //! Load image from an ascii file \overloading. + CImg& load_ascii(std::FILE *const file) { + return _load_ascii(file,0); + } + + //! Loadimage from an ascii file \newinstance. + static CImg get_load_ascii(std::FILE *const file) { + return CImg().load_ascii(file); + } + + CImg& _load_ascii(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_ascii(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg line(256); *line = 0; + int err = std::fscanf(nfile,"%255[^\n]",line._data); + unsigned int dx = 0, dy = 1, dz = 1, dc = 1; + cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc); + err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]"); + if (!dx || !dy || !dz || !dc) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_ascii(): Invalid ascii header in file '%s', image dimensions are set " + "to (%u,%u,%u,%u).", + cimg_instance, + filename?filename:"(FILE*)",dx,dy,dz,dc); + } + assign(dx,dy,dz,dc); + const ulongT siz = size(); + ulongT off = 0; + double val; + T *ptr = _data; + for (err = 1, off = 0; off& load_dlm(const char *const filename) { + return _load_dlm(0,filename); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(const char *const filename) { + return CImg().load_dlm(filename); + } + + //! Load image from a DLM file \overloading. + CImg& load_dlm(std::FILE *const file) { + return _load_dlm(file,0); + } + + //! Load image from a DLM file \newinstance. + static CImg get_load_dlm(std::FILE *const file) { + return CImg().load_dlm(file); + } + + CImg& _load_dlm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_dlm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + CImg delimiter(256), tmp(256); *delimiter = *tmp = 0; + unsigned int cdx = 0, dx = 0, dy = 0; + int err = 0; + double val; + assign(256,256,1,1,(T)0); + while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) { + if (err>0) (*this)(cdx++,dy) = (T)val; + if (cdx>=_width) resize(3*_width/2,_height,1,1,0); + char c = 0; + if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') { + dx = std::max(cdx,dx); + if (++dy>=_height) resize(_width,3*_height/2,1,1,0); + cdx = 0; + } + } + if (cdx && err==1) { dx = cdx; ++dy; } + if (!dx || !dy) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_dlm(): Invalid DLM file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + resize(dx,dy,1,1,0); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a BMP file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_bmp(const char *const filename) { + return _load_bmp(0,filename); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(const char *const filename) { + return CImg().load_bmp(filename); + } + + //! Load image from a BMP file \overloading. + CImg& load_bmp(std::FILE *const file) { + return _load_bmp(file,0); + } + + //! Load image from a BMP file \newinstance. + static CImg get_load_bmp(std::FILE *const file) { + return CImg().load_bmp(file); + } + + CImg& _load_bmp(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_bmp(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg header(54); + cimg::fread(header._data,54,nfile); + if (*header!='B' || header[1]!='M') { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_bmp(): Invalid BMP file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read header and pixel buffer + int + file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), + offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), + header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24), + dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), + dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), + compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), + nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), + bpp = header[0x1C] + (header[0x1D]<<8); + + if (!file_size || file_size==offset) { + cimg::fseek(nfile,0,SEEK_END); + file_size = (int)cimg::ftell(nfile); + cimg::fseek(nfile,54,SEEK_SET); + } + if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR); + + const int + dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)), + align_bytes = (4 - dx_bytes%4)%4; + const ulongT + cimg_iobuffer = (ulongT)24*1024*1024, + buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes); + + CImg colormap; + if (bpp<16) { if (!nb_colors) nb_colors = 1<0) cimg::fseek(nfile,xoffset,SEEK_CUR); + + CImg buffer; + if (buf_size=2) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + unsigned char mask = 0x80, val = 0; + cimg_forX(*this,x) { + if (mask==0x80) val = *(ptrs++); + const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask); + } + ptrs+=align_bytes; + } + } break; + case 4 : { // 16 colors + if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + unsigned char mask = 0xF0, val = 0; + cimg_forX(*this,x) { + if (mask==0xF0) val = *(ptrs++); + const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); + const unsigned char *col = (unsigned char*)(colormap._data + color); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + mask = cimg::ror(mask,4); + } + ptrs+=align_bytes; + } + } break; + case 8 : { // 256 colors + if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++)); + (*this)(x,y,2) = (T)*(col++); + (*this)(x,y,1) = (T)*(col++); + (*this)(x,y,0) = (T)*(col++); + } + ptrs+=align_bytes; + } + } break; + case 16 : { // 16 bits colors (RGB565) + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); + const unsigned short col = (unsigned short)c2<<8 | c1; + (*this)(x,y,2) = (T)((col&0x1F)<<3); + (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3); + (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3); + } + ptrs+=align_bytes; + } + } break; + case 24 : { // 24 bits colors + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + } + ptrs+=align_bytes; + } + } break; + case 32 : { // 32 bits colors + for (int y = height() - 1; y>=0; --y) { + if (buf_size>=cimg_iobuffer) { + if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break; + cimg::fseek(nfile,align_bytes,SEEK_CUR); + } + cimg_forX(*this,x) { + (*this)(x,y,2) = (T)*(ptrs++); + (*this)(x,y,1) = (T)*(ptrs++); + (*this)(x,y,0) = (T)*(ptrs++); + ++ptrs; + } + ptrs+=align_bytes; + } + } break; + } + if (dy<0) mirror('y'); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a JPEG file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_jpeg(const char *const filename) { + return _load_jpeg(0,filename); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(const char *const filename) { + return CImg().load_jpeg(filename); + } + + //! Load image from a JPEG file \overloading. + CImg& load_jpeg(std::FILE *const file) { + return _load_jpeg(file,0); + } + + //! Load image from a JPEG file \newinstance. + static CImg get_load_jpeg(std::FILE *const file) { + return CImg().load_jpeg(file); + } + + // Custom error handler for libjpeg. +#ifdef cimg_use_jpeg + struct _cimg_error_mgr { + struct jpeg_error_mgr original; + jmp_buf setjmp_buffer; + char message[JMSG_LENGTH_MAX]; + }; + + typedef struct _cimg_error_mgr *_cimg_error_ptr; + + METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) { + _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point + (*cinfo->err->format_message)(cinfo,c_err->message); + jpeg_destroy(cinfo); // Clean memory and temp files + longjmp(c_err->setjmp_buffer,1); + } +#endif + + CImg& _load_jpeg(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_jpeg(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_jpeg + if (file) + throw CImgIOException(_cimg_instance + "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.", + cimg_instance); + else return load_other(filename); +#else + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + struct jpeg_decompress_struct cinfo; + struct _cimg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr.original); + jerr.original.error_exit = _cimg_jpeg_error_exit; + if (setjmp(jerr.setjmp_buffer)) { // JPEG error + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_jpeg(): Error message returned by libjpeg: %s.", + cimg_instance,jerr.message); + } + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo,nfile); + jpeg_read_header(&cinfo,TRUE); + jpeg_start_decompress(&cinfo); + + if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { + if (!file) { + cimg::fclose(nfile); + return load_other(filename); + } else + throw CImgIOException(_cimg_instance + "load_jpeg(): Failed to load JPEG data from file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + } + CImg buffer(cinfo.output_width*cinfo.output_components); + JSAMPROW row_pointer[1]; + try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); } + catch (...) { if (!file) cimg::fclose(nfile); throw; } + T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, + *ptr_a = _data + 3UL*_width*_height; + while (cinfo.output_scanline + // This is experimental code, not much tested, use with care. + CImg& load_magick(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_magick(): Specified filename is (null).", + cimg_instance); + +#ifdef cimg_use_magick + Magick::Image image(filename); + const unsigned int W = image.size().width(), H = image.size().height(); + switch (image.type()) { + case Magick::PaletteMatteType : + case Magick::TrueColorMatteType : + case Magick::ColorSeparationType : { + assign(W,H,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + case Magick::PaletteType : + case Magick::TrueColorType : { + assign(W,H,1,3); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_g++) = (T)(pixels->green); + *(ptr_b++) = (T)(pixels->blue); + ++pixels; + } + } break; + case Magick::GrayscaleMatteType : { + assign(W,H,1,2); + T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + *(ptr_a++) = (T)(pixels->opacity); + ++pixels; + } + } break; + default : { + assign(W,H,1,1); + T *ptr_r = data(0,0,0,0); + Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); + for (ulongT off = (ulongT)W*H; off; --off) { + *(ptr_r++) = (T)(pixels->red); + ++pixels; + } + } + } + return *this; +#else + throw CImgIOException(_cimg_instance + "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + } + + //! Load image from a file, using Magick++ library \newinstance. + static CImg get_load_magick(const char *const filename) { + return CImg().load_magick(filename); + } + + //! Load image from a PNG file. + /** + \param filename Filename, as a C-string. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + **/ + CImg& load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return _load_png(0,filename,bits_per_value); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(const char *const filename, unsigned int *const bits_per_value=0) { + return CImg().load_png(filename,bits_per_value); + } + + //! Load image from a PNG file \overloading. + CImg& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return _load_png(file,0,bits_per_value); + } + + //! Load image from a PNG file \newinstance. + static CImg get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) { + return CImg().load_png(file,bits_per_value); + } + + // (Note: Most of this function has been written by Eric Fausett) + CImg& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_png(): Specified filename is (null).", + cimg_instance); + +#ifndef cimg_use_png + cimg::unused(bits_per_value); + if (file) + throw CImgIOException(_cimg_instance + "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.", + cimg_instance); + + else return load_other(filename); +#else + // Open file and check for PNG validity +#if defined __GNUC__ + const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb"); +#else + const char *nfilename = filename; + std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb"); +#endif + unsigned char pngCheck[8] = { 0 }; + cimg::fread(pngCheck,8,(std::FILE*)nfile); + if (png_sig_cmp(pngCheck,0,8)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Invalid PNG file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Setup PNG structures for read + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn); + if (!png_ptr) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Failed to initialize 'end_info' structure for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + + // Error handling callback for png file reading + if (setjmp(png_jmpbuf(png_ptr))) { + if (!file) cimg::fclose((std::FILE*)nfile); + png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Encountered unknown fatal error in libpng for file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + png_set_sig_bytes(png_ptr, 8); + + // Get PNG Header Info up to data block + png_read_info(png_ptr,info_ptr); + png_uint_32 W, H; + int bit_depth, color_type, interlace_type; + bool is_gray = false; + png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0); + if (bits_per_value) *bits_per_value = (unsigned int)bit_depth; + + // Transforms to unify image data + if (color_type==PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + color_type = PNG_COLOR_TYPE_RGB; + bit_depth = 8; + } + if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + is_gray = true; + bit_depth = 8; + } + if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + color_type |= PNG_COLOR_MASK_ALPHA; + } + if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + color_type |= PNG_COLOR_MASK_COLOR; + is_gray = true; + } + if (color_type==PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER); + + png_read_update_info(png_ptr,info_ptr); + if (bit_depth!=8 && bit_depth!=16) { + if (!file) cimg::fclose(nfile); + png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0); + throw CImgIOException(_cimg_instance + "load_png(): Invalid bit depth %u in file '%s'.", + cimg_instance, + bit_depth,nfilename?nfilename:"(FILE*)"); + } + const int byte_depth = bit_depth>>3; + + // Allocate memory for image reading + png_bytep *const imgData = new png_bytep[H]; + for (unsigned int row = 0; row& load_pnm(const char *const filename) { + return _load_pnm(0,filename); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(const char *const filename) { + return CImg().load_pnm(filename); + } + + //! Load image from a PNM file \overloading. + CImg& load_pnm(std::FILE *const file) { + return _load_pnm(file,0); + } + + //! Load image from a PNM file \newinstance. + static CImg get_load_pnm(std::FILE *const file) { + return CImg().load_pnm(file); + } + + CImg& _load_pnm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pnm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + unsigned int ppm_type, W, H, D = 1, colormax = 255; + CImg item(16384,1,1,1,0); + int err, rval, gval, bval; + const longT cimg_iobuffer = (longT)24*1024*1024; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item," P%u",&ppm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (ppm_type!=1 && ppm_type!=4) { + if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item,"%u",&colormax)!=1) + cimg::warn(_cimg_instance + "load_pnm(): COLORMAX field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else { colormax = D; D = 1; } + } + std::fgetc(nfile); + + if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension + const cimg_int64 siz = cimg::fsize(filename); + if (W*H*D>siz) + throw CImgIOException(_cimg_instance + "load_pnm(): Specified image dimensions in file '%s' exceed file size.", + cimg_instance, + filename); + } + + switch (ppm_type) { + case 1 : { // 2D B&W ascii + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; } + } break; + case 2 : { // 2D grey ascii + assign(W,H,1,1); + T* ptrd = _data; + cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; } + } break; + case 3 : { // 2D color ascii + assign(W,H,1,3); + T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forXY(*this,x,y) { + if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { + *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; + } else break; + } + } break; + case 4 : { // 2D b&w binary (support 3D PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + unsigned int w = 0, h = 0, d = 0; + for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + unsigned char mask = 0, val = 0; + for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) { + if (!mask) { if (off--) val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?0:255); + if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }} + } + } + } break; + case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension) + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } else { // 16 bits + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } + } break; + case 6 : { // 2D color binary + if (colormax<256) { // 8 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { // 16 bits + CImg raw; + assign(W,H,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer/2)); + cimg::fread(raw._data,raw._width,nfile); + if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); + to_read-=raw._width; + const unsigned short *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width/3; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } + } break; + case 8 : { // 2D/3D grey binary with int32 integers (PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const int *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + case 9 : { // 2D/3D grey binary with float values (PINK extension) + CImg raw; + assign(W,H,D,1); + T *ptrd = data(0,0,0,0); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const float *ptrs = raw._data; + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); + } + } break; + default : + assign(); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pnm(): PNM type 'P%d' found, but type is not supported.", + cimg_instance, + filename?filename:"(FILE*)",ppm_type); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PFM file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pfm(const char *const filename) { + return _load_pfm(0,filename); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(const char *const filename) { + return CImg().load_pfm(filename); + } + + //! Load image from a PFM file \overloading. + CImg& load_pfm(std::FILE *const file) { + return _load_pfm(file,0); + } + + //! Load image from a PFM file \newinstance. + static CImg get_load_pfm(std::FILE *const file) { + return CImg().load_pfm(file); + } + + CImg& _load_pfm(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pfm(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + char pfm_type; + CImg item(16384,1,1,1,0); + int W = 0, H = 0, err = 0; + double scale = 0; + while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item," P%c",&pfm_type)!=1) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): PFM header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } else if (W<=0 || H<=0) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.", + cimg_instance,W,H, + filename?filename:"(FILE*)"); + } + if (err==2) { + while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile); + if (cimg_sscanf(item,"%lf",&scale)!=1) + cimg::warn(_cimg_instance + "load_pfm(): SCALE field is undefined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + std::fgetc(nfile); + const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness(); + if (is_color) { + assign(W,H,1,3,(T)0); + CImg buf(3*W); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2); + cimg_forY(*this,y) { + cimg::fread(buf._data,3*W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,3*W); + const float *ptrs = buf._data; + cimg_forX(*this,x) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } else { + assign(W,H,1,1,(T)0); + CImg buf(W); + T *ptrd = data(0,0,0,0); + cimg_forY(*this,y) { + cimg::fread(buf._data,W,nfile); + if (is_inverted) cimg::invert_endianness(buf._data,W); + const float *ptrs = buf._data; + cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return mirror('y'); // Most of the .pfm files are flipped along the y-axis + } + + //! Load image from a RGB file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(0,filename,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(filename,dimw,dimh); + } + + //! Load image from a RGB file \overloading. + CImg& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgb(file,0,dimw,dimh); + } + + //! Load image from a RGB file \newinstance. + static CImg get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgb(file,dimw,dimh); + } + + CImg& _load_rgb(std::FILE *const file, const char *const filename, + const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgb(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const longT cimg_iobuffer = (longT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,3); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = raw._width/3UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a RGBA file. + /** + \param filename Filename, as a C-string. + \param dimw Width of the image buffer. + \param dimh Height of the image buffer. + **/ + CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(0,filename,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(filename,dimw,dimh); + } + + //! Load image from a RGBA file \overloading. + CImg& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return _load_rgba(file,0,dimw,dimh); + } + + //! Load image from a RGBA file \newinstance. + static CImg get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) { + return CImg().load_rgba(file,dimw,dimh); + } + + CImg& _load_rgba(std::FILE *const file, const char *const filename, + const unsigned int dimw, const unsigned int dimh) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_rgba(): Specified filename is (null).", + cimg_instance); + + if (!dimw || !dimh) return assign(); + const longT cimg_iobuffer = (longT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg raw; + assign(dimw,dimh,1,4); + T + *ptr_r = data(0,0,0,0), + *ptr_g = data(0,0,0,1), + *ptr_b = data(0,0,0,2), + *ptr_a = data(0,0,0,3); + for (longT to_read = (longT)size(); to_read>0; ) { + raw.assign(std::min(to_read,cimg_iobuffer)); + cimg::fread(raw._data,raw._width,nfile); + to_read-=raw._width; + const unsigned char *ptrs = raw._data; + for (ulongT off = raw._width/4UL; off; --off) { + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a TIFF file. + /** + \param filename Filename, as a C-string. + \param first_frame First frame to read (for multi-pages tiff). + \param last_frame Last frame to read (for multi-pages tiff). + \param step_frame Step value of frame reading. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + \param[out] voxel_size Voxel size, as stored in the filename. + \param[out] description Description, as stored in the filename. + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tiff is not defined at compile time the + function uses CImg& load_other(const char*). + **/ + CImg& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Specified filename is (null).", + cimg_instance); + + const unsigned int + nfirst_frame = first_frame1) + throw CImgArgumentException(_cimg_instance + "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.", + cimg_instance, + filename); + return load_other(filename); +#else +#if cimg_verbosity<3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn(_cimg_instance + "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).", + cimg_instance, + filename,nb_images,nfirst_frame,nlast_frame,nstep_frame); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + TIFFSetDirectory(tif,0); + CImg frame; + for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) { + frame._load_tiff(tif,l,bits_per_value,voxel_size,description); + if (l==nfirst_frame) + assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum); + if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum) + resize(std::max(frame._width,_width), + std::max(frame._height,_height),-100, + std::max(frame._spectrum,_spectrum),0); + draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame); + } + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "load_tiff(): Failed to open file '%s'.", + cimg_instance, + filename); + return *this; +#endif + } + + //! Load image from a TIFF file \newinstance. + static CImg get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImg().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); + } + + // (Original contribution by Jerome Boulanger). +#ifdef cimg_use_tiff + template + void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, + const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int row = 0; row + void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, + const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) { + t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif)); + if (buf) { + for (unsigned int vv = 0; vv + void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (row = 0; rowny?ny - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, 0); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0; rr + void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) { + t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + uint32 row, rowsperstrip = (uint32)-1; + TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip); + for (unsigned int vv = 0; vvny?ny - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif, row, vv); + if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { + _TIFFfree(buf); TIFFClose(tif); + throw CImgIOException(_cimg_instance + "load_tiff(): Invalid strip in file '%s'.", + cimg_instance, + TIFFFileName(tif)); + } + const t *ptr = buf; + for (unsigned int rr = 0;rr& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value, + float *const voxel_size, CImg *const description) { + if (!TIFFSetDirectory(tif,directory)) return assign(); + uint16 samplesperpixel = 1, bitspersample = 8, photo = 0; + uint16 sampleformat = 1; + uint32 nx = 1, ny = 1; + const char *const filename = TIFFFileName(tif); + const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); + TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); + TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); + TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); + TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample); + TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); + if (bits_per_value) *bits_per_value = (unsigned int)bitspersample; + if (voxel_size) { + const char *s_description = 0; + float vx = 0, vy = 0, vz = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) { + const char *s_desc = std::strstr(s_description,"VX="); + if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format + voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz; + } + s_desc = std::strstr(s_description,"spacing="); + if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format + voxel_size[2] = vz; + } + } + TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size); + TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1); + voxel_size[0] = 1.f/voxel_size[0]; + voxel_size[1] = 1.f/voxel_size[1]; + } + if (description) { + const char *s_description = 0; + if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) + CImg::string(s_description).move_to(*description); + } + const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel; + assign(nx,ny,1,spectrum); + + if ((photo>=3 && sampleformat==1 && + (bitspersample==4 || bitspersample==8) && + (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) || + (bitspersample==1 && samplesperpixel==1)) { + // Special case for unsigned color images. + uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32)); + if (!raster) { + _TIFFfree(raster); TIFFClose(tif); + throw CImgException(_cimg_instance + "load_tiff(): Failed to allocate memory (%s) for file '%s'.", + cimg_instance, + cimg::strbuffersize(nx*ny*sizeof(uint32)),filename); + } + TIFFReadRGBAImage(tif,nx,ny,raster,0); + switch (spectrum) { + case 1 : + cimg_forXY(*this,x,y) + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]); + break; + case 3 : + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); + } + break; + case 4 : + cimg_forXY(*this,x,y) { + (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]); + (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]); + } + break; + } + _TIFFfree(raster); + } else { // Other cases + uint16 config; + TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); + if (TIFFIsTiled(tif)) { + uint32 tw = 1, th = 1; + TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw); + TIFFGetField(tif,TIFFTAG_TILELENGTH,&th); + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_contig(tif,samplesperpixel,nx,ny,tw,th); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else if (sampleformat==SAMPLEFORMAT_INT) + _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + else _load_tiff_tiled_separate(tif,samplesperpixel,nx,ny,tw,th); + break; + } + } else { + if (config==PLANARCONFIG_CONTIG) switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) + _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig(tif,samplesperpixel,nx,ny); + else _load_tiff_contig(tif,samplesperpixel,nx,ny); + break; + } else switch (bitspersample) { + case 8 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 16 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 32 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + case 64 : + if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate(tif,samplesperpixel,nx,ny); + else _load_tiff_separate(tif,samplesperpixel,nx,ny); + break; + } + } + } + return *this; + } +#endif + + //! Load image from a MINC2 file. + /** + \param filename Filename, as a C-string. + **/ + // (Original code by Haz-Edine Assemlal). + CImg& load_minc2(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_minc2(): Specified filename is (null).", + cimg_instance); +#ifndef cimg_use_minc2 + return load_other(filename); +#else + minc::minc_1_reader rdr; + rdr.open(filename); + assign(rdr.ndim(1)?rdr.ndim(1):1, + rdr.ndim(2)?rdr.ndim(2):1, + rdr.ndim(3)?rdr.ndim(3):1, + rdr.ndim(4)?rdr.ndim(4):1); + if (pixel_type()==cimg::type::string()) + rdr.setup_read_byte(); + else if (pixel_type()==cimg::type::string()) + rdr.setup_read_int(); + else if (pixel_type()==cimg::type::string()) + rdr.setup_read_double(); + else + rdr.setup_read_float(); + minc::load_standard_volume(rdr,this->_data); + return *this; +#endif + } + + //! Load image from a MINC2 file \newinstance. + static CImg get_load_minc2(const char *const filename) { + return CImg().load_analyze(filename); + } + + //! Load image from an ANALYZE7.5/NIFTI file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_analyze(const char *const filename, float *const voxel_size=0) { + return _load_analyze(0,filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(const char *const filename, float *const voxel_size=0) { + return CImg().load_analyze(filename,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \overloading. + CImg& load_analyze(std::FILE *const file, float *const voxel_size=0) { + return _load_analyze(file,0,voxel_size); + } + + //! Load image from an ANALYZE7.5/NIFTI file \newinstance. + static CImg get_load_analyze(std::FILE *const file, float *const voxel_size=0) { + return CImg().load_analyze(file,voxel_size); + } + + CImg& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_analyze(): Specified filename is (null).", + cimg_instance); + + std::FILE *nfile_header = 0, *nfile = 0; + if (!file) { + CImg body(1024); + const char *const ext = cimg::split_filename(filename,body); + if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file + nfile_header = cimg::fopen(filename,"rb"); + cimg_sprintf(body._data + std::strlen(body),".img"); + nfile = cimg::fopen(body,"rb"); + } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file + nfile = cimg::fopen(filename,"rb"); + cimg_sprintf(body._data + std::strlen(body),".hdr"); + nfile_header = cimg::fopen(body,"rb"); + } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file + } else nfile_header = nfile = file; // File is a Niftii file + if (!nfile || !nfile_header) + throw CImgIOException(_cimg_instance + "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + // Read header. + bool endian = false; + unsigned int header_size; + cimg::fread(&header_size,1,nfile_header); + if (!header_size) + throw CImgIOException(_cimg_instance + "load_analyze(): Invalid zero-size header in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); } + + unsigned char *const header = new unsigned char[header_size]; + cimg::fread(header + 4,header_size - 4,nfile_header); + if (!file && nfile_header!=nfile) cimg::fclose(nfile_header); + if (endian) { + cimg::invert_endianness((short*)(header + 40),5); + cimg::invert_endianness((short*)(header + 70),1); + cimg::invert_endianness((short*)(header + 72),1); + cimg::invert_endianness((float*)(header + 76),4); + cimg::invert_endianness((float*)(header + 108),1); + cimg::invert_endianness((float*)(header + 112),1); + } + + if (nfile_header==nfile) { + const unsigned int vox_offset = (unsigned int)*(float*)(header + 108); + std::fseek(nfile,vox_offset,SEEK_SET); + } + + unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1; + if (!dim[0]) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with zero dimensions.", + cimg_instance, + filename?filename:"(FILE*)"); + + if (dim[0]>4) + cimg::warn(_cimg_instance + "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.", + cimg_instance, + filename?filename:"(FILE*)",dim[0]); + + if (dim[0]>=1) dimx = dim[1]; + if (dim[0]>=2) dimy = dim[2]; + if (dim[0]>=3) dimz = dim[3]; + if (dim[0]>=4) dimv = dim[4]; + float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1; + const unsigned short datatype = *(unsigned short*)(header + 70); + if (voxel_size) { + const float *vsize = (float*)(header + 76); + voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3]; + } + delete[] header; + + // Read pixel data. + assign(dimx,dimy,dimz,dimv); + const size_t pdim = (size_t)dimx*dimy*dimz*dimv; + switch (datatype) { + case 2 : { + unsigned char *const buffer = new unsigned char[pdim]; + cimg::fread(buffer,pdim,nfile); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 4 : { + short *const buffer = new short[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 8 : { + int *const buffer = new int[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 16 : { + float *const buffer = new float[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + case 64 : { + double *const buffer = new double[pdim]; + cimg::fread(buffer,pdim,nfile); + if (endian) cimg::invert_endianness(buffer,pdim); + cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor); + delete[] buffer; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_analyze(): Unable to load datatype %d in file '%s'", + cimg_instance, + datatype,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a .cimg[z] file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(const char *const filename, const char axis='z', const float align=0) { + return CImg().load_cimg(filename,axis,align); + } + + //! Load image from a .cimg[z] file \overloading. + CImg& load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a .cimg[z] file \newinstance + static CImg get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) { + return CImg().load_cimg(file,axis,align); + } + + //! Load sub-images of a .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Starting frame. + \param n1 Ending frame (~0U for max). + \param x0 X-coordinate of the starting sub-image vertex. + \param y0 Y-coordinate of the starting sub-image vertex. + \param z0 Z-coordinate of the starting sub-image vertex. + \param c0 C-coordinate of the starting sub-image vertex. + \param x1 X-coordinate of the ending sub-image vertex (~0U for max). + \param y1 Y-coordinate of the ending sub-image vertex (~0U for max). + \param z1 Z-coordinate of the ending sub-image vertex (~0U for max). + \param c1 C-coordinate of the ending sub-image vertex (~0U for max). + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load sub-images of a .cimg file \overloading. + CImg& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + CImgList list; + list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load sub-images of a .cimg file \newinstance. + static CImg get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1, + const char axis='z', const float align=0) { + return CImg().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align); + } + + //! Load image from an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param[out] voxel_size Pointer to the three voxel sizes read from the file. + **/ + CImg& load_inr(const char *const filename, float *const voxel_size=0) { + return _load_inr(0,filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(const char *const filename, float *const voxel_size=0) { + return CImg().load_inr(filename,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \overloading. + CImg& load_inr(std::FILE *const file, float *const voxel_size=0) { + return _load_inr(file,0,voxel_size); + } + + //! Load image from an INRIMAGE-4 file \newinstance. + static CImg get_load_inr(std::FILE *const file, float *voxel_size=0) { + return CImg().load_inr(file,voxel_size); + } + + static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) { + CImg item(1024), tmp1(64), tmp2(64); + *item = *tmp1 = *tmp2 = 0; + out[0] = std::fscanf(file,"%63s",item._data); + out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; + if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) + throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.", + pixel_type()); + + while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) { + cimg_sscanf(item," XDIM%*[^0-9]%d",out); + cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1); + cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2); + cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3); + cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6); + if (voxel_size) { + cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size); + cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1); + cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2); + } + if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1; + switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) { + case 0 : break; + case 2 : + out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; + std::strncpy(tmp1,tmp2,tmp1._width - 1); // fallthrough + case 1 : + if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; + if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; + if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; + if (out[4]>=0) break; // fallthrough + default : + throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.", + pixel_type(), + tmp2._data); + } + } + if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) + throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.", + pixel_type(), + out[0],out[1],out[2],out[3]); + if (out[4]<0 || out[5]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.", + pixel_type()); + if (out[6]<0) + throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.", + pixel_type()); + if (out[7]<0) + throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.", + pixel_type()); + } + + CImg& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) { +#define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \ + if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ + Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \ + cimg_forYZ(*this,y,z) { \ + cimg::fread(val,fopt[0]*fopt[3],nfile); \ + if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \ + xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \ + } \ + delete[] val; \ + loaded = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_inr(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + int fopt[8], endian = cimg::endianness()?1:0; + bool loaded = false; + if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1; + _load_inr_header(nfile,fopt,voxel_size); + assign(fopt[0],fopt[1],fopt[2],fopt[3]); + _cimg_load_inr_case(0,0,8,unsigned char); + _cimg_load_inr_case(0,1,8,char); + _cimg_load_inr_case(0,0,16,unsigned short); + _cimg_load_inr_case(0,1,16,short); + _cimg_load_inr_case(0,0,32,unsigned int); + _cimg_load_inr_case(0,1,32,int); + _cimg_load_inr_case(1,0,32,float); + _cimg_load_inr_case(1,1,32,float); + _cimg_load_inr_case(1,0,64,double); + _cimg_load_inr_case(1,1,64,double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_inr(): Unknown pixel type defined in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a EXR file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_exr(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_exr(): Specified filename is (null).", + cimg_instance); +#if defined(cimg_use_openexr) + Imf::RgbaInputFile file(filename); + Imath::Box2i dw = file.dataWindow(); + const int + inwidth = dw.max.x - dw.min.x + 1, + inheight = dw.max.y - dw.min.y + 1; + Imf::Array2D pixels; + pixels.resizeErase(inheight,inwidth); + file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth); + file.readPixels(dw.min.y, dw.max.y); + assign(inwidth,inheight,1,4); + T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3); + cimg_forXY(*this,x,y) { + *(ptr_r++) = (T)pixels[y][x].r; + *(ptr_g++) = (T)pixels[y][x].g; + *(ptr_b++) = (T)pixels[y][x].b; + *(ptr_a++) = (T)pixels[y][x].a; + } + return *this; +#elif defined(cimg_use_tinyexr) + float *res; + const char *err = 0; + int width = 0, height = 0; + const int ret = LoadEXR(&res,&width,&height,filename,&err); + if (ret) throw CImgIOException(_cimg_instance + "load_exr(): Unable to load EXR file '%s'.", + cimg_instance,filename); + CImg(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this); + std::free(res); + return *this; +#else + return load_other(filename); +#endif + } + + //! Load image from a EXR file \newinstance. + static CImg get_load_exr(const char *const filename) { + return CImg().load_exr(filename); + } + + //! Load image from a PANDORE-5 file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_pandore(const char *const filename) { + return _load_pandore(0,filename); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(const char *const filename) { + return CImg().load_pandore(filename); + } + + //! Load image from a PANDORE-5 file \overloading. + CImg& load_pandore(std::FILE *const file) { + return _load_pandore(file,0); + } + + //! Load image from a PANDORE-5 file \newinstance. + static CImg get_load_pandore(std::FILE *const file) { + return CImg().load_pandore(file); + } + + CImg& _load_pandore(std::FILE *const file, const char *const filename) { +#define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \ + cimg::fread(dims,nbdim,nfile); \ + if (endian) cimg::invert_endianness(dims,nbdim); \ + assign(nwidth,nheight,ndepth,ndim); \ + const size_t siz = size(); \ + stype *buffer = new stype[siz]; \ + cimg::fread(buffer,siz,nfile); \ + if (endian) cimg::invert_endianness(buffer,siz); \ + T *ptrd = _data; \ + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ + buffer-=siz; \ + delete[] buffer + +#define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \ + if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \ + else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \ + else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \ + else throw CImgIOException(_cimg_instance \ + "load_pandore(): Unknown pixel datatype in file '%s'.", \ + cimg_instance, \ + filename?filename:"(FILE*)"); } + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_pandore(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + CImg header(32); + cimg::fread(header._data,12,nfile); + if (cimg::strncasecmp("PANDORE",header,7)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): PANDORE header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + unsigned int imageid, dims[8] = { 0 }; + int ptbuf[4] = { 0 }; + cimg::fread(&imageid,1,nfile); + const bool endian = imageid>255; + if (endian) cimg::invert_endianness(imageid); + cimg::fread(header._data,20,nfile); + + switch (imageid) { + case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break; + case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break; + case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break; + case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break; + case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break; + case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break; + case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break; + case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break; + case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break; + case 11 : { // Region 1D + cimg::fread(dims,3,nfile); + if (endian) cimg::invert_endianness(dims,3); + assign(dims[1],1,1,1); + const unsigned siz = size(); + if (dims[2]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[2]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 12 : { // Region 2D + cimg::fread(dims,4,nfile); + if (endian) cimg::invert_endianness(dims,4); + assign(dims[2],dims[1],1,1); + const size_t siz = size(); + if (dims[3]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[3]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 13 : { // Region 3D + cimg::fread(dims,5,nfile); + if (endian) cimg::invert_endianness(dims,5); + assign(dims[3],dims[2],dims[1],1); + const size_t siz = size(); + if (dims[4]<256) { + unsigned char *buffer = new unsigned char[siz]; + cimg::fread(buffer,siz,nfile); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + if (dims[4]<65536) { + unsigned short *buffer = new unsigned short[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } else { + unsigned int *buffer = new unsigned int[siz]; + cimg::fread(buffer,siz,nfile); + if (endian) cimg::invert_endianness(buffer,siz); + T *ptrd = _data; + cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); + buffer-=siz; + delete[] buffer; + } + } + } + break; + case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break; + case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break; + case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break; + case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break; + case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break; + case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break; + case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break; + case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break; + case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break; + case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break; + case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break; + case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break; + case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break; + case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break; + case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); + break; + case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break; + case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); + break; + case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break; + case 34 : { // Points 1D + cimg::fread(ptbuf,1,nfile); + if (endian) cimg::invert_endianness(ptbuf,1); + assign(1); (*this)(0) = (T)ptbuf[0]; + } break; + case 35 : { // Points 2D + cimg::fread(ptbuf,2,nfile); + if (endian) cimg::invert_endianness(ptbuf,2); + assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; + } break; + case 36 : { // Points 3D + cimg::fread(ptbuf,3,nfile); + if (endian) cimg::invert_endianness(ptbuf,3); + assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_pandore(): Unable to load data with ID_type %u in file '%s'.", + cimg_instance, + imageid,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image from a PAR-REC (Philips) file. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_parrec(const char *const filename, const char axis='c', const float align=0) { + CImgList list; + list.load_parrec(filename); + if (list._width==1) return list[0].move_to(*this); + return assign(list.get_append(axis,align)); + } + + //! Load image from a PAR-REC (Philips) file \newinstance. + static CImg get_load_parrec(const char *const filename, const char axis='c', const float align=0) { + return CImg().load_parrec(filename,axis,align); + } + + //! Load image from a raw binary file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the image buffer. + \param size_y Height of the image buffer. + \param size_z Depth of the image buffer. + \param size_c Spectrum of the image buffer. + \param is_multiplexed Tells if the image values are multiplexed along the C-axis. + \param invert_endianness Tells if the endianness of the image buffer must be inverted. + \param offset Starting offset of the read in the specified file. + **/ + CImg& load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(const char *const filename, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return CImg().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \overloading. + CImg& load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + //! Load image from a raw binary file \newinstance. + static CImg get_load_raw(std::FILE *const file, + const unsigned int size_x=0, const unsigned int size_y=1, + const unsigned int size_z=1, const unsigned int size_c=1, + const bool is_multiplexed=false, const bool invert_endianness=false, + const ulongT offset=0) { + return CImg().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset); + } + + CImg& _load_raw(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int size_z, const unsigned int size_c, + const bool is_multiplexed, const bool invert_endianness, + const ulongT offset) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename is (null).", + cimg_instance); + if (cimg::is_directory(filename)) + throw CImgArgumentException(_cimg_instance + "load_raw(): Specified filename '%s' is a directory.", + cimg_instance,filename); + const bool is_bool = pixel_type()==cimg::type::string(); + ulongT siz = (ulongT)size_x*size_y*size_z*size_c; + unsigned int + _size_x = size_x, + _size_y = size_y, + _size_z = size_z, + _size_c = size_c; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + if (!siz) { // Retrieve file size + const longT fpos = cimg::ftell(nfile); + if (fpos<0) throw CImgArgumentException(_cimg_instance + "load_raw(): Cannot determine size of input file '%s'.", + cimg_instance,filename?filename:"(FILE*)"); + cimg::fseek(nfile,0,SEEK_END); + siz = (ulongT)cimg::ftell(nfile); + if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; } + else _size_y = (unsigned int)(siz*8); + _size_x = _size_z = _size_c = 1; + cimg::fseek(nfile,fpos,SEEK_SET); + } + cimg::fseek(nfile,(longT)offset,SEEK_SET); + assign(_size_x,_size_y,_size_z,_size_c,0); + + if (is_bool) { // Boolean data (bitwise) + unsigned char *const buf = new unsigned char[siz]; + cimg::fread(buf,siz,nfile); + _uchar2bool(buf,siz,is_multiplexed); + delete[] buf; + } else { // Non-boolean data + if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed + cimg::fread(_data,siz,nfile); + if (invert_endianness) cimg::invert_endianness(_data,siz); + } else if (siz) { // Multiplexed + CImg buf(1,1,1,_size_c); + cimg_forXYZ(*this,x,y,z) { + cimg::fread(buf._data,_size_c,nfile); + if (invert_endianness) cimg::invert_endianness(buf._data,_size_c); + set_vector_at(buf,x,y,z); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load image sequence from a YUV file. + /** + \param filename Filename, as a C-string. + \param size_x Width of the frames. + \param size_y Height of the frames. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param yuv2rgb Tells if the YUV to RGB transform must be applied. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + **/ + CImg& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load image sequence from a YUV file \overloading. + CImg& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return get_load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this); + } + + //! Load image sequence from a YUV file \newinstance. + static CImg get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') { + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb).get_append(axis); + } + + //! Load 3D object from a .OFF file. + /** + \param[out] primitives Primitives data of the 3D object. + \param[out] colors Colors data of the 3D object. + \param filename Filename, as a C-string. + **/ + template + CImg& load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return _load_off(primitives,colors,0,filename); + } + + //! Load 3D object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, const char *const filename) { + return CImg().load_off(primitives,colors,filename); + } + + //! Load 3D object from a .OFF file \overloading. + template + CImg& load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return _load_off(primitives,colors,file,0); + } + + //! Load 3D object from a .OFF file \newinstance. + template + static CImg get_load_off(CImgList& primitives, CImgList& colors, std::FILE *const file) { + return CImg().load_off(primitives,colors,file); + } + + template + CImg& _load_off(CImgList& primitives, CImgList& colors, + std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "load_off(): Specified filename is (null).", + cimg_instance); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); + unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; + CImg line(256); *line = 0; + int err; + + // Skip comments, and read magic string OFF + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): OFF header not found in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Invalid number of vertices or primitives specified in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + } + + // Read points data + assign(nb_points,3); + float X = 0, Y = 0, Z = 0; + cimg_forX(*this,l) { + do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#')); + if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "load_off(): Failed to read vertex %u/%u in file '%s'.", + cimg_instance, + l + 1,nb_points,filename?filename:"(FILE*)"); + } + (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; + } + + // Read primitive data + primitives.assign(); + colors.assign(); + bool stop_flag = false; + while (!stop_flag) { + float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; + unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; + *line = 0; + if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true; + else { + ++nb_read; + switch (prim) { + case 1 : { + if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 2 : { + if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 3 : { + if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 4 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors); + } + } break; + case 5 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 6 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++nb_primitives; + } + } break; + case 7 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i4,i3,i1).move_to(primitives); + CImg::vector(i0,i6,i5,i4).move_to(primitives); + CImg::vector(i3,i2,i1).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + case 8 : { + if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line._data))<7) { + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u from file '%s'.", + cimg_instance, + nb_read,nb_primitives,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } else { + err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2); + CImg::vector(i0,i3,i2,i1).move_to(primitives); + CImg::vector(i0,i5,i4,i3).move_to(primitives); + CImg::vector(i0,i7,i6,i5).move_to(primitives); + colors.insert(3,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); + ++(++nb_primitives); + } + } break; + default : + cimg::warn(_cimg_instance + "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.", + cimg_instance, + nb_read,nb_primitives,prim,filename?filename:"(FILE*)"); + + err = std::fscanf(nfile,"%*[^\n] "); + } + } + } + if (!file) cimg::fclose(nfile); + if (primitives._width!=nb_primitives) + cimg::warn(_cimg_instance + "load_off(): Only %u/%u primitives read from file '%s'.", + cimg_instance, + primitives._width,nb_primitives,filename?filename:"(FILE*)"); + return *this; + } + + //! Load image sequence from a video file, using OpenCV library. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read. + \param step_frame Step value for frame reading. + \param axis Alignment axis. + \param align Appending alignment. + **/ + CImg& load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, + const char axis='z', const float align=0) { + return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this); + } + + //! Load image sequence from a video file, using OpenCV library \newinstance. + static CImg get_load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, + const char axis='z', const float align=0) { + return CImgList().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align); + } + + //! Load image sequence using custom's external tool 'custom'. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_custom_external(const char *const filename, const char axis='z', const float align=0) { + return get_load_custom_external(filename,axis,align).move_to(*this); + } + + //! Load image sequence using custom's external tool 'custom' \newinstance. + static CImg get_load_custom_external(const char *const filename, const char axis='z', const float align=0) { + return CImgList().load_custom_external(filename).get_append(axis,align); + } + + //! Load gif file, using Imagemagick or GraphicsMagicks's external tools. + /** + \param filename Filename, as a C-string. + \param axis Appending axis, if file contains multiple images. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg& load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return get_load_gif_external(filename,axis,align).move_to(*this); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance. + static CImg get_load_gif_external(const char *const filename, + const char axis='z', const float align=0) { + return CImgList().load_gif_external(filename).get_append(axis,align); + } + + //! Load image from a HEIC file. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_heif(const char *const filename) { + return _load_heif(filename); + } + + //! Load image from a HEIC file \newinstance. + static CImg get_load_heif(const char *const filename) { + return CImg().load_heif(filename); + } + + CImg& _load_heif(const char *const filename) { +#ifndef cimg_use_heif + return load_other(filename); +#else + try { + heif::Context ctx; + ctx.read_from_file(filename); + + heif::ImageHandle handle = ctx.get_primary_image_handle(); + const heif::Image image = + handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA: + heif_chroma_interleaved_RGB); + const int + W = image.get_width(heif_channel_interleaved), + H = image.get_height(heif_channel_interleaved), + S = handle.has_alpha_channel()?4:3; + assign(W,H,1,S); + + int stride; + const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride); + T *ptr_r = _data, *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = S>3?data(0,0,0,3):0; + cimg_forY(*this,y) { + const unsigned char *ptrs = buffer + y*stride; + if (ptr_a) cimg_forX(*this,x) { // RGBA + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + *(ptr_a++) = (T)*(ptrs++); + } + else cimg_forX(*this,x) { // RGB + *(ptr_r++) = (T)*(ptrs++); + *(ptr_g++) = (T)*(ptrs++); + *(ptr_b++) = (T)*(ptrs++); + } + } + } catch (const heif::Error& e) { + throw CImgInstanceException(_cimg_instance + "load_heif(): Unable to decode image: %s", + cimg_instance, + e.get_message().c_str()); + } catch (...) { + throw; + } + return *this; +#endif + } + + //! Load image using GraphicsMagick's external tool 'gm'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_graphicsmagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + if (!cimg::system("which gm")) { + cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-", + cimg::graphicsmagick_path(), + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' " + "with external command 'gm'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"", + cimg::graphicsmagick_path(), + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::graphicsmagick_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.", + cimg_instance, + filename); + + } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else + load_pnm(filename_tmp); +#endif + std::remove(filename_tmp); + return *this; + } + + //! Load image using GraphicsMagick's external tool 'gm' \newinstance. + static CImg get_load_graphicsmagick_external(const char *const filename) { + return CImg().load_graphicsmagick_external(filename); + } + + //! Load gzipped image file, using external tool 'gunzip'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimg_instance + "load_gzip_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + const char + *const ext = cimg::split_filename(filename,body), + *const ext2 = cimg::split_filename(body,0); + + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load gzipped image file, using external tool 'gunzip' \newinstance. + static CImg get_load_gzip_external(const char *const filename) { + return CImg().load_gzip_external(filename); + } + + //! Load image using ImageMagick's external tool 'convert'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_imagemagick_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_imagemagick_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + if (!cimg::system("which convert")) { + cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { +#ifdef cimg_use_png + load_png(file); +#else + load_pnm(file); +#endif + } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(), + cimg_file_separator, + cimg::filenamerand(), +#ifdef cimg_use_png + "png" +#else + "pnm" +#endif + ); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"", + cimg::imagemagick_path(), + !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"", + s_filename.data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::imagemagick_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_imagemagick_external(): Failed to load file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + + } else cimg::fclose(file); +#ifdef cimg_use_png + load_png(filename_tmp); +#else + load_pnm(filename_tmp); +#endif + std::remove(filename_tmp); + return *this; + } + + //! Load image using ImageMagick's external tool 'convert' \newinstance. + static CImg get_load_imagemagick_external(const char *const filename) { + return CImg().load_imagemagick_external(filename); + } + + //! Load image from a DICOM file, using Medcon's external tool 'medcon'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_medcon_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_medcon_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + cimg::fclose(cimg::fopen(filename,"r")); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::medcon_path()); + cimg::split_filename(filename_tmp,body); + + cimg_snprintf(command,command._width,"%s.hdr",body._data); + file = cimg::std_fopen(command,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"m000-%s.hdr",body._data); + file = cimg::std_fopen(command,"rb"); + if (!file) { + throw CImgIOException(_cimg_instance + "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + load_analyze(command); + std::remove(command); + cimg::split_filename(command,body); + cimg_snprintf(command,command._width,"%s.img",body._data); + std::remove(command); + return *this; + } + + //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance. + static CImg get_load_medcon_external(const char *const filename) { + return CImg().load_medcon_external(filename); + } + + //! Load image from a .pdf file. + /** + \param filename Filename, as a C-string. + \param resolution Image resolution. + **/ + CImg& load_pdf_external(const char *const filename, const unsigned int resolution=400) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_pdf_external(): Specified filename is (null).", + cimg_instance); + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"", + resolution,s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_pdf_external(): Failed to load file '%s' with external command 'gs'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"", + CImg::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data()); + cimg::system(command,"gs"); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_pdf_external(): Failed to load file '%s' with external command 'gs'.", + cimg_instance, + filename); + } else cimg::fclose(file); + load_pnm(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load image from a .pdf file \newinstance. + static CImg get_load_pdf_external(const char *const filename, const unsigned int resolution=400) { + return CImg().load_pdf_external(filename,resolution); + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw'. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_dcraw_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_dcraw_external(): Specified filename is (null).", + cimg_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256); + std::FILE *file = 0; + const CImg s_filename = CImg::string(filename)._system_strescape(); +#if cimg_OS==1 + cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"", + cimg::dcraw_path(),s_filename.data()); + file = popen(command,"r"); + if (file) { + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_pnm(file); } catch (...) { + pclose(file); + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + } + pclose(file); + return *this; + } +#endif + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"", + cimg::dcraw_path(),s_filename.data(),CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command,cimg::dcraw_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.", + cimg_instance, + filename); + + } else cimg::fclose(file); + load_pnm(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance. + static CImg get_load_dcraw_external(const char *const filename) { + return CImg().load_dcraw_external(filename); + } + +#ifdef cimg_use_opencv + + // Convert a continuous cv::Mat to a CImg. + static CImg _cvmat2cimg(const cv::Mat &src) { + if (src.channels()==1) return CImg(src.ptr(),src.cols,src.rows,1,1); + else if (src.channels()==3) { // BGR + CImg res(src.cols,src.rows,1,src.channels()); + const unsigned char *ptrs = src.ptr(); + unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2); + cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); } + return res; + } + return CImg(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx"); + } + + // Convert a CImg to a cv::Mat. + cv::Mat _cimg2cvmat() const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Instance image is empty.", + cimg_instance); + if (_spectrum==2) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').", + cimg_instance); + if (_depth!=1) + throw CImgInstanceException(_cimg_instance + "_cimg2cvmat() : Invalid number of slices (should be '1').", + cimg_instance); + int mat_type = -1; + if (pixel_type()==cimg::type::string()) mat_type = CV_8UC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_8SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_16UC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_16SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_32SC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_32FC1; + if (pixel_type()==cimg::type::string()) mat_type = CV_64FC1; + if (mat_type<0) + throw CImgInstanceException(_cimg_instance + "_cvmat2cimg() : pixel type '%s' is not supported.", + cimg_instance,pixel_type()); + cv::Mat res; + std::vector channels(_spectrum); + if (_spectrum>1) { + cimg_forC(*this,c) + channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c)); + cv::merge(channels,res); + } else res = cv::Mat(_height,_width,mat_type,_data).clone(); + return res; + } + +#endif + + //! Load image from a camera stream, using OpenCV. + /** + \param index Index of the camera to capture images from (from 0 to 63). + \param capture_width Width of the desired image ('0' stands for default value). + \param capture_height Height of the desired image ('0' stands for default value). + \param skip_frames Number of frames to skip before the capture. + \param release_camera Tells if the camera resource must be released at the end of the method. + **/ + CImg& load_camera(const unsigned int camera_index=0, + const unsigned int capture_width=0, const unsigned int capture_height=0, + const unsigned int skip_frames=0, const bool release_camera=true) { +#ifdef cimg_use_opencv + if (camera_index>=64) + throw CImgArgumentException(_cimg_instance + "load_camera(): Invalid request for camera #%u " + "(no more than 100 cameras can be managed simultaneously).", + cimg_instance, + camera_index); + static cv::VideoCapture *captures[64] = { 0 }; + static unsigned int captures_w[64], captures_h[64]; + if (release_camera) { + cimg::mutex(9); + if (captures[camera_index]) captures[camera_index]->release(); + delete captures[camera_index]; + captures[camera_index] = 0; + captures_w[camera_index] = captures_h[camera_index] = 0; + cimg::mutex(9,0); + return *this; + } + if (!captures[camera_index]) { + cimg::mutex(9); + captures[camera_index] = new cv::VideoCapture(camera_index); + captures_w[camera_index] = captures_h[camera_index] = 0; + if (!captures[camera_index]->isOpened()) { + delete captures[camera_index]; + captures[camera_index] = 0; + cimg::mutex(9,0); + throw CImgIOException(_cimg_instance + "load_camera(): Failed to initialize camera #%u.", + cimg_instance, + camera_index); + } + cimg::mutex(9,0); + } + cimg::mutex(9); + if (capture_width!=captures_w[camera_index]) { + captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width); + captures_w[camera_index] = capture_width; + } + if (capture_height!=captures_h[camera_index]) { + captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height); + captures_h[camera_index] = capture_height; + } + for (unsigned int i = 0; igrab(); + cv::Mat cvimg; + captures[camera_index]->read(cvimg); + if (cvimg.empty()) assign(); else _cvmat2cimg(cvimg).move_to(*this); + cimg::mutex(9,0); + return *this; +#else + cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height); + throw CImgIOException(_cimg_instance + "load_camera(): This function requires features from the OpenCV library " + "('-Dcimg_use_opencv' must be defined).", + cimg_instance); +#endif + } + + //! Load image from a camera stream, using OpenCV \newinstance. + static CImg get_load_camera(const unsigned int camera_index=0, + const unsigned int capture_width=0, const unsigned int capture_height=0, + const unsigned int skip_frames=0, const bool release_camera=true) { + return CImg().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera); + } + + //! Load image using various non-native ways. + /** + \param filename Filename, as a C-string. + **/ + CImg& load_other(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimg_instance + "load_other(): Specified filename is (null).", + cimg_instance); + + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { load_magick(filename); } + catch (CImgException&) { + try { load_imagemagick_external(filename); } + catch (CImgException&) { + try { load_graphicsmagick_external(filename); } + catch (CImgException&) { + try { load_cimg(filename); } + catch (CImgException&) { + try { + cimg::fclose(cimg::fopen(filename,"rb")); + } catch (CImgException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_other(): Failed to open file '%s'.", + cimg_instance, + filename); + } + cimg::exception_mode(omode); + throw CImgIOException(_cimg_instance + "load_other(): Failed to recognize format of file '%s'.", + cimg_instance, + filename); + } + } + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load image using various non-native ways \newinstance. + static CImg get_load_other(const char *const filename) { + return CImg().load_other(filename); + } + + //@} + //--------------------------- + // + //! \name Data Output + //@{ + //--------------------------- + + //! Display information about the image data. + /** + \param title Name for the considered image. + \param display_stats Tells to compute and display image statistics. + **/ + const CImg& print(const char *const title=0, const bool display_stats=true) const { + + int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0; + CImg st; + if (!is_empty() && display_stats) { + st = get_stats(); + xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7]; + xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11]; + } + + const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1, + mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1; + + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type()); + + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum, + (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) + std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared"); + else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared"); + + if (!is_empty()) cimg_foroff(*this,off) { + std::fprintf(cimg::output(),"%g",(double)_data[off]); + if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" "); + if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); } + } + if (!is_empty() && display_stats) + std::fprintf(cimg::output(), + " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), " + "%scoords_max%s = (%u,%u,%u,%u).\n", + cimg::t_bold,cimg::t_normal,st[0], + cimg::t_bold,cimg::t_normal,st[1], + cimg::t_bold,cimg::t_normal,st[2], + cimg::t_bold,cimg::t_normal,std::sqrt(st[3]), + cimg::t_bold,cimg::t_normal,xm,ym,zm,vm, + cimg::t_bold,cimg::t_normal,xM,yM,zM,vM); + else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" "); + std::fflush(cimg::output()); + return *this; + } + + //! Display image into a CImgDisplay window. + /** + \param disp Display window. + **/ + const CImg& display(CImgDisplay& disp) const { + disp.display(*this); + return *this; + } + + //! Display image into a CImgDisplay window, in an interactive way. + /** + \param disp Display window. + \param display_info Tells if image information are displayed on the standard output. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + return _display(disp,0,display_info,XYZ,exit_on_anykey,false); + } + + //! Display image into an interactive window. + /** + \param title Window title + \param display_info Tells if image information are displayed on the standard output. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display(disp,title,display_info,XYZ,exit_on_anykey,false); + } + + const CImg& _display(CImgDisplay &disp, const char *const title, const bool display_info, + unsigned int *const XYZ, const bool exit_on_anykey, + const bool exit_on_singleclick) const { + unsigned int oldw = 0, oldh = 0, _XYZ[3] = { 0 }, key = 0; + int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1, + old_mouse_x = -1, old_mouse_y = -1; + + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1); + if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum); + else disp.set_title("%s",title); + } else if (title) disp.set_title("%s",title); + disp.show().flush(); + + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(dtitle); + + CImg zoom; + for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) { + if (reset_view) { + if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; } + else { + _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2; + _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2; + _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2; + } + x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1; + disp.resize(cimg_fitscreen(_width,_height,_depth),false); + oldw = disp._width; oldh = disp._height; + resize_disp = true; + reset_view = false; + } + if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) { + if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign(); + } else zoom = get_crop(x0,y0,z0,x1,y1,z1); + + const CImg& visu = zoom?zoom:*this; + const unsigned int + dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0, + tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U); + if (!is_empty() && !disp.is_fullscreen() && resize_disp) { + const float + ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh, + dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height()); + const unsigned int + imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM); + disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false); + resize_disp = false; + } + oldw = tw; oldh = th; + + bool + go_up = false, go_down = false, go_left = false, go_right = false, + go_inc = false, go_dec = false, go_in = false, go_out = false, + go_in_center = false; + + disp.set_title("%s",dtitle._data); + if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0); + if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0); + if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0); + + disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y; + CImg selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true); + old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y; + is_first_select = false; + + if (disp.wheel()) { + if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) && + (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) { + go_left = !(go_right = disp.wheel()>0); + } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { + go_down = !(go_up = disp.wheel()>0); + } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + go_out = !(go_in = disp.wheel()>0); go_in_center = false; + } + disp.set_wheel(); + } + + const int + sx0 = selection(0), sy0 = selection(1), sz0 = selection(2), + sx1 = selection(3), sy1 = selection(4), sz1 = selection(5); + if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) { + x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; + x0+=sx0; y0+=sy0; z0+=sz0; + if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) { + if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true; + } + resize_disp = true; + } else switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break; + case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { + // Special mode: play stack of frames + const unsigned int + w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)), + h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0)); + float frame_timing = 5; + bool is_stopped = false; + disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0; + for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) { + if (disp.is_resized()) disp.resize(false); + if (!timer) { + visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2])); + (++_XYZ[2])%=visu._depth; + } + if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U; + if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); } + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break; + case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break; + case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; + (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false). + toggle_fullscreen().set_key(key,false); key = 0; + } break; + } + frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing); + disp.wait(20); + } + const unsigned int + w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width, + h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height; + disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel(); + key = 0; + } break; + case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break; + case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break; + case cimg::keyPADSUB : go_out = true; key = 0; break; + case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break; + case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break; + case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break; + case cimg::keyPAD7 : go_up = go_left = true; key = 0; break; + case cimg::keyPAD9 : go_up = go_right = true; key = 0; break; + case cimg::keyPAD1 : go_down = go_left = true; key = 0; break; + case cimg::keyPAD3 : go_down = go_right = true; key = 0; break; + case cimg::keyPAGEUP : go_inc = true; key = 0; break; + case cimg::keyPAGEDOWN : go_dec = true; key = 0; break; + } + if (go_in) { + const int + mx = go_in_center?disp.width()/2:disp.mouse_x(), + my = go_in_center?disp.height()/2:disp.mouse_y(), + mX = mx*(width() + (depth()>1?depth():0))/disp.width(), + mY = my*(height() + (depth()>1?depth():0))/disp.height(); + int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2]; + if (mX=height()) { + X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth(); + } + if (mX>=width() && mY4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; } + if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; } + if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; } + } + if (go_out) { + const int + delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8, + ndelta_x = delta_x?delta_x:(_width>1), + ndelta_y = delta_y?delta_y:(_height>1), + ndelta_z = delta_z?delta_z:(_depth>1); + x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z; + x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; } + if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; } + if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; } + if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; } + if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; } + if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; } + const float + ratio = (float)(x1-x0)/(y1-y0), + ratiow = (float)disp._width/disp._height, + sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow)); + if (sub>0.01) resize_disp = true; + } + if (go_left) { + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); + if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + } + if (go_right) { + const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1); + if (x1+ndelta1); + if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; } + else { y1-=y0; y0 = 0; } + } + if (go_down) { + const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1); + if (y1+ndelta1); + if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; } + else { z1-=z0; z0 = 0; } + } + if (go_dec) { + const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1); + if (z1+ndelta + const CImg& display_object3d(CImgDisplay& disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static, + render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,primitives,colors,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const CImgList& primitives, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,primitives,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(CImgDisplay &disp, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(disp,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + //! Display object 3D in an interactive window \simplification. + template + const CImg& display_object3d(const char *const title, + const CImg& vertices, + const bool centering=true, + const int render_static=4, const int render_motion=1, + const bool is_double_sided=true, const float focale=700, + const float light_x=0, const float light_y=0, const float light_z=-5e8f, + const float specular_lightness=0.2f, const float specular_shininess=0.1f, + const bool display_axes=true, float *const pose_matrix=0, + const bool exit_on_anykey=false) const { + return display_object3d(title,vertices,CImgList(),centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + + template + const CImg& _display_object3d(CImgDisplay& disp, const char *const title, + const CImg& vertices, + const CImgList& primitives, + const CImgList& colors, + const to& opacities, + const bool centering, + const int render_static, const int render_motion, + const bool is_double_sided, const float focale, + const float light_x, const float light_y, const float light_z, + const float specular_lightness, const float specular_shininess, + const bool display_axes, float *const pose_matrix, + const bool exit_on_anykey) const { + typedef typename cimg::superset::type tpfloat; + + // Check input arguments + if (is_empty()) { + CImg background; + if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128); + else background.assign(1,2,1,3,32,64,32,116,64,96); + if (disp) background.resize(disp.width(),disp.height(),1,-100,3); + else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2, + CImgDisplay::screen_height()/2,1),1,-100,3); + return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } else { if (disp) disp.resize(*this,false); } + CImg error_message(1024); + if (!vertices.is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgArgumentException(_cimg_instance + "display_object3d(): Invalid specified 3D object (%u,%u) (%s).", + cimg_instance,vertices._width,primitives._width,error_message.data()); + if (vertices._width && !primitives) { + CImgList nprimitives(vertices._width,1,1,1,1); + cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l; + return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering, + render_static,render_motion,is_double_sided,focale, + light_x,light_y,light_z,specular_lightness,specular_shininess, + display_axes,pose_matrix,exit_on_anykey); + } + if (!disp) { + disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3); + if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)", + pixel_type(),vertices._width,primitives._width); + } else if (title) disp.set_title("%s",title); + + // Init 3D objects and compute object statistics + CImg + pose, + rotated_vertices(vertices._width,3), + bbox_vertices, rotated_bbox_vertices, + axes_vertices, rotated_axes_vertices, + bbox_opacities, axes_opacities; + CImgList bbox_primitives, axes_primitives; + CImgList reverse_primitives; + CImgList bbox_colors, bbox_colors2, axes_colors; + unsigned int ns_width = 0, ns_height = 0; + int _is_double_sided = (int)is_double_sided; + bool ndisplay_axes = display_axes; + const CImg + background_color(1,1,1,_spectrum,0), + foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type::max(),255)); + float + Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1, + xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0, + ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0, + zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0; + const float delta = cimg::max(xM - xm,yM - ym,zM - zm); + + rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1, + xm,xM,xM,xm,xm,xM,xM,xm, + ym,ym,yM,yM,ym,ym,yM,yM, + zm,zm,zm,zm,zM,zM,zM,zM); + bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6); + bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]); + bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]); + bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f); + + rotated_axes_vertices = axes_vertices.assign(7,3,1,1, + 0,20,0,0,22,-6,-6, + 0,0,20,0,-6,22,-6, + 0,0,0,20,0,0,22); + axes_opacities.assign(3,1,1,1,1); + axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]); + axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3); + + // Begin user interaction loop + CImg visu0(*this,false), visu; + CImg zbuffer(visu0.width(),visu0.height(),1,1,0); + bool init_pose = true, clicked = false, redraw = true; + unsigned int key = 0, font_size = 32; + int + x0 = 0, y0 = 0, x1 = 0, y1 = 0, + nrender_static = render_static, + nrender_motion = render_motion; + disp.show().flush(); + + while (!disp.is_closed() && !key) { + + // Init object pose + if (init_pose) { + const float + ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1, + dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2; + if (centering) + CImg(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose); + else CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose); + if (pose_matrix) { + CImg pose0(pose_matrix,4,3,1,1,false); + pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0); + pose0(3,3) = pose(3,3) = 1; + (pose0*pose).get_crop(0,0,3,2).move_to(pose); + Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15]; + } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; } + init_pose = false; + redraw = true; + } + + // Rotate and draw 3D object + if (redraw) { + const float + r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), + r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), + r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); + if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) { + const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2); + float + *const prv0 = rotated_vertices.data(), + *const prv1 = rotated_vertices.data(0,1), + *const prv2 = rotated_vertices.data(0,2); + cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024)) + cimg_forX(vertices,l) { + const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l]; + prv0[l] = r00*x + r10*y + r20*z + r30; + prv1[l] = r01*x + r11*y + r21*z + r31; + prv2[l] = r02*x + r12*y + r22*z + r32; + } + } + else cimg_forX(bbox_vertices,l) { + const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2); + rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30; + rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31; + rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32; + } + + // Draw objects + const bool render_with_zbuffer = !clicked && nrender_static>0; + visu = visu0; + if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0)) + visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale). + draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale); + else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg::empty(), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale, + width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1,sprite_scale); + // Draw axes + if (ndisplay_axes) { + const float + n = 1e-8f + cimg::hypot(r00,r01,r02), + _r00 = r00/n, _r10 = r10/n, _r20 = r20/n, + _r01 = r01/n, _r11 = r11/n, _r21 = r21/n, + _r02 = r01/n, _r12 = r12/n, _r22 = r22/n, + Xaxes = 25, Yaxes = visu._height - 38.f; + cimg_forX(axes_vertices,l) { + const float + x = axes_vertices(l,0), + y = axes_vertices(l,1), + z = axes_vertices(l,2); + rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z; + rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z; + rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z; + } + axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f; + axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f; + axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f; + visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives, + axes_colors,axes_opacities,1,false,focale). + draw_text((int)(Xaxes + rotated_axes_vertices(4,0)), + (int)(Yaxes + rotated_axes_vertices(4,1)), + "X",axes_colors[0]._data,0,axes_opacities(0,0),13). + draw_text((int)(Xaxes + rotated_axes_vertices(5,0)), + (int)(Yaxes + rotated_axes_vertices(5,1)), + "Y",axes_colors[1]._data,0,axes_opacities(1,0),13). + draw_text((int)(Xaxes + rotated_axes_vertices(6,0)), + (int)(Yaxes + rotated_axes_vertices(6,1)), + "Z",axes_colors[2]._data,0,axes_opacities(2,0),13); + } + visu.display(disp); + if (!clicked || nrender_motion==nrender_static) redraw = false; + } + + // Handle user interaction + if (!redraw) disp.wait(); + if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) { + redraw = true; + if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; } + else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); } + const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT(); + if (disp.button()&1 && !is_keyCTRL) { + const float + R = 0.45f*std::min(disp.width(),disp.height()), + R2 = R*R, + u0 = (float)(x0 - disp.width()/2), + v0 = (float)(y0 - disp.height()/2), + u1 = (float)(x1 - disp.width()/2), + v1 = (float)(y1 - disp.height()/2), + n0 = cimg::hypot(u0,v0), + n1 = cimg::hypot(u1,v1), + nu0 = n0>R?(u0*R/n0):u0, + nv0 = n0>R?(v0*R/n0):v0, + nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)), + nu1 = n1>R?(u1*R/n1):u1, + nv1 = n1>R?(v1*R/n1):v1, + nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)), + u = nv0*nw1 - nw0*nv1, + v = nw0*nu1 - nu0*nw1, + w = nv0*nu1 - nu0*nv1, + n = cimg::hypot(u,v,w), + alpha = (float)std::asin(n/R2)*180/cimg::PI; + (CImg::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose); + x0 = x1; y0 = y1; + } + if (disp.button()&2 && !is_keyCTRL) { + if (focale>0) Zoff-=(y0 - y1)*focale/400; + else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; } + x0 = x1; y0 = y1; + } + if (disp.wheel()) { + if (focale>0) Zoff-=disp.wheel()*focale/20; + else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; } + disp.set_wheel(); + } + if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) { + Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1; + } + if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) { + init_pose = true; disp.set_button(); x0 = x1; y0 = y1; + pose = CImg(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0); + } + } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } + + CImg filename(32); + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + if (!ns_width || !ns_height || + ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) { + ns_width = disp.screen_width()*3U/4; + ns_height = disp.screen_height()*3U/4; + } + if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false); + else { + ns_width = disp._width; ns_height = disp._height; + disp.resize(disp.screen_width(),disp.screen_height(),false); + } + disp.toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; + } break; + case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + // Switch single/double-sided primitives. + if (--_is_double_sided==-2) _is_double_sided = 1; + if (_is_double_sided>=0) reverse_primitives.assign(); + else primitives.get_reverse_object3d().move_to(reverse_primitives); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer + if (zbuffer) zbuffer.assign(); + else zbuffer.assign(visu0.width(),visu0.height(),1,1,0); + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes + ndisplay_axes = !ndisplay_axes; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points + nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines + nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat + nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded + nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + // Set rendering mode to gouraud-shaded. + nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded + nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5; + disp.set_key(key,false); key = 0; redraw = true; + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp); + visu.save(filename); + (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.off",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); + vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file + static unsigned int snap_number = 0; + std::FILE *file; + do { + +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving object... ",font_size,0).display(disp); + vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities). + save(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + +#ifdef cimg_use_board + case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.eps",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1, + sprite_scale); + board.saveEPS(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; + case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.svg",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp); + LibBoard::Board board; + (+visu)._draw_object3d(&board,zbuffer.fill(0), + Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff, + rotated_vertices,reverse_primitives?reverse_primitives:primitives, + colors,opacities,clicked?nrender_motion:nrender_static, + _is_double_sided==1,focale, + visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff, + specular_lightness,specular_shininess,1, + sprite_scale); + board.saveSVG(filename); + (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp); + disp.set_key(key,false); key = 0; + } break; +#endif + } + if (disp.is_resized()) { + disp.resize(false); visu0 = get_resize(disp,1); + if (zbuffer) zbuffer.assign(disp.width(),disp.height()); + redraw = true; + } + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + if (pose_matrix) { + std::memcpy(pose_matrix,pose._data,12*sizeof(float)); + pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale; + } + disp.set_button().set_key(key); + return *this; + } + + //! Display 1D graph in an interactive window. + /** + \param disp Display window. + \param plot_type Plot type. Can be { 0=points | 1=segments | 2=splines | 3=bars }. + \param vertex_type Vertex type. + \param labelx Title for the horizontal axis, as a C-string. + \param xmin Minimum value along the X-axis. + \param xmax Maximum value along the X-axis. + \param labely Title for the vertical axis, as a C-string. + \param ymin Minimum value along the X-axis. + \param ymax Maximum value along the X-axis. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImg& display_graph(CImgDisplay &disp, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); + } + + //! Display 1D graph in an interactive window \overloading. + const CImg& display_graph(const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey); + } + + const CImg& _display_graph(CImgDisplay &disp, const char *const title=0, + const unsigned int plot_type=1, const unsigned int vertex_type=1, + const char *const labelx=0, const double xmin=0, const double xmax=0, + const char *const labely=0, const double ymin=0, const double ymax=0, + const bool exit_on_anykey=false) const { + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "display_graph(): Empty instance.", + cimg_instance); + if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0). + set_title(title?"%s":"CImg<%s>",title?title:pixel_type()); + const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1); + const unsigned int old_normalization = disp.normalization(); + disp.show().flush()._normalization = 0; + + double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax; + if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; } + int x0 = 0, x1 = width()*height()*depth() - 1, key = 0; + + for (bool reset_view = true; !key && !disp.is_closed(); ) { + if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; } + CImg zoom(x1 - x0 + 1,1,1,spectrum()); + cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true); + if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; } + if (y0==y1) { --y0; ++y1; } + + const CImg selection = zoom.get_select_graph(disp,plot_type,vertex_type, + labelx, + nxmin + x0*(nxmax - nxmin)/siz1, + nxmin + x1*(nxmax - nxmin)/siz1, + labely,y0,y1,true); + const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y(); + if (selection[0]>=0) { + if (selection[2]<0) reset_view = true; + else { + x1 = x0 + selection[2]; x0+=selection[0]; + if (selection[1]>=0 && selection[3]>=0) { + y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32); + y1-=selection[1]*(y1 - y0)/(disp.height() - 32); + } + } + } else { + bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false; + switch (key = (int)disp.key()) { + case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break; + case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break; + case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break; + case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); + break; + case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); + break; + case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break; + case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break; + case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break; + case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break; + case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break; + } + if (disp.wheel()) { + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0); + else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0); + else go_out = !(go_in = disp.wheel()>0); + key = 0; + } + + if (go_in) { + const int + xsiz = x1 - x0, + mx = (mouse_x - 16)*xsiz/(disp.width() - 32), + cx = x0 + cimg::cut(mx,0,xsiz); + if (x1 - x0>4) { + x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8; + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + const double + ysiz = y1 - y0, + my = (mouse_y - 16)*ysiz/(disp.height() - 32), + cy = y1 - cimg::cut(my,0.,ysiz); + y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8; + } else y0 = y1 = 0; + } + } + if (go_out) { + if (x0>0 || x1<(int)siz1) { + const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1); + const double ndelta_y = (y1 - y0)/8; + x0-=ndelta_x; x1+=ndelta_x; + y0-=ndelta_y; y1+=ndelta_y; + if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; } + if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; } + } + } + if (go_left) { + const int delta = (x1 - x0)/5, ndelta = delta?delta:1; + if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; } + else { x1-=x0; x0 = 0; } + go_left = false; + } + if (go_right) { + const int delta = (x1 - x0)/5, ndelta = delta?delta:1; + if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; } + else { x0+=(siz1 - x1); x1 = (int)siz1; } + go_right = false; + } + if (go_up) { + const double delta = (y1 - y0)/10, ndelta = delta?delta:1; + y0+=ndelta; y1+=ndelta; + go_up = false; + } + if (go_down) { + const double delta = (y1 - y0)/10, ndelta = delta?delta:1; + y0-=ndelta; y1-=ndelta; + go_down = false; + } + } + if (!exit_on_anykey && key && key!=(int)cimg::keyESC && + (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + disp.set_key(key,false); + key = 0; + } + } + disp._normalization = old_normalization; + return *this; + } + + //! Save image as a file. + /** + \param filename Filename, as a C-string. + \param number When positive, represents an index added to the filename. Otherwise, no number is added. + \param digits Number of digits used for adding the number to the filename. + \note + - The used file format is defined by the file extension in the filename \p filename. + - Parameter \p number can be used to add a 6-digit number to the filename before saving. + + **/ + const CImg& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save(): Specified filename is (null).", + cimg_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + CImg nfilename(1024); + const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename): + filename; + +#ifdef cimg_save_plugin + cimg_save_plugin(fn); +#endif +#ifdef cimg_save_plugin1 + cimg_save_plugin1(fn); +#endif +#ifdef cimg_save_plugin2 + cimg_save_plugin2(fn); +#endif +#ifdef cimg_save_plugin3 + cimg_save_plugin3(fn); +#endif +#ifdef cimg_save_plugin4 + cimg_save_plugin4(fn); +#endif +#ifdef cimg_save_plugin5 + cimg_save_plugin5(fn); +#endif +#ifdef cimg_save_plugin6 + cimg_save_plugin6(fn); +#endif +#ifdef cimg_save_plugin7 + cimg_save_plugin7(fn); +#endif +#ifdef cimg_save_plugin8 + cimg_save_plugin8(fn); +#endif + // Text formats + if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn); + else if (!cimg::strcasecmp(ext,"csv") || + !cimg::strcasecmp(ext,"dlm") || + !cimg::strcasecmp(ext,"txt")) return save_dlm(fn); + else if (!cimg::strcasecmp(ext,"cpp") || + !cimg::strcasecmp(ext,"hpp") || + !cimg::strcasecmp(ext,"h") || + !cimg::strcasecmp(ext,"c")) return save_cpp(fn); + + // 2D binary formats + else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn); + else if (!cimg::strcasecmp(ext,"jpg") || + !cimg::strcasecmp(ext,"jpeg") || + !cimg::strcasecmp(ext,"jpe") || + !cimg::strcasecmp(ext,"jfif") || + !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn); + else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn); + else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn); + else if (!cimg::strcasecmp(ext,"png")) return save_png(fn); + else if (!cimg::strcasecmp(ext,"pgm") || + !cimg::strcasecmp(ext,"ppm") || + !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn); + else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn); + else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn); + else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn); + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); + + // 3D binary formats + else if (!*ext) { +#ifdef cimg_use_zlib + return save_cimg(fn,true); +#else + return save_cimg(fn,false); +#endif + } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn); + else if (!cimg::strcasecmp(ext,"hdr") || + !cimg::strcasecmp(ext,"nii")) return save_analyze(fn); + else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn); + else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn); + else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn); + else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn); + + // Archive files + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + + // Image sequences + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); + return save_other(fn); + } + + //! Save image as an ascii file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_ascii(const char *const filename) const { + return _save_ascii(0,filename); + } + + //! Save image as an Ascii file \overloading. + const CImg& save_ascii(std::FILE *const file) const { + return _save_ascii(file,0); + } + + const CImg& _save_ascii(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_ascii(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++)); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a .cpp source file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_cpp(const char *const filename) const { + return _save_cpp(0,filename); + } + + //! Save image as a .cpp source file \overloading. + const CImg& save_cpp(std::FILE *const file) const { + return _save_cpp(file,0); + } + + const CImg& _save_cpp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_cpp(): Specified filename is (null).", + cimg_instance); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + CImg varname(1024); *varname = 0; + if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data); + if (!*varname) cimg_snprintf(varname,varname._width,"unnamed"); + std::fprintf(nfile, + "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n" + "%s data_%s[] = { %s\n ", + varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data, + is_empty()?"};":""); + if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) { + std::fprintf(nfile,cimg::type::format(),cimg::type::format((*this)[off])); + if (off==siz) std::fprintf(nfile," };\n"); + else if (!((off + 1)%16)) std::fprintf(nfile,",\n "); + else std::fprintf(nfile,", "); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a DLM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_dlm(const char *const filename) const { + return _save_dlm(0,filename); + } + + //! Save image as a DLM file \overloading. + const CImg& save_dlm(std::FILE *const file) const { + return _save_dlm(file,0); + } + + const CImg& _save_dlm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_dlm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + const T* ptrs = _data; + cimg_forYZC(*this,y,z,c) { + cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":","); + std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a BMP file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_bmp(const char *const filename) const { + return _save_bmp(0,filename); + } + + //! Save image as a BMP file \overloading. + const CImg& save_bmp(std::FILE *const file) const { + return _save_bmp(file,0); + } + + const CImg& _save_bmp(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_bmp(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + CImg header(54,1,1,1,0); + unsigned char align_buf[4] = { 0 }; + const unsigned int + align = (4 - (3*_width)%4)%4, + buf_size = (3*_width + align)*height(), + file_size = 54 + buf_size; + header[0] = 'B'; header[1] = 'M'; + header[0x02] = file_size&0xFF; + header[0x03] = (file_size>>8)&0xFF; + header[0x04] = (file_size>>16)&0xFF; + header[0x05] = (file_size>>24)&0xFF; + header[0x0A] = 0x36; + header[0x0E] = 0x28; + header[0x12] = _width&0xFF; + header[0x13] = (_width>>8)&0xFF; + header[0x14] = (_width>>16)&0xFF; + header[0x15] = (_width>>24)&0xFF; + header[0x16] = _height&0xFF; + header[0x17] = (_height>>8)&0xFF; + header[0x18] = (_height>>16)&0xFF; + header[0x19] = (_height>>24)&0xFF; + header[0x1A] = 1; + header[0x1B] = 0; + header[0x1C] = 24; + header[0x1D] = 0; + header[0x22] = buf_size&0xFF; + header[0x23] = (buf_size>>8)&0xFF; + header[0x24] = (buf_size>>16)&0xFF; + header[0x25] = (buf_size>>24)&0xFF; + header[0x27] = 0x1; + header[0x2B] = 0x1; + cimg::fwrite(header._data,54,nfile); + + const T + *ptr_r = data(0,_height - 1,0,0), + *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0; + + switch (_spectrum) { + case 1 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + const unsigned char val = (unsigned char)*(ptr_r++); + std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; + } + } break; + case 2 : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc(0,nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; + } + } break; + default : { + cimg_forY(*this,y) { + cimg_forX(*this,x) { + std::fputc((unsigned char)(*(ptr_b++)),nfile); + std::fputc((unsigned char)(*(ptr_g++)),nfile); + std::fputc((unsigned char)(*(ptr_r++)),nfile); + } + cimg::fwrite(align_buf,align,nfile); + ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a JPEG file. + /** + \param filename Filename, as a C-string. + \param quality Image quality (in %) + **/ + const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { + return _save_jpeg(0,filename,quality); + } + + //! Save image as a JPEG file \overloading. + const CImg& save_jpeg(std::FILE *const file, const unsigned int quality=100) const { + return _save_jpeg(file,0,quality); + } + + const CImg& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_jpeg(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + +#ifndef cimg_use_jpeg + if (!file) return save_other(filename,quality); + else throw CImgIOException(_cimg_instance + "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.", + cimg_instance); +#else + unsigned int dimbuf = 0; + J_COLOR_SPACE colortype = JCS_RGB; + + switch (_spectrum) { + case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break; + case 2 : dimbuf = 3; colortype = JCS_RGB; break; + case 3 : dimbuf = 3; colortype = JCS_RGB; break; + default : dimbuf = 4; colortype = JCS_CMYK; break; + } + + // Call libjpeg functions + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + jpeg_stdio_dest(&cinfo,nfile); + cinfo.image_width = _width; + cinfo.image_height = _height; + cinfo.input_components = dimbuf; + cinfo.in_color_space = colortype; + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); + jpeg_start_compress(&cinfo,TRUE); + + JSAMPROW row_pointer[1]; + CImg buffer(_width*dimbuf); + + while (cinfo.next_scanline& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_magick(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_magick + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_magick(): Instance is multispectral, only the three first channels will be " + "saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + Magick::Image image(Magick::Geometry(_width,_height),"black"); + image.type(Magick::TrueColorType); + image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8)); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = _spectrum>1?data(0,0,0,1):0, + *ptr_b = _spectrum>2?data(0,0,0,2):0; + Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height); + switch (_spectrum) { + case 1 : // Scalar images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++); + ++pixels; + } + break; + case 2 : // RG images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = 0; ++pixels; + } + break; + default : // RGB images + for (ulongT off = (ulongT)_width*_height; off; --off) { + pixels->red = (Magick::Quantum)*(ptr_r++); + pixels->green = (Magick::Quantum)*(ptr_g++); + pixels->blue = (Magick::Quantum)*(ptr_b++); + ++pixels; + } + } + image.syncPixels(); + image.write(filename); + return *this; +#else + cimg::unused(bytes_per_pixel); + throw CImgIOException(_cimg_instance + "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.", + cimg_instance, + filename); +#endif + } + + //! Save image as a PNG file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible. + **/ + const CImg& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_png(0,filename,bytes_per_pixel); + } + + //! Save image as a PNG file \overloading. + const CImg& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_png(file,0,bytes_per_pixel); + } + + const CImg& _save_png(std::FILE *const file, const char *const filename, + const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_png(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + +#ifndef cimg_use_png + cimg::unused(bytes_per_pixel); + if (!file) return save_other(filename); + else throw CImgIOException(_cimg_instance + "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.", + cimg_instance); +#else + +#if defined __GNUC__ + const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning + std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb"); + volatile double stmin, stmax = (double)max_min(stmin); +#else + const char *nfilename = filename; + std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb"); + double stmin, stmax = (double)max_min(stmin); +#endif + + if (_depth>1) + cimg::warn(_cimg_instance + "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + + if (_spectrum>4) + cimg::warn(_cimg_instance + "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename); + + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + filename,stmin,stmax); + + // Setup PNG structures for write + png_voidp user_error_ptr = 0; + png_error_ptr user_error_fn = 0, user_warning_fn = 0; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, + user_warning_fn); + if (!png_ptr){ + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr,(png_infopp)0); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_init_io(png_ptr, nfile); + + const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8); + + int color_type; + switch (spectrum()) { + case 1 : color_type = PNG_COLOR_TYPE_GRAY; break; + case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3 : color_type = PNG_COLOR_TYPE_RGB; break; + default : color_type = PNG_COLOR_TYPE_RGB_ALPHA; + } + const int interlace_type = PNG_INTERLACE_NONE; + const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; + const int filter_method = PNG_FILTER_TYPE_DEFAULT; + png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method); + png_write_info(png_ptr,info_ptr); + const int byte_depth = bit_depth>>3; + const int numChan = spectrum()>4?4:spectrum(); + const int pixel_bit_depth_flag = numChan * (bit_depth - 1); + + // Allocate Memory for Image Save and Fill pixel data + png_bytep *const imgData = new png_byte*[_height]; + for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width]; + const T *pC0 = data(0,0,0,0); + switch (pixel_bit_depth_flag) { + case 7 : { // Gray 8-bit + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++); + } + } break; + case 14 : { // Gray w/ Alpha 8-bit + const T *pC1 = data(0,0,0,1); + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) { + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + } + } + } break; + case 21 : { // RGB 8-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); + cimg_forY(*this,y) { + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x) { + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + *(ptrd++) = (unsigned char)*(pC2++); + } + } + } break; + case 28 : { // RGB x/ Alpha 8-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); + cimg_forY(*this,y){ + unsigned char *ptrd = imgData[y]; + cimg_forX(*this,x){ + *(ptrd++) = (unsigned char)*(pC0++); + *(ptrd++) = (unsigned char)*(pC1++); + *(ptrd++) = (unsigned char)*(pC2++); + *(ptrd++) = (unsigned char)*(pC3++); + } + } + } break; + case 15 : { // Gray 16-bit + cimg_forY(*this,y){ + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++); + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width); + } + } break; + case 30 : { // Gray w/ Alpha 16-bit + const T *pC1 = data(0,0,0,1); + cimg_forY(*this,y){ + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width); + } + } break; + case 45 : { // RGB 16-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2); + cimg_forY(*this,y) { + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + *(ptrd++) = (unsigned short)*(pC2++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width); + } + } break; + case 60 : { // RGB w/ Alpha 16-bit + const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3); + cimg_forY(*this,y) { + unsigned short *ptrd = (unsigned short*)(imgData[y]); + cimg_forX(*this,x) { + *(ptrd++) = (unsigned short)*(pC0++); + *(ptrd++) = (unsigned short)*(pC1++); + *(ptrd++) = (unsigned short)*(pC2++); + *(ptrd++) = (unsigned short)*(pC3++); + } + if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width); + } + } break; + default : + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimg_instance + "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.", + cimg_instance, + nfilename?nfilename:"(FILE*)"); + } + png_write_image(png_ptr,imgData); + png_write_end(png_ptr,info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + // Deallocate Image Write Memory + cimg_forY(*this,n) delete[] imgData[n]; + delete[] imgData; + + if (!file) cimg::fclose(nfile); + return *this; +#endif + } + + //! Save image as a PNM file. + /** + \param filename Filename, as a C-string. + \param bytes_per_pixel Force the number of bytes per pixels for the saving. + **/ + const CImg& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(0,filename,bytes_per_pixel); + } + + //! Save image as a PNM file \overloading. + const CImg& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const { + return _save_pnm(file,0,bytes_per_pixel); + } + + const CImg& _save_pnm(std::FILE *const file, const char *const filename, + const unsigned int bytes_per_pixel=0) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + double stmin, stmax = (double)max_min(stmin); + if (_depth>1) + cimg::warn(_cimg_instance + "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536) + cimg::warn(_cimg_instance + "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.", + cimg_instance, + stmin,stmax,filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL))); + + std::fprintf(nfile,"P%c\n%u %u\n%u\n", + (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535)); + + switch (_spectrum) { + case 1 : { // Scalar image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Binary PGM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + } break; + case 2 : { // RG image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = 0; + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } break; + default : { // RGB image + if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned char)*(ptr_r++); + *(ptrd++) = (unsigned char)*(ptr_g++); + *(ptrd++) = (unsigned char)*(ptr_b++); + } + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } else { // Binary PPM 16 bits + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size/3); + unsigned short *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (unsigned short)*(ptr_r++); + *(ptrd++) = (unsigned short)*(ptr_g++); + *(ptrd++) = (unsigned short)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PNK file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pnk(const char *const filename) const { + return _save_pnk(0,filename); + } + + //! Save image as a PNK file \overloading. + const CImg& save_pnk(std::FILE *const file) const { + return _save_pnk(file,0); + } + + const CImg& _save_pnk(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pnk(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum>1) + cimg::warn(_cimg_instance + "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T *ptr = data(0,0,0,0); + + if (!cimg::type::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file + _save_pnm(file,filename,0); + else if (!cimg::type::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D + std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + unsigned char *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else if (!cimg::type::is_float()) { // Save as P8: Binary int32-valued 3D + if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max()); + else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max()); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + int *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } else { // Save as P9: Binary float-valued 3D + if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max()); + else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max()); + CImg buf((unsigned int)buf_size); + for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,buf_size); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a PFM file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_pfm(const char *const filename) const { + get_mirror('y')._save_pfm(0,filename); + return *this; + } + + //! Save image as a PFM file \overloading. + const CImg& save_pfm(std::FILE *const file) const { + get_mirror('y')._save_pfm(file,0); + return *this; + } + + const CImg& _save_pfm(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pfm(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + if (_spectrum>3) + cimg::warn(_cimg_instance + "save_pfm(): image instance is multispectral, only the three first channels will be saved " + "in file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const T + *ptr_r = data(0,0,0,0), + *ptr_g = (_spectrum>=2)?data(0,0,0,1):0, + *ptr_b = (_spectrum>=3)?data(0,0,0,2):0; + const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3)); + + std::fprintf(nfile,"P%c\n%u %u\n1.0\n", + (_spectrum==1?'f':'F'),_width,_height); + + switch (_spectrum) { + case 1 : { // Scalar image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++); + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,N,nfile); + to_write-=N; + } + } break; + case 2 : { // RG image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const unsigned int N = std::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = 0; + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } break; + default : { // RGB image + CImg buf(buf_size); + for (longT to_write = (longT)width()*height(); to_write>0; ) { + const unsigned int N = std::min((unsigned int)to_write,buf_size/3); + float *ptrd = buf._data; + for (ulongT i = N; i>0; --i) { + *(ptrd++) = (float)*(ptr_r++); + *(ptrd++) = (float)*(ptr_g++); + *(ptrd++) = (float)*(ptr_b++); + } + if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size); + cimg::fwrite(buf._data,3*N,nfile); + to_write-=N; + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a RGB file. + /** + \param filename Filename, as a C-string. + **/ + const CImg& save_rgb(const char *const filename) const { + return _save_rgb(0,filename); + } + + //! Save image as a RGB file \overloading. + const CImg& save_rgb(std::FILE *const file) const { + return _save_rgb(file,0); + } + + const CImg& _save_rgb(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgb(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=3) + cimg::warn(_cimg_instance + "save_rgb(): image instance has not exactly 3 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT wh = (ulongT)_width*_height; + unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0; + switch (_spectrum) { + case 1 : { // Scalar image + for (ulongT k = 0; k& save_rgba(const char *const filename) const { + return _save_rgba(0,filename); + } + + //! Save image as a RGBA file \overloading. + const CImg& save_rgba(std::FILE *const file) const { + return _save_rgba(file,0); + } + + const CImg& _save_rgba(std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_rgba(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + if (_spectrum!=4) + cimg::warn(_cimg_instance + "save_rgba(): image instance has not exactly 4 channels, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT wh = (ulongT)_width*_height; + unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer; + const T + *ptr1 = data(0,0,0,0), + *ptr2 = _spectrum>1?data(0,0,0,1):0, + *ptr3 = _spectrum>2?data(0,0,0,2):0, + *ptr4 = _spectrum>3?data(0,0,0,3):0; + switch (_spectrum) { + case 1 : { // Scalar images + for (ulongT k = 0; k{ 0=None | 1=LZW | 2=JPEG }. + \param[out] voxel_size Voxel size, to be stored in the filename. + \param[out] description Description, to be stored in the filename. + \param use_bigtiff Allow to save big tiff files (>4Gb). + \note + - libtiff support is enabled by defining the precompilation + directive \c cimg_use_tif. + - When libtiff is enabled, 2D and 3D (multipage) several + channel per pixel are supported for + char,uchar,short,ushort,float and \c double pixel types. + - If \c cimg_use_tiff is not defined at compile time the + function uses CImg&save_other(const char*). + **/ + const CImg& save_tiff(const char *const filename, const unsigned int compression_type=0, + + const float *const voxel_size=0, const char *const description=0, + const bool use_bigtiff=true) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_tiff(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifdef cimg_use_tiff + const bool + _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images + TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); + if (tif) { + cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description); + TIFFClose(tif); + } else throw CImgIOException(_cimg_instance + "save_tiff(): Failed to open file '%s' for writing.", + cimg_instance, + filename); + return *this; +#else + cimg::unused(compression_type,voxel_size,description,use_bigtiff); + return save_other(filename); +#endif + } + +#ifdef cimg_use_tiff + +#define _cimg_save_tiff(types,typed,compression_type) if (!std::strcmp(types,pixel_type())) { \ + const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); } + + // [internal] Save a plane into a tiff file + template + const CImg& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { + if (is_empty() || !tif || pixel_t) return *this; + const char *const filename = TIFFFileName(tif); + uint32 rowsperstrip = (uint32)-1; + uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric; + if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; + else photometric = PHOTOMETRIC_MINISBLACK; + TIFFSetDirectory(tif,directory); + TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width); + TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height); + if (voxel_size) { + const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2]; + TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE); + TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx); + TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy); + CImg s_description(256); + cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz); + TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data()); + } + if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description); + TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); + TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp); + if (cimg::type::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3); + else if (cimg::type::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1); + else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2); + double valm, valM = max_min(valm); + TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm); + TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM); + TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp); + TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); + TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric); + TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG: + compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE); + rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip); + TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip); + TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB); + TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg"); + + t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif)); + if (buf) { + for (unsigned int row = 0; row<_height; row+=rowsperstrip) { + uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip); + tstrip_t strip = TIFFComputeStrip(tif,row,0); + tsize_t i = 0; + for (unsigned int rr = 0; rr& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, + const unsigned int compression_type, const float *const voxel_size, + const char *const description) const { + _cimg_save_tiff("bool",unsigned char,compression_type); + _cimg_save_tiff("unsigned char",unsigned char,compression_type); + _cimg_save_tiff("char",char,compression_type); + _cimg_save_tiff("unsigned short",unsigned short,compression_type); + _cimg_save_tiff("short",short,compression_type); + _cimg_save_tiff("unsigned int",unsigned int,compression_type); + _cimg_save_tiff("int",int,compression_type); + _cimg_save_tiff("unsigned int64",unsigned int,compression_type); + _cimg_save_tiff("int64",int,compression_type); + _cimg_save_tiff("float",float,compression_type); + _cimg_save_tiff("double",float,compression_type); + const char *const filename = TIFFFileName(tif); + throw CImgInstanceException(_cimg_instance + "save_tiff(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + return *this; + } +#endif + + //! Save image as a MINC2 file. + /** + \param filename Filename, as a C-string. + \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from. + **/ + const CImg& save_minc2(const char *const filename, + const char *const imitate_file=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_minc2(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_minc2 + cimg::unused(imitate_file); + return save_other(filename); +#else + minc::minc_1_writer wtr; + if (imitate_file) + wtr.open(filename, imitate_file); + else { + minc::minc_info di; + if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X)); + if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y)); + if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z)); + if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME)); + wtr.open(filename,di,1,NC_FLOAT,0); + } + if (pixel_type()==cimg::type::string()) + wtr.setup_write_byte(); + else if (pixel_type()==cimg::type::string()) + wtr.setup_write_int(); + else if (pixel_type()==cimg::type::string()) + wtr.setup_write_double(); + else + wtr.setup_write_float(); + minc::save_standard_volume(wtr, this->_data); + return *this; +#endif + } + + //! Save image as an ANALYZE7.5 or NIFTI file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_analyze(const char *const filename, const float *const voxel_size=0) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_analyze(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + std::FILE *file; + CImg hname(1024), iname(1024); + const char *const ext = cimg::split_filename(filename); + short datatype = -1; + if (!*ext) { + cimg_snprintf(hname,hname._width,"%s.hdr",filename); + cimg_snprintf(iname,iname._width,"%s.img",filename); + } + if (!cimg::strncasecmp(ext,"hdr",3)) { + std::strcpy(hname,filename); + std::strncpy(iname,filename,iname._width - 1); + cimg_sprintf(iname._data + std::strlen(iname) - 3,"img"); + } + if (!cimg::strncasecmp(ext,"img",3)) { + std::strcpy(hname,filename); + std::strncpy(iname,filename,iname._width - 1); + cimg_sprintf(hname._data + std::strlen(iname) - 3,"hdr"); + } + if (!cimg::strncasecmp(ext,"nii",3)) { + std::strncpy(hname,filename,hname._width - 1); *iname = 0; + } + + CImg header(*iname?348:352,1,1,1,0); + int *const iheader = (int*)header._data; + *iheader = 348; + std::strcpy(header._data + 4,"CImg"); + std::strcpy(header._data + 14," "); + ((short*)&(header[36]))[0] = 4096; + ((char*)&(header[38]))[0] = 114; + ((short*)&(header[40]))[0] = 4; + ((short*)&(header[40]))[1] = (short)_width; + ((short*)&(header[40]))[2] = (short)_height; + ((short*)&(header[40]))[3] = (short)_depth; + ((short*)&(header[40]))[4] = (short)_spectrum; + if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"unsigned int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"int64")) datatype = 8; + if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; + if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; + if (datatype<0) + throw CImgIOException(_cimg_instance + "save_analyze(): Unsupported pixel type '%s' for file '%s'.", + cimg_instance, + pixel_type(),filename); + + ((short*)&(header[70]))[0] = datatype; + ((short*)&(header[72]))[0] = sizeof(T); + ((float*)&(header[108]))[0] = (float)(*iname?0:header.width()); + ((float*)&(header[112]))[0] = 1; + ((float*)&(header[76]))[0] = 0; + if (voxel_size) { + ((float*)&(header[76]))[1] = voxel_size[0]; + ((float*)&(header[76]))[2] = voxel_size[1]; + ((float*)&(header[76]))[3] = voxel_size[2]; + } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1; + file = cimg::fopen(hname,"wb"); + cimg::fwrite(header._data,header.width(),file); + if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } + cimg::fwrite(_data,size(),file); + cimg::fclose(file); + return *this; + } + + //! Save image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param is_compressed Tells if the file contains compressed image data. + **/ + const CImg& save_cimg(const char *const filename, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(filename,is_compressed); + return *this; + } + + //! Save image as a .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, const bool is_compressed=false) const { + CImgList(*this,true).save_cimg(file,is_compressed); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file. + /** + \param filename Filename, as a C-string. + \param n0 Index of the image inside the file. + \param x0 X-coordinate of the sub-image location. + \param y0 Y-coordinate of the sub-image location. + \param z0 Z-coordinate of the sub-image location. + \param c0 C-coordinate of the sub-image location. + **/ + const CImg& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(filename,n0,x0,y0,z0,c0); + return *this; + } + + //! Save image as a sub-image into an existing .cimg file \overloading. + const CImg& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + CImgList(*this,true).save_cimg(file,n0,x0,y0,z0,c0); + return *this; + } + + //! Save blank image as a .cimg file. + /** + \param filename Filename, as a C-string. + \param dx Width of the image. + \param dy Height of the image. + \param dz Depth of the image. + \param dc Number of channels of the image. + \note + - All pixel values of the saved image are set to \c 0. + - Use this method to save large images without having to instantiate and allocate them. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dc); + } + + //! Save blank image as a .cimg file \overloading. + /** + Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int) + with a file stream argument instead of a filename string. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return CImgList::save_empty_cimg(file,1,dx,dy,dz,dc); + } + + //! Save image as an INRIMAGE-4 file. + /** + \param filename Filename, as a C-string. + \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions. + **/ + const CImg& save_inr(const char *const filename, const float *const voxel_size=0) const { + return _save_inr(0,filename,voxel_size); + } + + //! Save image as an INRIMAGE-4 file \overloading. + const CImg& save_inr(std::FILE *const file, const float *const voxel_size=0) const { + return _save_inr(file,0,voxel_size); + } + + const CImg& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_inr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + int inrpixsize = -1; + const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; + if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { + inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; + } + if (!cimg::strcasecmp(pixel_type(),"char")) { + inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; + } + if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { + inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; + } + if (!cimg::strcasecmp(pixel_type(),"short")) { + inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; + } + if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { + inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"int")) { + inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"float")) { + inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; + } + if (!cimg::strcasecmp(pixel_type(),"double")) { + inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; + } + if (inrpixsize<=0) + throw CImgIOException(_cimg_instance + "save_inr(): Unsupported pixel type '%s' for file '%s'", + cimg_instance, + pixel_type(),filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + CImg header(257); + int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n", + _width,_height,_depth,_spectrum); + if (voxel_size) err+=cimg_sprintf(header._data + err,"VX=%g\nVY=%g\nVZ=%g\n", + voxel_size[0],voxel_size[1],voxel_size[2]); + err+=cimg_sprintf(header._data + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm"); + std::memset(header._data + err,'\n',252 - err); + std::memcpy(header._data + 252,"##}\n",4); + cimg::fwrite(header._data,256,nfile); + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile); + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as an OpenEXR file. + /** + \param filename Filename, as a C-string. + \note The OpenEXR file format is described here. + **/ + const CImg& save_exr(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_exr(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.", + cimg_instance, + filename); + +#ifndef cimg_use_openexr + return save_other(filename); +#else + Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba; + switch (_spectrum) { + case 1 : { // Grayscale image + for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_rPandore file specifications + for more information). + **/ + const CImg& save_pandore(const char *const filename, const unsigned int colorspace=0) const { + return _save_pandore(0,filename,colorspace); + } + + //! Save image as a Pandore-5 file \overloading. + /** + Same as save_pandore(const char *,unsigned int) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const { + return _save_pandore(file,0,colorspace); + } + + unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const { + unsigned int nbdims = 0; + if (id==2 || id==3 || id==4) { + dims[0] = 1; dims[1] = _width; nbdims = 2; + } + if (id==5 || id==6 || id==7) { + dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; + } + if (id==8 || id==9 || id==10) { + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + } + if (id==16 || id==17 || id==18) { + dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; + } + if (id==19 || id==20 || id==21) { + dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; + } + if (id==22 || id==23 || id==25) { + dims[0] = _spectrum; dims[1] = _width; nbdims = 2; + } + if (id==26 || id==27 || id==29) { + dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; + } + if (id==30 || id==31 || id==33) { + dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; + } + return nbdims; + } + + const CImg& _save_pandore(std::FILE *const file, const char *const filename, + const unsigned int colorspace) const { + +#define __cimg_save_pandore_case(dtype) \ + dtype *buffer = new dtype[size()]; \ + const T *ptrs = _data; \ + cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \ + buffer-=size(); \ + cimg::fwrite(buffer,size(),nfile); \ + delete[] buffer + +#define _cimg_save_pandore_case(sy,sz,sv,stype,id) \ + if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \ + (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \ + unsigned int *iheader = (unsigned int*)(header + 12); \ + nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ + cimg::fwrite(header,36,nfile); \ + if (sizeof(unsigned long)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else if (sizeof(unsigned int)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else if (sizeof(unsigned short)==4) { CImg ndims(5); \ + for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \ + cimg::fwrite(ndims._data,nbdims,nfile); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ + __cimg_save_pandore_case(unsigned char); \ + } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ + if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \ + else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \ + else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ + if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \ + else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \ + else throw CImgIOException(_cimg_instance \ + "save_pandore(): Unsupported datatype for file '%s'.",\ + cimg_instance, \ + filename?filename:"(FILE*)"); \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_pandore(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, + 0,0,0,0,'C','I','m','g',0,0,0,0,0, + 'N','o',' ','d','a','t','e',0,0,0,0 }; + unsigned int nbdims, dims[5] = { 0 }; + bool saved = false; + _cimg_save_pandore_case(1,1,1,"unsigned char",2); + _cimg_save_pandore_case(1,1,1,"char",3); + _cimg_save_pandore_case(1,1,1,"unsigned short",3); + _cimg_save_pandore_case(1,1,1,"short",3); + _cimg_save_pandore_case(1,1,1,"unsigned int",3); + _cimg_save_pandore_case(1,1,1,"int",3); + _cimg_save_pandore_case(1,1,1,"unsigned int64",3); + _cimg_save_pandore_case(1,1,1,"int64",3); + _cimg_save_pandore_case(1,1,1,"float",4); + _cimg_save_pandore_case(1,1,1,"double",4); + + _cimg_save_pandore_case(0,1,1,"unsigned char",5); + _cimg_save_pandore_case(0,1,1,"char",6); + _cimg_save_pandore_case(0,1,1,"unsigned short",6); + _cimg_save_pandore_case(0,1,1,"short",6); + _cimg_save_pandore_case(0,1,1,"unsigned int",6); + _cimg_save_pandore_case(0,1,1,"int",6); + _cimg_save_pandore_case(0,1,1,"unsigned int64",6); + _cimg_save_pandore_case(0,1,1,"int64",6); + _cimg_save_pandore_case(0,1,1,"float",7); + _cimg_save_pandore_case(0,1,1,"double",7); + + _cimg_save_pandore_case(0,0,1,"unsigned char",8); + _cimg_save_pandore_case(0,0,1,"char",9); + _cimg_save_pandore_case(0,0,1,"unsigned short",9); + _cimg_save_pandore_case(0,0,1,"short",9); + _cimg_save_pandore_case(0,0,1,"unsigned int",9); + _cimg_save_pandore_case(0,0,1,"int",9); + _cimg_save_pandore_case(0,0,1,"unsigned int64",9); + _cimg_save_pandore_case(0,0,1,"int64",9); + _cimg_save_pandore_case(0,0,1,"float",10); + _cimg_save_pandore_case(0,0,1,"double",10); + + _cimg_save_pandore_case(0,1,3,"unsigned char",16); + _cimg_save_pandore_case(0,1,3,"char",17); + _cimg_save_pandore_case(0,1,3,"unsigned short",17); + _cimg_save_pandore_case(0,1,3,"short",17); + _cimg_save_pandore_case(0,1,3,"unsigned int",17); + _cimg_save_pandore_case(0,1,3,"int",17); + _cimg_save_pandore_case(0,1,3,"unsigned int64",17); + _cimg_save_pandore_case(0,1,3,"int64",17); + _cimg_save_pandore_case(0,1,3,"float",18); + _cimg_save_pandore_case(0,1,3,"double",18); + + _cimg_save_pandore_case(0,0,3,"unsigned char",19); + _cimg_save_pandore_case(0,0,3,"char",20); + _cimg_save_pandore_case(0,0,3,"unsigned short",20); + _cimg_save_pandore_case(0,0,3,"short",20); + _cimg_save_pandore_case(0,0,3,"unsigned int",20); + _cimg_save_pandore_case(0,0,3,"int",20); + _cimg_save_pandore_case(0,0,3,"unsigned int64",20); + _cimg_save_pandore_case(0,0,3,"int64",20); + _cimg_save_pandore_case(0,0,3,"float",21); + _cimg_save_pandore_case(0,0,3,"double",21); + + _cimg_save_pandore_case(1,1,0,"unsigned char",22); + _cimg_save_pandore_case(1,1,0,"char",23); + _cimg_save_pandore_case(1,1,0,"unsigned short",23); + _cimg_save_pandore_case(1,1,0,"short",23); + _cimg_save_pandore_case(1,1,0,"unsigned int",23); + _cimg_save_pandore_case(1,1,0,"int",23); + _cimg_save_pandore_case(1,1,0,"unsigned int64",23); + _cimg_save_pandore_case(1,1,0,"int64",23); + _cimg_save_pandore_case(1,1,0,"float",25); + _cimg_save_pandore_case(1,1,0,"double",25); + + _cimg_save_pandore_case(0,1,0,"unsigned char",26); + _cimg_save_pandore_case(0,1,0,"char",27); + _cimg_save_pandore_case(0,1,0,"unsigned short",27); + _cimg_save_pandore_case(0,1,0,"short",27); + _cimg_save_pandore_case(0,1,0,"unsigned int",27); + _cimg_save_pandore_case(0,1,0,"int",27); + _cimg_save_pandore_case(0,1,0,"unsigned int64",27); + _cimg_save_pandore_case(0,1,0,"int64",27); + _cimg_save_pandore_case(0,1,0,"float",29); + _cimg_save_pandore_case(0,1,0,"double",29); + + _cimg_save_pandore_case(0,0,0,"unsigned char",30); + _cimg_save_pandore_case(0,0,0,"char",31); + _cimg_save_pandore_case(0,0,0,"unsigned short",31); + _cimg_save_pandore_case(0,0,0,"short",31); + _cimg_save_pandore_case(0,0,0,"unsigned int",31); + _cimg_save_pandore_case(0,0,0,"int",31); + _cimg_save_pandore_case(0,0,0,"unsigned int64",31); + _cimg_save_pandore_case(0,0,0,"int64",31); + _cimg_save_pandore_case(0,0,0,"float",33); + _cimg_save_pandore_case(0,0,0,"double",33); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save image as a raw data file. + /** + \param filename Filename, as a C-string. + \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false). + \note The .raw format does not store the image dimensions in the output file, + so you have to keep track of them somewhere to be able to read the file correctly afterwards. + **/ + const CImg& save_raw(const char *const filename, const bool is_multiplexed=false) const { + return _save_raw(0,filename,is_multiplexed); + } + + //! Save image as a raw data file \overloading. + /** + Same as save_raw(const char *,bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_raw(std::FILE *const file, const bool is_multiplexed=false) const { + return _save_raw(file,0,is_multiplexed); + } + + const CImg& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_raw(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + if (pixel_type()==cimg::type::string()) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = _bool2uchar(siz,is_multiplexed); + cimg::fwrite(buf,siz,nfile); + delete[] buf; + } else { // Non boolean data + if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed + else { // Multiplexed + CImg buf(_spectrum); + cimg_forXYZ(*this,x,y,z) { + cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c); + cimg::fwrite(buf._data,_spectrum,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + // Return unsigned char buffer that encodes data of a CImg instance bitwise. + // (buffer needs to be deallocated afterwards, with delete[]). + const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const { + const ulongT _siz = size(); + siz = _siz/8 + (_siz%8?1:0); + unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0; + + if (!is_multiplexed || _spectrum==1) // Non-multiplexed + cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }} + else // Multiplexed + cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) { + (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; } + } + if (bit) *ptrd = val; + return buf; + } + + // Fill CImg instance from bitwise data encoded in an unsigned char buffer. + void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) { + const ulongT S = std::min(siz*8,size()); + const unsigned char *ptrs = buf; + unsigned char val = 0, mask = 0; + T *ptrd = _data; + if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed + for (ulongT off = 0; off>=1)) { val = *(ptrs++); mask = 128; } + *(ptrd++) = (T)((val&mask)?1:0); + } + else if (S) { // Multiplexed + ulongT off = 0; + for (int z = 0; z>=1)) { val = *(ptrs++); ++off; mask = 128; } + (*this)(x,y,z,c) = (T)((val&mask)?1:0); + } + } + } + + //! Save image as a .yuv video file. + /** + \param filename Filename, as a C-string. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false). + \note Each slice of the instance image is considered to be a single frame of the output video file. + **/ + const CImg& save_yuv(const char *const filename, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(filename,chroma_subsampling,is_rgb); + return *this; + } + + //! Save image as a .yuv video file \overloading. + /** + Same as save_yuv(const char*,const unsigned int,const bool) const + with a file stream argument instead of a filename string. + **/ + const CImg& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + CImgList(*this,true).save_yuv(file,chroma_subsampling,is_rgb); + return *this; + } + + //! Save 3D object as an Object File Format (.off) file. + /** + \param filename Filename, as a C-string. + \param primitives List of 3D object primitives. + \param colors List of 3D object colors. + \note + - Instance image contains the vertices data of the 3D object. + - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format. + Such primitives will be lost or simplified during file saving. + - The .off file format is described here. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + const char *const filename) const { + return _save_off(primitives,colors,0,filename); + } + + //! Save 3D object as an Object File Format (.off) file \overloading. + /** + Same as save_off(const CImgList&,const CImgList&,const char*) const + with a file stream argument instead of a filename string. + **/ + template + const CImg& save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file) const { + return _save_off(primitives,colors,file,0); + } + + template + const CImg& _save_off(const CImgList& primitives, const CImgList& colors, + std::FILE *const file, const char *const filename) const { + if (!file && !filename) + throw CImgArgumentException(_cimg_instance + "save_off(): Specified filename is (null).", + cimg_instance); + if (is_empty()) + throw CImgInstanceException(_cimg_instance + "save_off(): Empty instance, for file '%s'.", + cimg_instance, + filename?filename:"(FILE*)"); + + CImgList opacities; + CImg error_message(1024); + if (!is_object3d(primitives,colors,opacities,true,error_message)) + throw CImgInstanceException(_cimg_instance + "save_off(): Invalid specified 3D object, for file '%s' (%s).", + cimg_instance, + filename?filename:"(FILE*)",error_message.data()); + + const CImg default_color(1,3,1,1,(tc)std::min((int)cimg::type::max(),200)); + std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); + unsigned int supported_primitives = 0; + cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives; + std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width); + cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n", + (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); + cimglist_for(primitives,l) { + const CImg& color = l1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f; + switch (psiz) { + case 1 : std::fprintf(nfile,"1 %u %f %f %f\n", + (unsigned int)primitives(l,0),r,g,b); break; + case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + (unsigned int)primitives(l,1),r,g,b); break; + case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; + case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; + case 6 : { + const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"2 %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 9 : { + const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"3 %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,2), + (unsigned int)primitives(l,1),rt,gt,bt); + } break; + case 12 : { + const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5); + const float + rt = color.atXY(xt,yt,0)/255.f, + gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f, + bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f; + std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", + (unsigned int)primitives(l,0),(unsigned int)primitives(l,3), + (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt); + } break; + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save volumetric image as a video (using the OpenCV library when available). + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). + \param keep_open Tells if the video writer associated to the specified filename + must be kept open or not (to allow frames to be added in the same file afterwards). + **/ + const CImg& save_video(const char *const filename, const unsigned int fps=25, + const char *codec=0, const bool keep_open=false) const { + if (is_empty()) { CImgList().save_video(filename,fps,codec,keep_open); return *this; } + CImgList list; + get_split('z').move_to(list); + list.save_video(filename,fps,codec,keep_open); + return *this; + } + + //! Save volumetric image as a video, using custom external binary. + /** + \param filename Filename, as a C-string. + \param fps Video framerate. + \param codec Video codec, as a C-string. + \param bitrate Video bitrate. + \note + - Each slice of the instance image is considered to be a single frame of the output video file. + - This method uses \c custom, an external executable binary provided by + custom. + It must be installed for the method to succeed. + **/ + const CImg& save_custom_external(const char *const filename, const unsigned int fps=25, + const char *const codec=0, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_custom_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImgList list; + get_split('z').move_to(list); + list.save_custom_external(filename,fps,codec,bitrate); + return *this; + } + + //! Save image using gzip external binary. + /** + \param filename Filename, as a C-string. + \note This method uses \c gzip, an external executable binary provided by + gzip. + It must be installed for the method to succeed. + **/ + const CImg& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_gzip_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::gzip_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimg_instance, + filename); + + else cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image using GraphicsMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c gm, an external executable binary provided by + GraphicsMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_graphicsmagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "GraphicsMagick only writes the first image slice.", + cimg_instance,filename); + +#ifdef cimg_use_png +#define _cimg_sge_extension1 "png" +#define _cimg_sge_extension2 "png" +#else +#define _cimg_sge_extension1 "pgm" +#define _cimg_sge_extension2 "ppm" +#endif + CImg command(1024), filename_tmp(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(), + _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + +#ifdef cimg_use_png + save_png(filename_tmp); +#else + save_pnm(filename_tmp); +#endif + cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"", + cimg::graphicsmagick_path(),quality, + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::graphicsmagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image using ImageMagick's external binary. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note This method uses \c convert, an external executable binary provided by + ImageMagick. + It must be installed for the method to succeed. + **/ + const CImg& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_imagemagick_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "ImageMagick only writes the first image slice.", + cimg_instance,filename); +#ifdef cimg_use_png +#define _cimg_sie_extension1 "png" +#define _cimg_sie_extension2 "png" +#else +#define _cimg_sie_extension1 "pgm" +#define _cimg_sie_extension2 "ppm" +#endif + CImg command(1024), filename_tmp(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(), + cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); +#ifdef cimg_use_png + save_png(filename_tmp); +#else + save_pnm(filename_tmp); +#endif + cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"", + cimg::imagemagick_path(),quality, + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::imagemagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimg_instance + "save_imagemagick_external(): Failed to save file '%s' with " + "external command 'magick/convert'.", + cimg_instance, + filename); + + if (file) cimg::fclose(file); + std::remove(filename_tmp); + return *this; + } + + //! Save image as a Dicom file. + /** + \param filename Filename, as a C-string. + \note This method uses \c medcon, an external executable binary provided by + (X)Medcon. + It must be installed for the method to succeed. + **/ + const CImg& save_medcon_external(const char *const filename) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_medcon_external(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + CImg command(1024), filename_tmp(256), body(256); + std::FILE *file; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand()); + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + save_analyze(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"", + cimg::medcon_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::medcon_path()); + std::remove(filename_tmp); + cimg::split_filename(filename_tmp,body); + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data); + std::remove(filename_tmp); + + file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"m000-%s",filename); + file = cimg::std_fopen(command,"rb"); + if (!file) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimg_instance + "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.", + cimg_instance, + filename); + } + } + cimg::fclose(file); + std::rename(command,filename); + return *this; + } + + // Save image for non natively supported formats. + /** + \param filename Filename, as a C-string. + \param quality Image quality (expressed in percent), when the file format supports it. + \note + - The filename extension tells about the desired file format. + - This method tries to save the instance image as a file, using external tools from + ImageMagick or + GraphicsMagick. + At least one of these tool must be installed for the method to succeed. + - It is recommended to use the generic method save(const char*, int) const instead, + as it can handle some file formats natively. + **/ + const CImg& save_other(const char *const filename, const unsigned int quality=100) const { + if (!filename) + throw CImgArgumentException(_cimg_instance + "save_other(): Specified filename is (null).", + cimg_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + if (_depth>1) + cimg::warn(_cimg_instance + "save_other(): File '%s', saving a volumetric image with an external call to " + "ImageMagick or GraphicsMagick only writes the first image slice.", + cimg_instance,filename); + + const unsigned int omode = cimg::exception_mode(); + bool is_saved = true; + cimg::exception_mode(0); + try { save_magick(filename); } + catch (CImgException&) { + try { save_imagemagick_external(filename,quality); } + catch (CImgException&) { + try { save_graphicsmagick_external(filename,quality); } + catch (CImgException&) { + is_saved = false; + } + } + } + cimg::exception_mode(omode); + if (!is_saved) + throw CImgIOException(_cimg_instance + "save_other(): Failed to save file '%s'. Format is not natively supported, " + "and no external commands succeeded.", + cimg_instance, + filename); + return *this; + } + + //! Serialize a CImg instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { + return CImgList(*this,true).get_serialize(is_compressed); + } + + // [internal] Return a 40x38 color logo of a 'danger' item. + static CImg _logo40x38() { + CImg res(40,38,1,3); + const unsigned char *ptrs = cimg::logo40x38; + T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2); + for (ulongT off = 0; off<(ulongT)res._width*res._height;) { + const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++); + for (unsigned int l = 0; l structure + # + # + # + #------------------------------------------ + */ + //! Represent a list of images CImg. + template + struct CImgList { + unsigned int _width, _allocated_width; + CImg *_data; + + //! Simple iterator type, to loop through each image of a list. + /** + \note + - The \c CImgList::iterator type is defined as a CImg*. + - You may use it like this: + \code + CImgList<> list; // Assuming this image list is not empty + for (CImgList<>::iterator it = list.begin(); it* iterator; + + //! Simple const iterator type, to loop through each image of a \c const list instance. + /** + \note + - The \c CImgList::const_iterator type is defined to be a const CImg*. + - Similar to CImgList::iterator, but for constant list instances. + **/ + typedef const CImg* const_iterator; + + //! Pixel value type. + /** + Refer to the pixels value type of the images in the list. + \note + - The \c CImgList::value_type type of a \c CImgList is defined to be a \c T. + It is then similar to CImg::value_type. + - \c CImgList::value_type is actually not used in %CImg methods. It has been mainly defined for + compatibility with STL naming conventions. + **/ + typedef T value_type; + + // Define common types related to template type T. + typedef typename cimg::superset::type Tbool; + typedef typename cimg::superset::type Tuchar; + typedef typename cimg::superset::type Tchar; + typedef typename cimg::superset::type Tushort; + typedef typename cimg::superset::type Tshort; + typedef typename cimg::superset::type Tuint; + typedef typename cimg::superset::type Tint; + typedef typename cimg::superset::type Tulong; + typedef typename cimg::superset::type Tlong; + typedef typename cimg::superset::type Tfloat; + typedef typename cimg::superset::type Tdouble; + typedef typename cimg::last::type boolT; + typedef typename cimg::last::type ucharT; + typedef typename cimg::last::type charT; + typedef typename cimg::last::type ushortT; + typedef typename cimg::last::type shortT; + typedef typename cimg::last::type uintT; + typedef typename cimg::last::type intT; + typedef typename cimg::last::type ulongT; + typedef typename cimg::last::type longT; + typedef typename cimg::last::type uint64T; + typedef typename cimg::last::type int64T; + typedef typename cimg::last::type floatT; + typedef typename cimg::last::type doubleT; + + //@} + //--------------------------- + // + //! \name Plugins + //@{ + //--------------------------- +#ifdef cimglist_plugin +#include cimglist_plugin +#endif +#ifdef cimglist_plugin1 +#include cimglist_plugin1 +#endif +#ifdef cimglist_plugin2 +#include cimglist_plugin2 +#endif +#ifdef cimglist_plugin3 +#include cimglist_plugin3 +#endif +#ifdef cimglist_plugin4 +#include cimglist_plugin4 +#endif +#ifdef cimglist_plugin5 +#include cimglist_plugin5 +#endif +#ifdef cimglist_plugin6 +#include cimglist_plugin6 +#endif +#ifdef cimglist_plugin7 +#include cimglist_plugin7 +#endif +#ifdef cimglist_plugin8 +#include cimglist_plugin8 +#endif + + //@} + //-------------------------------------------------------- + // + //! \name Constructors / Destructor / Instance Management + //@{ + //-------------------------------------------------------- + + //! Destructor. + /** + Destroy current list instance. + \note + - Any allocated buffer is deallocated. + - Destroying an empty list does nothing actually. + **/ + ~CImgList() { + delete[] _data; + } + + //! Default constructor. + /** + Construct a new empty list instance. + \note + - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its + image buffer pointer data(). + - An empty list may be reassigned afterwards, with the family of the assign() methods. + In all cases, the type of pixels stays \c T. + **/ + CImgList(): + _width(0),_allocated_width(0),_data(0) {} + + //! Construct list containing empty images. + /** + \param n Number of empty images. + \note Useful when you know by advance the number of images you want to manage, as + it will allocate the right amount of memory for the list, without needs for reallocation + (that may occur when starting from an empty list and inserting several images in it). + **/ + explicit CImgList(const unsigned int n):_width(n) { + if (n) _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + else { _allocated_width = 0; _data = 0; } + } + + //! Construct list containing images of specified size. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \note Pixel values are not initialized and may probably contain garbage. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + } + + //! Construct list containing images of specified size, and initialize pixel values. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val Initialization value for images pixels. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T& val): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + } + + //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers. + /** + \param n Number of images. + \param width Width of images. + \param height Height of images. + \param depth Depth of images. + \param spectrum Number of channels of images. + \param val0 First value of the initializing integers sequence. + \param val1 Second value of the initializing integers sequence. + \warning You must specify at least width*height*depth*spectrum values in your argument list, + or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...): + _width(0),_allocated_width(0),_data(0) { +#define _CImgList_stdarg(t) { \ + assign(n,width,height,depth,spectrum); \ + const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \ + T *ptrd = _data->_data; \ + va_list ap; \ + va_start(ap,val1); \ + for (ulongT l = 0, s = 0, i = 0; iwidth*height*depth*spectrum values in your argument list, + or you will probably segfault. + **/ + CImgList(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...): + _width(0),_allocated_width(0),_data(0) { + _CImgList_stdarg(double); + } + + //! Construct list containing copies of an input image. + /** + \param n Number of images. + \param img Input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img. + **/ + template + CImgList(const unsigned int n, const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + } + + //! Construct list from one image. + /** + \param img Input image to copy in the constructed list. + \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img. + **/ + template + explicit CImgList(const CImg& img, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(1); + _data[0].assign(img,is_shared); + } + + //! Construct list from two images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + } + + //! Construct list from three images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + } + + //! Construct list from four images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); + } + + //! Construct list from five images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); + } + + //! Construct list from six images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + } + + //! Construct list from seven images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); + } + + //! Construct list from eight images. + /** + \param img1 First input image to copy in the constructed list. + \param img2 Second input image to copy in the constructed list. + \param img3 Third input image to copy in the constructed list. + \param img4 Fourth input image to copy in the constructed list. + \param img5 Fifth input image to copy in the constructed list. + \param img6 Sixth input image to copy in the constructed list. + \param img7 Seventh input image to copy in the constructed list. + \param img8 Eighth input image to copy in the constructed list. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false): + _width(0),_allocated_width(0),_data(0) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + } + + //! Construct list copy. + /** + \param list Input list to copy. + \note The shared state of each element of the constructed list is kept the same as in \c list. + **/ + template + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + } + + //! Construct list copy \specialization. + CImgList(const CImgList& list):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared); + } + + //! Construct list copy, and force the shared state of the list elements. + /** + \param list Input list to copy. + \param is_shared Tells if the elements of the list are shared or non-shared copies of input images. + **/ + template + CImgList(const CImgList& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) { + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],is_shared); + } + + //! Construct list by reading the content of a file. + /** + \param filename Filename, as a C-string. + **/ + explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) { + assign(filename); + } + + //! Construct list from the content of a display window. + /** + \param disp Display window to get content from. + \note Constructed list contains a single image only. + **/ + explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) { + assign(disp); + } + + //! Return a list with elements being shared copies of images in the list instance. + /** + \note list2 = list1.get_shared() is equivalent to list2.assign(list1,true). + **/ + CImgList get_shared() { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Return a list with elements being shared copies of images in the list instance \const. + const CImgList get_shared() const { + CImgList res(_width); + cimglist_for(*this,l) res[l].assign(_data[l],true); + return res; + } + + //! Destructor \inplace. + /** + \see CImgList(). + **/ + CImgList& assign() { + delete[] _data; + _width = _allocated_width = 0; + _data = 0; + return *this; + } + + //! Destructor \inplace. + /** + Equivalent to assign(). + \note Only here for compatibility with STL naming conventions. + **/ + CImgList& clear() { + return assign(); + } + + //! Construct list containing empty images \inplace. + /** + \see CImgList(unsigned int). + **/ + CImgList& assign(const unsigned int n) { + if (!n) return assign(); + if (_allocated_width(n<<2)) { + delete[] _data; + _data = new CImg[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))]; + } + _width = n; + return *this; + } + + //! Construct list containing images of specified size \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, + const unsigned int depth=1, const unsigned int spectrum=1) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum); + return *this; + } + + //! Construct list containing images of specified size, and initialize pixel values \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const T& val) { + assign(n); + cimglist_apply(*this,assign)(width,height,depth,spectrum,val); + return *this; + } + + //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace. + /** + \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) { + _CImgList_stdarg(int); + return *this; + } + + //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace. + /** + \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...). + **/ + CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, + const unsigned int depth, const unsigned int spectrum, + const double val0, const double val1, ...) { + _CImgList_stdarg(double); + return *this; + } + + //! Construct list containing copies of an input image \inplace. + /** + \see CImgList(unsigned int, const CImg&, bool). + **/ + template + CImgList& assign(const unsigned int n, const CImg& img, const bool is_shared=false) { + assign(n); + cimglist_apply(*this,assign)(img,is_shared); + return *this; + } + + //! Construct list from one image \inplace. + /** + \see CImgList(const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img, const bool is_shared=false) { + assign(1); + _data[0].assign(img,is_shared); + return *this; + } + + //! Construct list from two images \inplace. + /** + \see CImgList(const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const bool is_shared=false) { + assign(2); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); + return *this; + } + + //! Construct list from three images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool is_shared=false) { + assign(3); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + return *this; + } + + //! Construct list from four images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const bool is_shared=false) { + assign(4); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); + return *this; + } + + //! Construct list from five images \inplace. + /** + \see CImgList(const CImg&, const CImg&, const CImg&, const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const bool is_shared=false) { + assign(5); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); + return *this; + } + + //! Construct list from six images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const bool is_shared=false) { + assign(6); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + return *this; + } + + //! Construct list from seven images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const bool is_shared=false) { + assign(7); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); + return *this; + } + + //! Construct list from eight images \inplace. + /** + \see CImgList(const CImg&,const CImg&,const CImg&,const CImg&,const CImg&,const CImg&, + const CImg&, const CImg&, bool). + **/ + template + CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, + const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, + const bool is_shared=false) { + assign(8); + _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); + _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); + _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared); + return *this; + } + + //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace. + /** + \see CImgList(const CImgList&, bool is_shared). + **/ + template + CImgList& assign(const CImgList& list, const bool is_shared=false) { + cimg::unused(is_shared); + assign(list._width); + cimglist_for(*this,l) _data[l].assign(list[l],false); + return *this; + } + + //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization. + CImgList& assign(const CImgList& list, const bool is_shared=false) { + if (this==&list) return *this; + CImgList res(list._width); + cimglist_for(res,l) res[l].assign(list[l],is_shared); + return res.move_to(*this); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& assign(const char *const filename) { + return load(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& assign(const CImgDisplay &disp) { + return assign(CImg(disp)); + } + + //! Transfer the content of the list instance to another list. + /** + \param list Destination list. + \note When returning, the current list instance is empty and the initial content of \c list is destroyed. + **/ + template + CImgList& move_to(CImgList& list) { + list.assign(_width); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[l]); + assign(); + return list; + } + + //! Transfer the content of the list instance at a specified position in another list. + /** + \param list Destination list. + \param pos Index of the insertion in the list. + \note When returning, the list instance is empty and the initial content of \c list is preserved + (only images indexes may be modified). + **/ + template + CImgList& move_to(CImgList& list, const unsigned int pos) { + if (is_empty()) return list; + const unsigned int npos = pos>list._width?list._width:pos; + list.insert(_width,npos); + bool is_one_shared_element = false; + cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared; + if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]); + else cimglist_for(*this,l) _data[l].move_to(list[npos + l]); + assign(); + return list; + } + + //! Swap all fields between two list instances. + /** + \param list List to swap fields with. + \note Can be used to exchange the content of two lists in a fast way. + **/ + CImgList& swap(CImgList& list) { + cimg::swap(_width,list._width,_allocated_width,list._allocated_width); + cimg::swap(_data,list._data); + return list; + } + + //! Return a reference to an empty list. + /** + \note Can be used to define default values in a function taking a CImgList as an argument. + \code + void f(const CImgList& list=CImgList::empty()); + \endcode + **/ + static CImgList& empty() { + static CImgList _empty; + return _empty.assign(); + } + + //! Return a reference to an empty list \const. + static const CImgList& const_empty() { + static const CImgList _empty; + return _empty; + } + + //@} + //------------------------------------------ + // + //! \name Overloaded Operators + //@{ + //------------------------------------------ + + //! Return a reference to one image element of the list. + /** + \param pos Index of the image element. + **/ + CImg& operator()(const unsigned int pos) { +#if cimg_verbosity>=3 + if (pos>=_width) { + cimg::warn(_cimglist_instance + "operator(): Invalid image request, at position [%u].", + cimglist_instance, + pos); + return *_data; + } +#endif + return _data[pos]; + } + + //! Return a reference to one image of the list. + /** + \param pos Index of the image element. + **/ + const CImg& operator()(const unsigned int pos) const { + return const_cast*>(this)->operator()(pos); + } + + //! Return a reference to one pixel value of one image of the list. + /** + \param pos Index of the image element. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list(n,x,y,z,c) is equivalent to list[n](x,y,z,c). + **/ + T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) { + return (*this)[pos](x,y,z,c); + } + + //! Return a reference to one pixel value of one image of the list \const. + const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, + const unsigned int z=0, const unsigned int c=0) const { + return (*this)[pos](x,y,z,c); + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + operator CImg*() { + return _data; + } + + //! Return pointer to the first image of the list \const. + operator const CImg*() const { + return _data; + } + + //! Construct list from one image \inplace. + /** + \param img Input image to copy in the constructed list. + \note list = img; is equivalent to list.assign(img);. + **/ + template + CImgList& operator=(const CImg& img) { + return assign(img); + } + + //! Construct list from another list. + /** + \param list Input list to copy. + \note list1 = list2 is equivalent to list1.assign(list2);. + **/ + template + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list from another list \specialization. + CImgList& operator=(const CImgList& list) { + return assign(list); + } + + //! Construct list by reading the content of a file \inplace. + /** + \see CImgList(const char *const). + **/ + CImgList& operator=(const char *const filename) { + return assign(filename); + } + + //! Construct list from the content of a display window \inplace. + /** + \see CImgList(const CImgDisplay&). + **/ + CImgList& operator=(const CImgDisplay& disp) { + return assign(disp); + } + + //! Return a non-shared copy of a list. + /** + \note +list is equivalent to CImgList(list,false). + It forces the copy to have non-shared elements. + **/ + CImgList operator+() const { + return CImgList(*this,false); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end. + /** + \param img Image inserted at the end of the instance copy. + \note Define a convenient way to create temporary lists of images, as in the following code: + \code + (img1,img2,img3,img4).display("My four images"); + \endcode + **/ + template + CImgList& operator,(const CImg& img) { + return insert(img); + } + + //! Return a copy of the list instance, where image \c img has been inserted at the end \const. + template + CImgList operator,(const CImg& img) const { + return (+*this).insert(img); + } + + //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end. + /** + \param list List inserted at the end of the instance copy. + **/ + template + CImgList& operator,(const CImgList& list) { + return insert(list); + } + + //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const. + template + CImgList& operator,(const CImgList& list) const { + return (+*this).insert(list); + } + + //! Return image corresponding to the appending of all images of the instance list along specified axis. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \note list>'x' is equivalent to list.get_append('x'). + **/ + CImg operator>(const char axis) const { + return get_append(axis,0); + } + + //! Return list corresponding to the splitting of all images of the instance list along specified axis. + /** + \param axis Axis used for image splitting. + \note list<'x' is equivalent to list.get_split('x'). + **/ + CImgList operator<(const char axis) const { + return get_split(axis); + } + + //@} + //------------------------------------- + // + //! \name Instance Characteristics + //@{ + //------------------------------------- + + //! Return the type of image pixel values as a C string. + /** + Return a \c char* string containing the usual type name of the image pixel values + (i.e. a stringified version of the template parameter \c T). + \note + - The returned string may contain spaces (as in \c "unsigned char"). + - If the pixel type \c T does not correspond to a registered type, the string "unknown" is returned. + **/ + static const char* pixel_type() { + return cimg::type::string(); + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to size() but returns result as a (signed) integer. + **/ + int width() const { + return (int)_width; + } + + //! Return the size of the list, i.e. the number of images contained in it. + /** + \note Similar to width() but returns result as an unsigned integer. + **/ + unsigned int size() const { + return _width; + } + + //! Return pointer to the first image of the list. + /** + \note Images in a list are stored as a buffer of \c CImg. + **/ + CImg *data() { + return _data; + } + + //! Return pointer to the first image of the list \const. + const CImg *data() const { + return _data; + } + + //! Return pointer to the pos-th image of the list. + /** + \param pos Index of the image element to access. + \note list.data(n); is equivalent to list.data + n;. + **/ +#if cimg_verbosity>=3 + CImg *data(const unsigned int pos) { + if (pos>=size()) + cimg::warn(_cimglist_instance + "data(): Invalid pointer request, at position [%u].", + cimglist_instance, + pos); + return _data + pos; + } + + const CImg *data(const unsigned int l) const { + return const_cast*>(this)->data(l); + } +#else + CImg *data(const unsigned int l) { + return _data + l; + } + + //! Return pointer to the pos-th image of the list \const. + const CImg *data(const unsigned int l) const { + return _data + l; + } +#endif + + //! Return iterator to the first image of the list. + /** + **/ + iterator begin() { + return _data; + } + + //! Return iterator to the first image of the list \const. + const_iterator begin() const { + return _data; + } + + //! Return iterator to one position after the last image of the list. + /** + **/ + iterator end() { + return _data + _width; + } + + //! Return iterator to one position after the last image of the list \const. + const_iterator end() const { + return _data + _width; + } + + //! Return reference to the first image of the list. + /** + **/ + CImg& front() { + return *_data; + } + + //! Return reference to the first image of the list \const. + const CImg& front() const { + return *_data; + } + + //! Return a reference to the last image of the list. + /** + **/ + const CImg& back() const { + return *(_data + _width - 1); + } + + //! Return a reference to the last image of the list \const. + CImg& back() { + return *(_data + _width - 1); + } + + //! Return pos-th image of the list. + /** + \param pos Index of the image element to access. + **/ + CImg& at(const int pos) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "at(): Empty instance.", + cimglist_instance); + + return _data[cimg::cut(pos,0,width() - 1)]; + } + + //! Access to pixel value with Dirichlet boundary conditions. + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions. + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZC(p,x,y,z,c); is equivalent to list[p].atXYZC(x,y,z,c);. + **/ + T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions \const. + T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZC(): Empty instance.", + cimglist_instance); + + return _atNXYZC(pos,x,y,z,c); + } + + T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) { + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); + } + + T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const { + return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c); + } + + //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const. + T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXYZ(): Empty instance.", + cimglist_instance); + + return _atNXYZ(pos,x,y,z,c); + } + + T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); + } + + T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const. + T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNXY(): Empty instance.", + cimglist_instance); + + return _atNXY(pos,x,y,z,c); + } + + T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); + } + + T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const. + T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value); + } + + //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const. + T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atNX(): Empty instance.", + cimglist_instance); + + return _atNX(pos,x,y,z,c); + } + + T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); + } + + T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \param out_value Default value returned if \c offset is outside image bounds. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) { + return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c); + } + + //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const. + T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const { + return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos). + /** + \param pos Index of the image element to access. + \param x X-coordinate of the pixel value. + \param y Y-coordinate of the pixel value. + \param z Z-coordinate of the pixel value. + \param c C-coordinate of the pixel value. + \note list.atNXYZ(p,x,y,z,c); is equivalent to list[p].atXYZ(x,y,z,c);. + **/ + T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const. + T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "atN(): Empty instance.", + cimglist_instance); + return _atN(pos,x,y,z,c); + } + + T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) { + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); + } + + T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const { + return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c); + } + + //@} + //------------------------------------- + // + //! \name Instance Checking + //@{ + //------------------------------------- + + //! Return \c true if list is empty. + /** + **/ + bool is_empty() const { + return (!_data || !_width); + } + + //! Test if number of image elements is equal to specified value. + /** + \param size_n Number of image elements to test. + **/ + bool is_sameN(const unsigned int size_n) const { + return _width==size_n; + } + + //! Test if number of image elements is equal between two images lists. + /** + \param list Input list to compare with. + **/ + template + bool is_sameN(const CImgList& list) const { + return is_sameN(list._width); + } + + // Define useful functions to check list dimensions. + // (cannot be documented because macro-generated). +#define _cimglist_def_is_same1(axis) \ + bool is_same##axis(const unsigned int val) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \ + return res; \ + } \ + bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \ + return is_sameN(n) && is_same##axis(val); \ + } \ + +#define _cimglist_def_is_same2(axis1,axis2) \ + bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \ + return res; \ + } \ + bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \ + return is_sameN(n) && is_same##axis1##axis2(val1,val2); \ + } \ + +#define _cimglist_def_is_same3(axis1,axis2,axis3) \ + bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \ + const unsigned int val3) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \ + return res; \ + } \ + bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \ + const unsigned int val2, const unsigned int val3) const { \ + return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \ + } \ + +#define _cimglist_def_is_same(axis) \ + template bool is_same##axis(const CImg& img) const { \ + bool res = true; \ + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \ + return res; \ + } \ + template bool is_same##axis(const CImgList& list) const { \ + const unsigned int lmin = std::min(_width,list._width); \ + bool res = true; \ + for (unsigned int l = 0; l bool is_sameN##axis(const unsigned int n, const CImg& img) const { \ + return (is_sameN(n) && is_same##axis(img)); \ + } \ + template bool is_sameN##axis(const CImgList& list) const { \ + return (is_sameN(list) && is_same##axis(list)); \ + } + + _cimglist_def_is_same(XY) + _cimglist_def_is_same(XZ) + _cimglist_def_is_same(XC) + _cimglist_def_is_same(YZ) + _cimglist_def_is_same(YC) + _cimglist_def_is_same(XYZ) + _cimglist_def_is_same(XYC) + _cimglist_def_is_same(YZC) + _cimglist_def_is_same(XYZC) + _cimglist_def_is_same1(X) + _cimglist_def_is_same1(Y) + _cimglist_def_is_same1(Z) + _cimglist_def_is_same1(C) + _cimglist_def_is_same2(X,Y) + _cimglist_def_is_same2(X,Z) + _cimglist_def_is_same2(X,C) + _cimglist_def_is_same2(Y,Z) + _cimglist_def_is_same2(Y,C) + _cimglist_def_is_same2(Z,C) + _cimglist_def_is_same3(X,Y,Z) + _cimglist_def_is_same3(X,Y,C) + _cimglist_def_is_same3(X,Z,C) + _cimglist_def_is_same3(Y,Z,C) + + //! Test if dimensions of each image of the list match specified arguments. + /** + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameXYZC(const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) const { + bool res = true; + for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc); + return res; + } + + //! Test if list dimensions match specified arguments. + /** + \param n Number of images in the list. + \param dx Checked image width. + \param dy Checked image height. + \param dz Checked image depth. + \param dc Checked image spectrum. + **/ + bool is_sameNXYZC(const unsigned int n, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) const { + return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc); + } + + //! Test if list contains one particular pixel location. + /** + \param n Index of the image whom checked pixel value belong to. + \param x X-coordinate of the checked pixel value. + \param y Y-coordinate of the checked pixel value. + \param z Z-coordinate of the checked pixel value. + \param c C-coordinate of the checked pixel value. + **/ + bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const { + if (is_empty()) return false; + return n>=0 && n=0 && x<_data[n].width() && y>=0 && y<_data[n].height() && + z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum(); + } + + //! Test if list contains image with specified index. + /** + \param n Index of the checked image. + **/ + bool containsN(const int n) const { + if (is_empty()) return false; + return n>=0 && n + bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const { + if (is_empty()) return false; + cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; } + return false; + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \param[out] z Z-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y,z). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y, t& z) const { + t c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \param[out] y Y-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x,y). + **/ + template + bool contains(const T& pixel, t& n, t& x, t&y) const { + t z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \param[out] x X-coordinate of the pixel value, if test succeeds. + \note If true, set coordinates (n,x). + **/ + template + bool contains(const T& pixel, t& n, t& x) const { + t y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + \param[out] n Index of image containing the pixel value, if test succeeds. + \note If true, set coordinates (n). + **/ + template + bool contains(const T& pixel, t& n) const { + t x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if one of the image list contains the specified referenced value. + /** + \param pixel Reference to pixel value to test. + **/ + bool contains(const T& pixel) const { + unsigned int n, x, y, z, c; + return contains(pixel,n,x,y,z,c); + } + + //! Test if the list contains the image 'img'. + /** + \param img Reference to image to test. + \param[out] n Index of image in the list, if test succeeds. + \note If true, returns the position (n) of the image in the list. + **/ + template + bool contains(const CImg& img, t& n) const { + if (is_empty()) return false; + const CImg *const ptr = &img; + cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; } + return false; + } + + //! Test if the list contains the image img. + /** + \param img Reference to image to test. + **/ + bool contains(const CImg& img) const { + unsigned int n; + return contains(img,n); + } + + //@} + //------------------------------------- + // + //! \name Mathematical Functions + //@{ + //------------------------------------- + + //! Return a reference to the minimum pixel value of the instance list. + /** + **/ + T& min() { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the maximum pixel value of the instance list \const. + const T& max() const { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T max_value = *ptr_max; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs); + } + return *ptr_max; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + T& min_max(t& max_val) { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min_max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const. + /** + \param[out] max_val Value of the maximum value found. + **/ + template + const T& min_max(t& max_val) const { + bool is_all_empty = true; + T *ptr_min = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_min = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "min_max(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_min, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (valmax_value) max_value = val; + } + } + max_val = (t)max_value; + return *ptr_min; + } + + //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well. + /** + \param[out] min_val Value of the minimum value found. + **/ + template + T& max_min(t& min_val) { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max_min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + const T& max_min(t& min_val) const { + bool is_all_empty = true; + T *ptr_max = 0; + cimglist_for(*this,l) if (!_data[l].is_empty()) { + ptr_max = _data[l]._data; + is_all_empty = false; + break; + } + if (is_all_empty) + throw CImgInstanceException(_cimglist_instance + "max_min(): %s.", + _data?"List of empty images":"Empty instance", + cimglist_instance); + T min_value = *ptr_max, max_value = min_value; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_for(img,ptrs,T) { + const T val = *ptrs; + if (val>max_value) { max_value = val; ptr_max = ptrs; } + if (val + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " + "at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + if (is_shared) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified shared image " + "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).", + cimglist_instance, + img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos); + + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list + _data = new_data; + *_data = img; + } else { + if (new_data) { // Insert with re-allocation + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); + delete[] _data; + _data = new_data; + } else if (npos!=_width - 1) // Insert without re-allocation + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; + _data[npos] = img; + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization. + CImgList& insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if (npos>_width) + throw CImgArgumentException(_cimglist_instance + "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) " + "at position %u.", + cimglist_instance, + img._width,img._height,img._depth,img._spectrum,img._data,npos); + CImg *const new_data = (++_width>_allocated_width)?new CImg[_allocated_width?(_allocated_width<<=1): + (_allocated_width=16)]:0; + if (!_data) { // Insert new element into empty list + _data = new_data; + if (is_shared && img) { + _data->_width = img._width; + _data->_height = img._height; + _data->_depth = img._depth; + _data->_spectrum = img._spectrum; + _data->_is_shared = true; + _data->_data = img._data; + } else *_data = img; + } + else { + if (new_data) { // Insert with re-allocation + if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos); + if (npos!=_width - 1) + std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + if (is_shared && img) { + new_data[npos]._width = img._width; + new_data[npos]._height = img._height; + new_data[npos]._depth = img._depth; + new_data[npos]._spectrum = img._spectrum; + new_data[npos]._is_shared = true; + new_data[npos]._data = img._data; + } else { + new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; + new_data[npos]._data = 0; + new_data[npos] = img; + } + std::memset((void*)_data,0,sizeof(CImg)*(_width - 1)); + delete[] _data; + _data = new_data; + } else { // Insert without re-allocation + if (npos!=_width - 1) + std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg)*(_width - 1 - npos)); + if (is_shared && img) { + _data[npos]._width = img._width; + _data[npos]._height = img._height; + _data[npos]._depth = img._depth; + _data[npos]._spectrum = img._spectrum; + _data[npos]._is_shared = true; + _data[npos]._data = img._data; + } else { + _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; + _data[npos]._data = 0; + _data[npos] = img; + } + } + } + return *this; + } + + //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance. + template + CImgList get_insert(const CImg& img, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(img,pos,is_shared); + } + + //! Insert n empty images img into the current image list, at position \p pos. + /** + \param n Number of empty images to insert. + \param pos Index of the insertion. + **/ + CImgList& insert(const unsigned int n, const unsigned int pos=~0U) { + CImg empty; + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i get_insert(const unsigned int n, const unsigned int pos=~0U) const { + return (+*this).insert(n,pos); + } + + //! Insert \c n copies of the image \c img into the current image list, at position \c pos. + /** + \param n Number of image copies to insert. + \param img Image to insert by copy. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of \c img or not. + **/ + template + CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, + const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + insert(img,npos,is_shared); + for (unsigned int i = 1; i + CImgList get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,img,pos,is_shared); + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos. + /** + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) { + const unsigned int npos = pos==~0U?_width:pos; + if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared); + else insert(CImgList(list),npos,is_shared); + return *this; + } + + //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance. + template + CImgList get_insert(const CImgList& list, const unsigned int pos=~0U, const bool is_shared=false) const { + return (+*this).insert(list,pos,is_shared); + } + + //! Insert n copies of the list \c list at position \c pos of the current list. + /** + \param n Number of list copies to insert. + \param list Image list to insert. + \param pos Index of the insertion. + \param is_shared Tells if inserted images are shared copies of images of \c list or not. + **/ + template + CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) { + if (!n) return *this; + const unsigned int npos = pos==~0U?_width:pos; + for (unsigned int i = 0; i + CImgList get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, + const bool is_shared=false) const { + return (+*this).insert(n,list,pos,is_shared); + } + + //! Remove all images between from indexes. + /** + \param pos1 Starting index of the removal. + \param pos2 Ending index of the removal. + **/ + CImgList& remove(const unsigned int pos1, const unsigned int pos2) { + const unsigned int + npos1 = pos1=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + else { + if (tpos2>=_width) + throw CImgArgumentException(_cimglist_instance + "remove(): Invalid remove request at positions %u->%u.", + cimglist_instance, + npos1,tpos2); + + for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign(); + const unsigned int nb = 1 + npos2 - npos1; + if (!(_width-=nb)) return assign(); + if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation + if (npos1!=_width) + std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + std::memset((void*)(_data + _width),0,sizeof(CImg)*nb); + } else { // Removing items with reallocation + _allocated_width>>=4; + while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1; + CImg *const new_data = new CImg[_allocated_width]; + if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg)*npos1); + if (npos1!=_width) + std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg)*(_width - npos1)); + if (_width!=_allocated_width) + std::memset((void*)(new_data + _width),0,sizeof(CImg)*(_allocated_width - _width)); + std::memset((void*)_data,0,sizeof(CImg)*(_width + nb)); + delete[] _data; + _data = new_data; + } + } + return *this; + } + + //! Remove all images between from indexes \newinstance. + CImgList get_remove(const unsigned int pos1, const unsigned int pos2) const { + return (+*this).remove(pos1,pos2); + } + + //! Remove image at index \c pos from the image list. + /** + \param pos Index of the image to remove. + **/ + CImgList& remove(const unsigned int pos) { + return remove(pos,pos); + } + + //! Remove image at index \c pos from the image list \newinstance. + CImgList get_remove(const unsigned int pos) const { + return (+*this).remove(pos); + } + + //! Remove last image. + /** + **/ + CImgList& remove() { + return remove(_width - 1); + } + + //! Remove last image \newinstance. + CImgList get_remove() const { + return (+*this).remove(); + } + + //! Reverse list order. + CImgList& reverse() { + for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]); + return *this; + } + + //! Reverse list order \newinstance. + CImgList get_reverse() const { + return (+*this).reverse(); + } + + //! Return a sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList& images(const unsigned int pos0, const unsigned int pos1) { + return get_images(pos0,pos1).move_to(*this); + } + + //! Return a sublist \newinstance. + CImgList get_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l]); + return res; + } + + //! Return a shared sublist. + /** + \param pos0 Starting index of the sublist. + \param pos1 Ending index of the sublist. + **/ + CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); + return res; + } + + //! Return a shared sublist \newinstance. + const CImgList get_shared_images(const unsigned int pos0, const unsigned int pos1) const { + if (pos0>pos1 || pos1>=_width) + throw CImgArgumentException(_cimglist_instance + "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.", + cimglist_instance, + pos0,pos1); + CImgList res(pos1 - pos0 + 1); + cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false); + return res; + } + + //! Return a single image which is the appending of all images of the current CImgList instance. + /** + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + **/ + CImg get_append(const char axis, const float align=0) const { + if (is_empty()) return CImg(); + if (_width==1) return +((*this)[0]); + unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0; + CImg res; + switch (cimg::lowercase(axis)) { + case 'x' : { // Along the X-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx+=img._width; + dy = std::max(dy,img._height); + dz = std::max(dz,img._depth); + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._height==1 && img._depth==1 && img._spectrum==1 && + res._height==1 && res._depth==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._width); + else + res.draw_image(pos, + (int)(align*(dy - img._height)), + (int)(align*(dz - img._depth)), + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._width; + } + } break; + case 'y' : { // Along the Y-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy+=img._height; + dz = std::max(dz,img._depth); + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._depth==1 && img._spectrum==1 && + res._width==1 && res._depth==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._height); + else + res.draw_image((int)(align*(dx - img._width)), + pos, + (int)(align*(dz - img._depth)), + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._height; + } + } break; + case 'z' : { // Along the Z-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy = std::max(dy,img._height); + dz+=img._depth; + dc = std::max(dc,img._spectrum); + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._height==1 && img._spectrum==1 && + res._width==1 && res._height==1 && res._spectrum==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._depth); + else + res.draw_image((int)(align*(dx - img._width)), + (int)(align*(dy - img._height)), + pos, + (int)(align*(dc - img._spectrum)), + img); + } + pos+=img._depth; + } + } break; + default : { // Along the C-axis + cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + dx = std::max(dx,img._width); + dy = std::max(dy,img._height); + dz = std::max(dz,img._depth); + dc+=img._spectrum; + } + } + res.assign(dx,dy,dz,dc,(T)0); + if (res) cimglist_for(*this,l) { + const CImg& img = (*this)[l]; + if (img) { + if (img._width==1 && img._height==1 && img._depth==1 && + res._width==1 && res._height==1 && res._depth==1) + std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum); + else + res.draw_image((int)(align*(dx - img._width)), + (int)(align*(dy - img._height)), + (int)(align*(dz - img._depth)), + pos, + img); + } + pos+=img._spectrum; + } + } + } + return res; + } + + //! Return a list where each image has been split along the specified axis. + /** + \param axis Axis to split images along. + \param nb Number of split parts for each image. + **/ + CImgList& split(const char axis, const int nb=-1) { + return get_split(axis,nb).move_to(*this); + } + + //! Return a list where each image has been split along the specified axis \newinstance. + CImgList get_split(const char axis, const int nb=-1) const { + CImgList res; + cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U); + return res; + } + + //! Insert image at the end of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_back(const CImg& img) { + return insert(img); + } + + //! Insert image at the front of the list. + /** + \param img Image to insert. + **/ + template + CImgList& push_front(const CImg& img) { + return insert(img,0); + } + + //! Insert list at the end of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_back(const CImgList& list) { + return insert(list); + } + + //! Insert list at the front of the current list. + /** + \param list List to insert. + **/ + template + CImgList& push_front(const CImgList& list) { + return insert(list,0); + } + + //! Remove last image. + /** + **/ + CImgList& pop_back() { + return remove(_width - 1); + } + + //! Remove first image. + /** + **/ + CImgList& pop_front() { + return remove(0); + } + + //! Remove image pointed by iterator. + /** + \param iter Iterator pointing to the image to remove. + **/ + CImgList& erase(const iterator iter) { + return remove(iter - _data); + } + + //@} + //---------------------------------- + // + //! \name Data Input + //@{ + //---------------------------------- + + //! Display a simple interactive interface to select images or sublists. + /** + \param disp Window instance to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \param exit_on_anykey Exit function when any key is pressed. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(CImgDisplay &disp, const bool feature_type=true, + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false); + } + + //! Display a simple interactive interface to select images or sublists. + /** + \param title Title of a new window used to display selection and user interface. + \param feature_type Can be \c false to select a single image, or \c true to select a sublist. + \param axis Axis along whom images are appended for visualization. + \param align Alignment setting when images have not all the same size. + \param exit_on_anykey Exit function when any key is pressed. + \return A one-column vector containing the selected image indexes. + **/ + CImg get_select(const char *const title, const bool feature_type=true, + const char axis='x', const float align=0, + const bool exit_on_anykey=false) const { + CImgDisplay disp; + return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false); + } + + CImg _select(CImgDisplay &disp, const char *const title, const bool feature_type, + const char axis, const float align, const bool exit_on_anykey, + const unsigned int orig, const bool resize_disp, + const bool exit_on_rightbutton, const bool exit_on_wheel) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "select(): Empty instance.", + cimglist_instance); + + // Create image correspondence table and get list dimensions for visualization. + CImgList _indices; + unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg& img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + if (h>max_height) max_height = h; + sum_width+=w; sum_height+=h; + if (axis=='x') CImg(w,1,1,1,(unsigned int)l).move_to(_indices); + else CImg(h,1,1,1,(unsigned int)l).move_to(_indices); + } + const CImg indices0 = _indices>'x'; + + // Create display window. + if (!disp) { + if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1); + else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1); + if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else { + if (title) disp.set_title("%s",title); + disp.move_inside_screen(); + } + if (resize_disp) { + if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false); + else disp.resize(cimg_fitscreen(max_width,sum_height,1),false); + } + + const unsigned int old_normalization = disp.normalization(); + bool old_is_resized = disp.is_resized(); + disp._normalization = 0; + disp.show().set_key(0).show_mouse(); + static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 }; + + // Enter event loop. + CImg visu0, visu; + CImg indices; + CImg positions(_width,4,1,1,-1); + int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1; + bool is_clicked = false, is_selected = false, text_down = false, update_display = true; + unsigned int key = 0, font_size = 32; + + while (!is_selected && !disp.is_closed() && !key) { + + // Create background image. + if (!visu0) { + visu0.assign(disp._width,disp._height,1,3,0); visu.assign(); + (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices); + unsigned int _ind = 0; + const CImg onexone(1,1,1,1,(T)0); + if (axis=='x') + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) + cimglist_for(*this,ind) { + unsigned int x0 = 0; + while (x0 &src = _data[ind]?_data[ind]:onexone; + CImg res; + src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2). + move_to(res); + const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true); + res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)x0; + positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height())); + positions(ind,2)+=res._width; + positions(ind,3)+=res._height - 1; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + else + cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4)) + cimglist_for(*this,ind) { + unsigned int y0 = 0; + while (y0 &src = _data[ind]?_data[ind]:onexone; + CImg res; + src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2). + move_to(res); + const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false); + res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100); + positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width())); + positions(ind,1) = positions(ind,3) = (int)y0; + positions(ind,2)+=res._width - 1; + positions(ind,3)+=res._height; + visu0.draw_image(positions(ind,0),positions(ind,1),res); + } + if (axis=='x') --positions(_ind,2); else --positions(_ind,3); + update_display = true; + } + + if (!visu || oindex0!=index0 || oindex1!=index1) { + if (index0>=0 && index1>=0) { + visu.assign(visu0,false); + const int indm = std::min(index0,index1), indM = std::max(index0,index1); + for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) { + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + background_color,0.2f); + if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) || + (axis!='x' && positions(ind,3) - positions(ind,1)>=8)) + visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3), + foreground_color,0.9f,0xAAAAAAAA); + } + if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down, + orig + indm,orig + indM,indM - indm + 1); + else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down, + orig + index0, + _data[index0]._width, + _data[index0]._height, + _data[index0]._depth, + _data[index0]._spectrum); + update_display = true; + } else visu.assign(); + } + if (!visu) { visu.assign(visu0,true); update_display = true; } + if (update_display) { visu.display(disp); update_display = false; } + disp.wait(); + + // Manage user events. + const int xm = disp.mouse_x(), ym = disp.mouse_y(); + int index = -1; + + if (xm>=0) { + index = (int)indices(axis=='x'?xm:ym); + if (disp.button()&1) { + if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; } + oindex1 = index1; index1 = index; + if (!feature_type) is_selected = true; + } else { + if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; } + else is_selected = true; + } + } else { + if (is_clicked) { + if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; } + else index1 = -1; + } else index0 = index1 = -1; + } + + if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; } + if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; } + if (disp.wheel() && exit_on_wheel) is_selected = true; + + CImg filename(32); + switch (key = disp.key()) { +#if cimg_OS!=2 + case cimg::keyCTRLRIGHT : +#endif + case 0 : case cimg::keyCTRLLEFT : key = 0; break; + case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false), + CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.set_fullscreen(false). + resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false). + _is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true; + disp.set_key(key,false); key = 0; visu0.assign(); + } break; + case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.bmp",snap_number++); + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + if (visu0) { + (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp); + visu0.save(filename); + (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + } + disp.set_key(key,false).wait(); key = 0; + } break; + case cimg::keyO : + if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { + static unsigned int snap_number = 0; + std::FILE *file; + do { +#ifdef cimg_use_zlib + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimgz",snap_number++); +#else + cimg_snprintf(filename,filename._width,cimg_appname "_%.4u.cimg",snap_number++); +#endif + if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file); + } while (file); + (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp); + save(filename); + (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp); + disp.set_key(key,false).wait(); key = 0; + } break; + } + if (disp.is_resized()) { disp.resize(false); visu0.assign(); } + if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }} + else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }} + if (!exit_on_anykey && key && key!=cimg::keyESC && + (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) { + key = 0; + } + } + CImg res(1,2,1,1,-1); + if (is_selected) { + if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1)); + else res.fill(index0); + } + if (!(disp.button()&2)) disp.set_button(); + disp._normalization = old_normalization; + disp._is_resized = old_is_resized; + disp.set_key(key); + return res; + } + + //! Load a list from a file. + /** + \param filename Filename to read data from. + **/ + CImgList& load(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load(): Specified filename is (null).", + cimglist_instance); + + if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) { + CImg filename_local(256); + load(cimg::load_network(filename,filename_local)); + std::remove(filename_local); + return *this; + } + + const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + bool is_loaded = true; + try { +#ifdef cimglist_load_plugin + cimglist_load_plugin(filename); +#endif +#ifdef cimglist_load_plugin1 + cimglist_load_plugin1(filename); +#endif +#ifdef cimglist_load_plugin2 + cimglist_load_plugin2(filename); +#endif +#ifdef cimglist_load_plugin3 + cimglist_load_plugin3(filename); +#endif +#ifdef cimglist_load_plugin4 + cimglist_load_plugin4(filename); +#endif +#ifdef cimglist_load_plugin5 + cimglist_load_plugin5(filename); +#endif +#ifdef cimglist_load_plugin6 + cimglist_load_plugin6(filename); +#endif +#ifdef cimglist_load_plugin7 + cimglist_load_plugin7(filename); +#endif +#ifdef cimglist_load_plugin8 + cimglist_load_plugin8(filename); +#endif + if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) load_tiff(filename); + else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(ext,"cimg") || + !cimg::strcasecmp(ext,"cimgz") || + !*ext) load_cimg(filename); + else if (!cimg::strcasecmp(ext,"rec") || + !cimg::strcasecmp(ext,"par")) load_parrec(filename); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) load_video(filename); + else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + + // If nothing loaded, try to guess file format from magic number in file. + if (!is_loaded && !is_stdin) { + std::FILE *const file = cimg::std_fopen(filename,"rb"); + if (!file) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to open file '%s'.", + cimglist_instance, + filename); + } + + const char *const f_type = cimg::ftype(file,filename); + cimg::fclose(file); + is_loaded = true; + try { + if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename); + else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename); + else is_loaded = false; + } catch (CImgIOException&) { is_loaded = false; } + } + + // If nothing loaded, try to load file as a single image. + if (!is_loaded) { + assign(1); + try { + _data->load(filename); + } catch (CImgIOException&) { + cimg::exception_mode(omode); + throw CImgIOException(_cimglist_instance + "load(): Failed to recognize format of file '%s'.", + cimglist_instance, + filename); + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load a list from a file \newinstance. + static CImgList get_load(const char *const filename) { + return CImgList().load(filename); + } + + //! Load a list from a .cimg file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_cimg(const char *const filename) { + return _load_cimg(0,filename); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename) { + return CImgList().load_cimg(filename); + } + + //! Load a list from a .cimg file. + /** + \param file File to read data from. + **/ + CImgList& load_cimg(std::FILE *const file) { + return _load_cimg(file,0); + } + + //! Load a list from a .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file) { + return CImgList().load_cimg(file); + } + + CImgList& _load_cimg(std::FILE *const file, const char *const filename) { +#ifdef cimg_use_zlib +#define _cimgz_load_cimg_case(Tss) { \ + Bytef *const cbuf = new Bytef[csiz]; \ + cimg::fread(cbuf,(size_t)csiz,nfile); \ + if (is_bool) { \ + CImg raw(W*H*D*C/8); \ + uLongf destlen = (uLongf)raw.size(); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ + img.assign(W,H,D,C); \ + img._uchar2bool(raw,raw.size(),false); \ + } else { \ + CImg raw(W,H,D,C); \ + uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \ + uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } \ + delete[] cbuf; \ +} +#else +#define _cimgz_load_cimg_case(Tss) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \ + cimglist_instance, \ + filename?filename:"(FILE*)"); +#endif + +#define _cimg_load_cimg_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + const bool is_bool = cimg::type::string()==cimg::type::string(); \ + for (unsigned int l = 0; l=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; csiz = 0; \ + if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:("(FILE*)")); \ + if (W*H*D*C>0) { \ + CImg &img = _data[l]; \ + if (err==5) _cimgz_load_cimg_case(Tss) \ + else { \ + img.assign(W,H,D,C); \ + T *ptrd = img._data; \ + if (is_bool) { \ + CImg raw; \ + for (ulongT to_read = img.size(); to_read; ) { \ + raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + CImg(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\ + _uchar2bool(raw,raw._width,false); \ + to_read-=raw._width; \ + } \ + } else { \ + CImg raw; \ + for (ulongT to_read = img.size(); to_read; ) { \ + raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + const Tss *ptrs = raw._data; \ + for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + to_read-=raw._width; \ + } \ + } \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + + const ulongT cimg_iobuffer = (ulongT)24*1024*1024; + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N = 0, W, H, D, C; + cimg_uint64 csiz; + int i, err; + do { + j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; + } while (*tmp=='#' && i>=0); + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + assign(N); + _cimg_load_cimg_case("bool",bool); + _cimg_load_cimg_case("unsigned_char",unsigned char); + _cimg_load_cimg_case("uchar",unsigned char); + _cimg_load_cimg_case("char",char); + _cimg_load_cimg_case("unsigned_short",unsigned short); + _cimg_load_cimg_case("ushort",unsigned short); + _cimg_load_cimg_case("short",short); + _cimg_load_cimg_case("unsigned_int",unsigned int); + _cimg_load_cimg_case("uint",unsigned int); + _cimg_load_cimg_case("int",int); + _cimg_load_cimg_case("unsigned_long",ulongT); + _cimg_load_cimg_case("ulong",ulongT); + _cimg_load_cimg_case("long",longT); + _cimg_load_cimg_case("unsigned_int64",uint64T); + _cimg_load_cimg_case("uint64",uint64T); + _cimg_load_cimg_case("int64",int64T); + _cimg_load_cimg_case("float",float); + _cimg_load_cimg_case("double",double); + + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype._data,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a sublist list from a (non compressed) .cimg file. + /** + \param filename Filename to read data from. + \param n0 Starting index of images to read (~0U for max). + \param n1 Ending index of images to read (~0U for max). + \param x0 Starting X-coordinates of image regions to read. + \param y0 Starting Y-coordinates of image regions to read. + \param z0 Starting Z-coordinates of image regions to read. + \param c0 Starting C-coordinates of image regions to read. + \param x1 Ending X-coordinates of image regions to read (~0U for max). + \param y1 Ending Y-coordinates of image regions to read (~0U for max). + \param z1 Ending Z-coordinates of image regions to read (~0U for max). + \param c1 Ending C-coordinates of image regions to read (~0U for max). + **/ + CImgList& load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sublist list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \overloading. + CImgList& load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + //! Load a sub-image list from a (non compressed) .cimg file \newinstance. + static CImgList get_load_cimg(std::FILE *const file, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { + return CImgList().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1); + } + + CImgList& _load_cimg(std::FILE *const file, const char *const filename, + const unsigned int n0, const unsigned int n1, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0, + const unsigned int x1, const unsigned int y1, + const unsigned int z1, const unsigned int c1) { +#define _cimg_load_cimg_case2(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l<=nn1; ++l) { \ + j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \ + W = H = D = C = 0; \ + if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \ + throw CImgIOException(_cimglist_instance \ + "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \ + cimglist_instance, \ + W,H,D,C,l,filename?filename:"(FILE*)"); \ + if (W*H*D*C>0) { \ + if (l=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const unsigned int \ + _nx1 = nx1==~0U?W - 1:nx1, \ + _ny1 = ny1==~0U?H - 1:ny1, \ + _nz1 = nz1==~0U?D - 1:nz1, \ + _nc1 = nc1==~0U?C - 1:nc1; \ + if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \ + throw CImgArgumentException(_cimglist_instance \ + "load_cimg(): Invalid specified coordinates " \ + "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \ + "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \ + cimglist_instance, \ + n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \ + CImg raw(1 + _nx1 - nx0); \ + CImg &img = _data[l - nn0]; \ + img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \ + T *ptrd = img._data; \ + ulongT skipvb = nc0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \ + const ulongT skipzb = nz0*W*H*sizeof(Tss); \ + if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \ + const ulongT skipyb = ny0*W*sizeof(Tss); \ + if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \ + const ulongT skipxb = nx0*sizeof(Tss); \ + if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ + cimg::fread(raw._data,raw._width,nfile); \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \ + const Tss *ptrs = raw._data; \ + for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \ + const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + loaded = true; \ + } + + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Specified filename is (null).", + cimglist_instance); + unsigned int + nn0 = std::min(n0,n1), nn1 = std::max(n0,n1), + nx0 = std::min(x0,x1), nx1 = std::max(x0,x1), + ny0 = std::min(y0,y1), ny1 = std::max(y0,y1), + nz0 = std::min(z0,z1), nz1 = std::max(z0,z1), + nc0 = std::min(c0,c1), nc1 = std::max(c0,c1); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool loaded = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N, W, H, D, C; + int i, err; + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]", + &N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + nn1 = n1==~0U?N - 1:n1; + if (nn1>=N) + throw CImgArgumentException(_cimglist_instance + "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " + "because file '%s' contains only %u images.", + cimglist_instance, + n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N); + assign(1 + nn1 - n0); + _cimg_load_cimg_case2("bool",bool); + _cimg_load_cimg_case2("unsigned_char",unsigned char); + _cimg_load_cimg_case2("uchar",unsigned char); + _cimg_load_cimg_case2("char",char); + _cimg_load_cimg_case2("unsigned_short",unsigned short); + _cimg_load_cimg_case2("ushort",unsigned short); + _cimg_load_cimg_case2("short",short); + _cimg_load_cimg_case2("unsigned_int",unsigned int); + _cimg_load_cimg_case2("uint",unsigned int); + _cimg_load_cimg_case2("int",int); + _cimg_load_cimg_case2("unsigned_long",ulongT); + _cimg_load_cimg_case2("ulong",ulongT); + _cimg_load_cimg_case2("long",longT); + _cimg_load_cimg_case2("unsigned_int64",uint64T); + _cimg_load_cimg_case2("uint64",uint64T); + _cimg_load_cimg_case2("int64",int64T); + _cimg_load_cimg_case2("float",float); + _cimg_load_cimg_case2("double",double); + if (!loaded) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_cimg(): Unsupported pixel type '%s' for file '%s'.", + cimglist_instance, + str_pixeltype._data,filename?filename:"(FILE*)"); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file. + /** + \param filename Filename to read data from. + **/ + CImgList& load_parrec(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_parrec(): Specified filename is (null).", + cimglist_instance); + + CImg body(1024), filenamepar(1024), filenamerec(1024); + *body = *filenamepar = *filenamerec = 0; + const char *const ext = cimg::split_filename(filename,body); + if (!std::strcmp(ext,"par")) { + std::strncpy(filenamepar,filename,filenamepar._width - 1); + cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data); + } + if (!std::strcmp(ext,"PAR")) { + std::strncpy(filenamepar,filename,filenamepar._width - 1); + cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data); + } + if (!std::strcmp(ext,"rec")) { + std::strncpy(filenamerec,filename,filenamerec._width - 1); + cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data); + } + if (!std::strcmp(ext,"REC")) { + std::strncpy(filenamerec,filename,filenamerec._width - 1); + cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data); + } + std::FILE *file = cimg::fopen(filenamepar,"r"); + + // Parse header file + CImgList st_slices; + CImgList st_global; + CImg line(256); *line = 0; + int err; + do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.')); + do { + unsigned int sn,size_x,size_y,pixsize; + float rs,ri,ss; + err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss); + if (err==7) { + CImg::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices); + unsigned int i; for (i = 0; i::vector(size_x,size_y,sn).move_to(st_global); + else { + CImg &vec = st_global[i]; + if (size_x>vec[0]) vec[0] = size_x; + if (size_y>vec[1]) vec[1] = size_y; + vec[2] = sn; + } + st_slices[st_slices._width - 1][7] = (float)i; + } + } while (err==7); + + // Read data + std::FILE *file2 = cimg::fopen(filenamerec,"rb"); + cimglist_for(st_global,l) { + const CImg& vec = st_global[l]; + CImg(vec[0],vec[1],vec[2]).move_to(*this); + } + + cimglist_for(st_slices,l) { + const CImg& vec = st_slices[l]; + const unsigned int + sn = (unsigned int)vec[0] - 1, + pixsize = (unsigned int)vec[1], + size_x = (unsigned int)vec[2], + size_y = (unsigned int)vec[3], + imn = (unsigned int)vec[7]; + const float ri = vec[4], rs = vec[5], ss = vec[6]; + switch (pixsize) { + case 8 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 16 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + case 32 : { + CImg buf(size_x,size_y); + cimg::fread(buf._data,size_x*size_y,file2); + if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y); + CImg& img = (*this)[imn]; + cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); + } break; + default : + cimg::fclose(file); + cimg::fclose(file2); + throw CImgIOException(_cimglist_instance + "load_parrec(): Unsupported %d-bits pixel type for file '%s'.", + cimglist_instance, + pixsize,filename); + } + } + cimg::fclose(file); + cimg::fclose(file2); + if (!_width) + throw CImgIOException(_cimglist_instance + "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.", + cimglist_instance, + filename); + return *this; + } + + //! Load a list from a PAR/REC (Philips) file \newinstance. + static CImgList get_load_parrec(const char *const filename) { + return CImgList().load_parrec(filename); + } + + //! Load a list from a YUV image sequence file. + /** + \param filename Filename to read data from. + \param size_x Width of the images. + \param size_y Height of the images. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param yuv2rgb Apply YUV to RGB transformation during reading. + **/ + CImgList& load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(0,filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from a YUV image sequence file \newinstance. + static CImgList get_load_yuv(const char *const filename, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(filename,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \overloading. + CImgList& load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return _load_yuv(file,0,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + //! Load a list from an image sequence YUV file \newinstance. + static CImgList get_load_yuv(std::FILE *const file, + const unsigned int size_x, const unsigned int size_y=1, + const unsigned int chroma_subsampling=444, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, const bool yuv2rgb=true) { + return CImgList().load_yuv(file,size_x,size_y,chroma_subsampling, + first_frame,last_frame,step_frame,yuv2rgb); + } + + CImgList& _load_yuv(std::FILE *const file, const char *const filename, + const unsigned int size_x, const unsigned int size_y, + const unsigned int chroma_subsampling, + const unsigned int first_frame, const unsigned int last_frame, + const unsigned int step_frame, const bool yuv2rgb) { + if (!filename && !file) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "load_yuv(): Specified chroma subsampling '%u' is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + nfirst_frame = first_frame YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2); + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); + bool stop_flag = false; + int err; + if (nfirst_frame) { + err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR); + if (err) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "load_yuv(): File '%s' doesn't contain frame number %u.", + cimglist_instance, + filename?filename:"(FILE*)",nfirst_frame); + } + } + unsigned int frame; + for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) { + YUV.get_shared_channel(0).fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile); + if (err!=(int)(YUV._width*YUV._height)) { + stop_flag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + UV.fill(0); + // *TRY* to read the luminance part, do not replace by cimg::fread! + err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile); + if (err!=(int)(UV.size())) { + stop_flag = true; + if (err>0) + cimg::warn(_cimglist_instance + "load_yuv(): File '%s' contains incomplete data or given image dimensions " + "(%u,%u) are incorrect.", + cimglist_instance, + filename?filename:"(FILE*)",size_x,size_y); + } else { + const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1); + ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2); + const unsigned int wd = YUV._width; + switch (chroma_subsampling) { + case 420 : + cimg_forY(UV,y) { + cimg_forX(UV,x) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd1[wd] = U; *(ptrd1)++ = U; + ptrd2[wd] = V; *(ptrd2)++ = V; + ptrd2[wd] = V; *(ptrd2)++ = V; + } + ptrd1+=wd; ptrd2+=wd; + } + break; + case 422 : + cimg_forXY(UV,x,y) { + const ucharT U = *(ptrs1++), V = *(ptrs2++); + *(ptrd1++) = U; *(ptrd1++) = U; + *(ptrd2++) = V; *(ptrd2++) = V; + } + break; + default : + YUV.draw_image(0,0,0,1,UV); + } + if (yuv2rgb) YUV.YCbCrtoRGB(); + insert(YUV); + if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR); + } + } + } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_yuv() : Missing data in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame) + cimg::warn(_cimglist_instance + "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.", + cimglist_instance, + nlast_frame,frame - 1,filename?filename:"(FILE*)"); + + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Load an image from a video file, using OpenCV library. + /** + \param filename Filename, as a C-string. + \param first_frame Index of the first frame to read. + \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U'). + \param step_frame Step value for frame reading. + \note If step_frame==0, the current video stream is forced to be released (without any frames read). + **/ + CImgList& load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { +#ifndef cimg_use_opencv + if (first_frame || last_frame!=~0U || step_frame>1) + throw CImgArgumentException(_cimglist_instance + "load_video() : File '%s', arguments 'first_frame', 'last_frame' " + "and 'step_frame' requires features from the OpenCV library " + "('-Dcimg_use_opencv' must be defined).", + cimglist_instance,filename); + return load_custom_external(filename); +#else + static cv::VideoCapture *captures[32] = { 0 }; + static CImgList filenames(32); + static CImg positions(32,1,1,1,0); + static int last_used_index = -1; + + // Detect if a video capture already exists for the specified filename. + cimg::mutex(9); + int index = -1; + if (filename) { + if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { + index = last_used_index; + } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { + index = l; break; + } + } else index = last_used_index; + cimg::mutex(9,0); + + // Release stream if needed. + if (!step_frame || (index>=0 && positions[index]>first_frame)) { + if (index>=0) { + cimg::mutex(9); + captures[index]->release(); + delete captures[index]; + captures[index] = 0; + positions[index] = 0; + filenames[index].assign(); + if (last_used_index==index) last_used_index = -1; + index = -1; + cimg::mutex(9,0); + } else + if (filename) + cimg::warn(_cimglist_instance + "load_video() : File '%s', no opened video stream associated with filename found.", + cimglist_instance,filename); + else + cimg::warn(_cimglist_instance + "load_video() : No opened video stream found.", + cimglist_instance,filename); + if (!step_frame) return *this; + } + + // Find empty slot for capturing video stream. + if (index<0) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_video(): No already open video reader found. You must specify a " + "non-(null) filename argument for the first call.", + cimglist_instance); + else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } + if (index<0) + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', no video reader slots available. " + "You have to release some of your previously opened videos.", + cimglist_instance,filename); + cimg::mutex(9); + captures[index] = new cv::VideoCapture(filename); + positions[index] = 0; + if (!captures[index]->isOpened()) { + delete captures[index]; + captures[index] = 0; + cimg::mutex(9,0); + cimg::fclose(cimg::fopen(filename,"rb")); // Check file availability + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', unable to detect format of video file.", + cimglist_instance,filename); + } + CImg::string(filename).move_to(filenames[index]); + cimg::mutex(9,0); + } + + cimg::mutex(9); + const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count)); + cimg::mutex(9,0); + assign(); + + // Skip frames if requested. + bool go_on = true; + unsigned int &pos = positions[index]; + while (posgrab()) { cimg::mutex(9,0); go_on = false; break; } + cimg::mutex(9,0); + ++pos; + } + + // Read and convert frames. + const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame); + while (go_on && pos<=_last_frame) { + cv::Mat cvimg; + cimg::mutex(9); + if (captures[index]->read(cvimg)) { CImg::_cvmat2cimg(cvimg).move_to(*this); ++pos; } + else go_on = false; + cimg::mutex(9,0); + if (go_on) + for (unsigned int i = 1; go_on && igrab()) go_on = false; + cimg::mutex(9,0); + } + } + + if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary + cimg::mutex(9); + captures[index]->release(); + delete captures[index]; + captures[index] = 0; + filenames[index].assign(); + positions[index] = 0; + index = -1; + cimg::mutex(9,0); + } + + cimg::mutex(9); + last_used_index = index; + cimg::mutex(9,0); + + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_video(): File '%s', unable to locate frame %u.", + cimglist_instance,filename,first_frame); + return *this; +#endif + } + + //! Load an image from a video file, using OpenCV library \newinstance. + static CImgList get_load_video(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1) { + return CImgList().load_video(filename,first_frame,last_frame,step_frame); + } + + //! Load an image from a video file using the external tool 'custom'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_custom_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_custom_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), filename_tmp2(256); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data); + cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"", + cimg::custom_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp2)._system_strescape().data()); + cimg::system(command, cimg::custom_path()); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + assign(); + unsigned int i = 1; + for (bool stop_flag = false; !stop_flag; ++i) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i); + CImg img; + try { img.load_pnm(filename_tmp2); } + catch (CImgException&) { stop_flag = true; } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + } + cimg::exception_mode(omode); + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_custom_external(): Failed to open file '%s' with external command 'custom'.", + cimglist_instance, + filename); + return *this; + } + + //! Load an image from a video file using the external tool 'custom' \newinstance. + static CImgList get_load_custom_external(const char *const filename) { + return CImgList().load_custom_external(filename); + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gif_external(const char *const filename) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "load_gif_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + if (!_load_gif_external(filename,false)) + if (!_load_gif_external(filename,true)) + try { assign(CImg().load_other(filename)); } catch (CImgException&) { assign(); } + if (is_empty()) + throw CImgIOException(_cimglist_instance + "load_gif_external(): Failed to open file '%s'.", + cimglist_instance,filename); + return *this; + } + + CImgList& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + std::FILE *file = 0; + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data); + else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"", + cimg::graphicsmagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"", + cimg::imagemagick_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::imagemagick_path()); + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + assign(); + + // Try to read a single frame gif. + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data); + CImg img; + try { img.load_png(filename_tmp2); } + catch (CImgException&) { } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + else { // Try to read animated gif + unsigned int i = 0; + for (bool stop_flag = false; !stop_flag; ++i) { + if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i); + else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i); + try { img.load_png(filename_tmp2); } + catch (CImgException&) { stop_flag = true; } + if (img) { img.move_to(*this); std::remove(filename_tmp2); } + } + } + cimg::exception_mode(omode); + return *this; + } + + //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance. + static CImgList get_load_gif_external(const char *const filename) { + return CImgList().load_gif_external(filename); + } + + //! Load a gzipped list, using external tool 'gunzip'. + /** + \param filename Filename to read data from. + **/ + CImgList& load_gzip_external(const char *const filename) { + if (!filename) + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Specified filename is (null).", + cimglist_instance); + cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file = 0; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gunzip_path(), + CImg::string(filename)._system_strescape().data(), + CImg::string(filename_tmp)._system_strescape().data()); + cimg::system(command, cimg::gunzip_path()); + if (!(file=cimg::std_fopen(filename_tmp,"rb"))) { + cimg::fclose(cimg::fopen(filename,"r")); + throw CImgIOException(_cimglist_instance + "load_gzip_external(): Failed to open file '%s'.", + cimglist_instance, + filename); + + } else cimg::fclose(file); + load(filename_tmp); + std::remove(filename_tmp); + return *this; + } + + //! Load a gzipped list, using external tool 'gunzip' \newinstance. + static CImgList get_load_gzip_external(const char *const filename) { + return CImgList().load_gzip_external(filename); + } + + //! Load images from a TIFF file. + /** + \param filename Filename to read data from. + \param first_frame Index of first image frame to read. + \param last_frame Index of last image frame to read. + \param step_frame Step applied between each frame. + \param[out] bits_per_value Number of bits used to store a scalar value in the image file. + \param[out] voxel_size Voxel size, as stored in the filename. + \param[out] description Description, as stored in the filename. + **/ + CImgList& load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + const unsigned int + nfirst_frame = first_frame::get_load_tiff(filename)); +#else +#if cimg_verbosity<3 + TIFFSetWarningHandler(0); + TIFFSetErrorHandler(0); +#endif + TIFF *tif = TIFFOpen(filename,"r"); + if (tif) { + unsigned int nb_images = 0; + do ++nb_images; while (TIFFReadDirectory(tif)); + if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images)) + cimg::warn(_cimglist_instance + "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since " + "file '%s' contains %u image(s).", + cimglist_instance, + nfirst_frame,nlast_frame,nstep_frame,filename,nb_images); + + if (nfirst_frame>=nb_images) return assign(); + if (nlast_frame>=nb_images) nlast_frame = nb_images - 1; + assign(1 + (nlast_frame - nfirst_frame)/nstep_frame); + TIFFSetDirectory(tif,0); + cimglist_for(*this,l) + _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description); + TIFFClose(tif); + } else throw CImgIOException(_cimglist_instance + "load_tiff(): Failed to open file '%s'.", + cimglist_instance, + filename); + return *this; +#endif + } + + //! Load a multi-page TIFF file \newinstance. + static CImgList get_load_tiff(const char *const filename, + const unsigned int first_frame=0, const unsigned int last_frame=~0U, + const unsigned int step_frame=1, unsigned int *const bits_per_value=0, + float *const voxel_size=0, CImg *const description=0) { + return CImgList().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description); + } + + //@} + //---------------------------------- + // + //! \name Data Output + //@{ + //---------------------------------- + + //! Print information about the list on the standard output. + /** + \param title Label set to the information displayed. + \param display_stats Tells if image statistics must be computed and displayed. + **/ + const CImgList& print(const char *const title=0, const bool display_stats=true) const { + unsigned int msiz = 0; + cimglist_for(*this,l) msiz+=_data[l].size(); + msiz*=sizeof(T); + const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U; + CImg _title(64); + if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type()); + std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p", + cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal, + cimg::t_bold,cimg::t_normal,(void*)this, + cimg::t_bold,cimg::t_normal,_width,_allocated_width, + mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)), + mdisp==0?"b":(mdisp==1?"Kio":"Mio"), + cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin()); + if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1)); + else std::fprintf(cimg::output(),".\n"); + + char tmp[16] = { 0 }; + cimglist_for(*this,ll) { + cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll); + std::fprintf(cimg::output()," "); + _data[ll].print(tmp,display_stats); + if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); } + } + std::fflush(cimg::output()); + return *this; + } + + //! Display the current CImgList instance in an existing CImgDisplay window (by reference). + /** + \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + \note This function displays the list images of the current CImgList instance into an existing + CImgDisplay window. + Images of the list are appended in a single temporary image for visualization purposes. + The function returns immediately. + **/ + const CImgList& display(CImgDisplay &disp, const char axis='x', const float align=0) const { + disp.display(*this,axis,align); + return *this; + } + + //! Display the current CImgList instance in a new display window. + /** + \param disp Display window. + \param display_info Tells if image information are displayed on the standard output. + \param axis Alignment axis for images viewing. + \param align Appending alignment. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + \note This function opens a new window with a specific title and displays the list images of the + current CImgList instance into it. + Images of the list are appended in a single temporary image for visualization purposes. + The function returns when a key is pressed or the display window is closed by the user. + **/ + const CImgList& display(CImgDisplay &disp, const bool display_info, + const char axis='x', const float align=0, + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { + bool is_exit = false; + return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); + } + + //! Display the current CImgList instance in a new display window. + /** + \param title Title of the opening display window. + \param display_info Tells if list information must be written on standard output. + \param axis Appending axis. Can be { 'x' | 'y' | 'z' | 'c' }. + \param align Appending alignment. + \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function. + \param exit_on_anykey Exit function when any key is pressed. + **/ + const CImgList& display(const char *const title=0, const bool display_info=true, + const char axis='x', const float align=0, + unsigned int *const XYZ=0, const bool exit_on_anykey=false) const { + CImgDisplay disp; + bool is_exit = false; + return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit); + } + + const CImgList& _display(CImgDisplay &disp, const char *const title, const CImgList *const titles, + const bool display_info, const char axis, const float align, unsigned int *const XYZ, + const bool exit_on_anykey, const unsigned int orig, const bool is_first_call, + bool &is_exit) const { + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "display(): Empty instance.", + cimglist_instance); + if (!disp) { + if (axis=='x') { + unsigned int sum_width = 0, max_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + sum_width+=w; + if (h>max_height) max_height = h; + } + disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1); + } else { + unsigned int max_width = 0, sum_height = 0; + cimglist_for(*this,l) { + const CImg &img = _data[l]; + const unsigned int + w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false), + h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true); + if (w>max_width) max_width = w; + sum_height+=h; + } + disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1); + } + if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width); + } else if (title) disp.set_title("%s",title); + else if (titles) disp.set_title("%s",titles->__display()._data); + const CImg dtitle = CImg::string(disp.title()); + if (display_info) print(disp.title()); + disp.show().flush(); + + if (_width==1) { + const unsigned int dw = disp._width, dh = disp._height; + if (!is_first_call) + disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false); + disp.set_title("%s (%ux%ux%ux%u)", + dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum); + _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call); + if (disp.key()) is_exit = true; + disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data()); + } else { + bool disp_resize = !is_first_call; + while (!disp.is_closed() && !is_exit) { + const CImg s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true); + disp_resize = true; + if (s[0]<0 && !disp.wheel()) { // No selections done + if (disp.button()&2) { disp.flush(); break; } + is_exit = true; + } else if (disp.wheel()) { // Zoom in/out + const int wheel = disp.wheel(); + disp.set_wheel(); + if (!is_first_call && wheel<0) break; + if (wheel>0 && _width>=4) { + const unsigned int + delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)), + ind0 = (unsigned int)std::max(0,s[0] - (int)delta), + ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta); + if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) { + const CImgList sublist = get_shared_images(ind0,ind1); + CImgList t_sublist; + if (titles) t_sublist = titles->get_shared_images(ind0,ind1); + sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, + orig + ind0,false,is_exit); + } + } + } else if (s[0]!=0 || s[1]!=width() - 1) { + const CImgList sublist = get_shared_images(s[0],s[1]); + CImgList t_sublist; + if (titles) t_sublist = titles->get_shared_images(s[0],s[1]); + sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey, + orig + s[0],false,is_exit); + } + disp.set_title("%s",dtitle.data()); + } + } + return *this; + } + + // [internal] Return string to describe display title. + CImg __display() const { + CImg res, str; + cimglist_for(*this,l) { + CImg::string((char*)_data[l]).move_to(str); + if (l!=width() - 1) { + str.resize(str._width + 1,1,1,1,0); + str[str._width - 2] = ','; + str[str._width - 1] = ' '; + } + res.append(str,'x'); + } + if (!res) return CImg(1,1,1,1,0).move_to(res); + cimg::strellipsize(res,128,false); + if (_width>1) { + const unsigned int l = (unsigned int)std::strlen(res); + if (res._width<=l + 16) res.resize(l + 16,1,1,1,0); + cimg_snprintf(res._data + l,16," (#%u)",_width); + } + return res; + } + + //! Save list into a file. + /** + \param filename Filename to write data to. + \param number When positive, represents an index added to the filename. Otherwise, no number is added. + \param digits Number of digits used for adding the number to the filename. + **/ + const CImgList& save(const char *const filename, const int number=-1, const unsigned int digits=6) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save(): Specified filename is (null).", + cimglist_instance); + // Do not test for empty instances, since .cimg format is able to manage empty instances. + const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.'); + const char *const ext = cimg::split_filename(filename); + CImg nfilename(1024); + const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename): + filename; + +#ifdef cimglist_save_plugin + cimglist_save_plugin(fn); +#endif +#ifdef cimglist_save_plugin1 + cimglist_save_plugin1(fn); +#endif +#ifdef cimglist_save_plugin2 + cimglist_save_plugin2(fn); +#endif +#ifdef cimglist_save_plugin3 + cimglist_save_plugin3(fn); +#endif +#ifdef cimglist_save_plugin4 + cimglist_save_plugin4(fn); +#endif +#ifdef cimglist_save_plugin5 + cimglist_save_plugin5(fn); +#endif +#ifdef cimglist_save_plugin6 + cimglist_save_plugin6(fn); +#endif +#ifdef cimglist_save_plugin7 + cimglist_save_plugin7(fn); +#endif +#ifdef cimglist_save_plugin8 + cimglist_save_plugin8(fn); +#endif + if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true); + else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false); + else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true); + else if (!cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return save_video(fn); +#ifdef cimg_use_tiff + else if (!cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn); +#endif + else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn); + else { + if (_width==1) _data[0].save(fn,-1); + else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); } + } + return *this; + } + + //! Tell if an image list can be saved as one single file. + /** + \param filename Filename, as a C-string. + \return \c true if the file format supports multiple images, \c false otherwise. + **/ + static bool is_saveable(const char *const filename) { + const char *const ext = cimg::split_filename(filename); + if (!cimg::strcasecmp(ext,"cimgz") || +#ifdef cimg_use_tiff + !cimg::strcasecmp(ext,"tif") || + !cimg::strcasecmp(ext,"tiff") || +#endif + !cimg::strcasecmp(ext,"yuv") || + !cimg::strcasecmp(ext,"avi") || + !cimg::strcasecmp(ext,"mov") || + !cimg::strcasecmp(ext,"asf") || + !cimg::strcasecmp(ext,"divx") || + !cimg::strcasecmp(ext,"flv") || + !cimg::strcasecmp(ext,"mpg") || + !cimg::strcasecmp(ext,"m1v") || + !cimg::strcasecmp(ext,"m2v") || + !cimg::strcasecmp(ext,"m4v") || + !cimg::strcasecmp(ext,"mjp") || + !cimg::strcasecmp(ext,"mp4") || + !cimg::strcasecmp(ext,"mkv") || + !cimg::strcasecmp(ext,"mpe") || + !cimg::strcasecmp(ext,"movie") || + !cimg::strcasecmp(ext,"ogm") || + !cimg::strcasecmp(ext,"ogg") || + !cimg::strcasecmp(ext,"ogv") || + !cimg::strcasecmp(ext,"qt") || + !cimg::strcasecmp(ext,"rm") || + !cimg::strcasecmp(ext,"vob") || + !cimg::strcasecmp(ext,"webm") || + !cimg::strcasecmp(ext,"wmv") || + !cimg::strcasecmp(ext,"xvid") || + !cimg::strcasecmp(ext,"mpeg")) return true; + return false; + } + + //! Save image sequence as a GIF animated file. + /** + \param filename Filename to write data to. + \param fps Number of desired frames per second. + \param nb_loops Number of loops (\c 0 for infinite looping). + **/ + const CImgList& save_gif_external(const char *const filename, const float fps=25, + const unsigned int nb_loops=0) { + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; + +#ifdef cimg_use_png +#define _cimg_save_gif_extension "png" +#else +#define _cimg_save_gif_extension "ppm" +#endif + + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1); + CImg::string(filename_tmp2).move_to(filenames); + if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filename_tmp2); + else _data[l].save(filename_tmp2); + } + cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u", + cimg::imagemagick_path(),(unsigned int)std::max(0.f,cimg::round(100/fps)),nb_loops); + CImg::string(command).move_to(filenames,0); + cimg_snprintf(command,command._width,"\"%s\"", + CImg::string(filename)._system_strescape().data()); + CImg::string(command).move_to(filenames); + CImg _command = filenames>'x'; + cimg_for(_command,p,char) if (!*p) *p = ' '; + _command.back() = 0; + + cimg::system(_command, cimg::imagemagick_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]); + return *this; + } + + //! Save list as a YUV image sequence file. + /** + \param filename Filename to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(const char *const filename=0, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(0,filename,chroma_subsampling,is_rgb); + } + + //! Save image sequence into a YUV file. + /** + \param file File to write data to. + \param chroma_subsampling Type of chroma subsampling. Can be { 420 | 422 | 444 }. + \param is_rgb Tells if the RGB to YUV conversion must be done for saving. + **/ + const CImgList& save_yuv(std::FILE *const file, + const unsigned int chroma_subsampling=444, + const bool is_rgb=true) const { + return _save_yuv(file,0,chroma_subsampling,is_rgb); + } + + const CImgList& _save_yuv(std::FILE *const file, const char *const filename, + const unsigned int chroma_subsampling, + const bool is_rgb) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified filename is (null).", + cimglist_instance); + if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444) + throw CImgArgumentException(_cimglist_instance + "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.", + cimglist_instance, + chroma_subsampling,filename?filename:"(FILE*)"); + if (is_empty()) { cimg::fempty(file,filename); return *this; } + const unsigned int + cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1, + cfy = chroma_subsampling==420?2:1, + w0 = (*this)[0]._width, h0 = (*this)[0]._height, + width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy); + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + cimglist_for(*this,l) { + const CImg &frame = (*this)[l]; + cimg_forZ(frame,z) { + CImg YUV; + if (sizeof(T)==1 && !is_rgb && + frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3) + YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true); + else { + YUV = frame.get_slice(z); + if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0); + if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0); + if (is_rgb) YUV.RGBtoYCbCr(); + } + if (chroma_subsampling==444) + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile); + else { + cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile); + CImg UV = YUV.get_channels(1,2); + UV.resize(UV._width/cfx,UV._height/cfy,1,2,2); + cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile); + } + } + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param filename Filename to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(const char *const filename, const bool is_compressed=false) const { + return _save_cimg(0,filename,is_compressed); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const { + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, " + "saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); +#endif + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const bool is_bool = ptype==cimg::type::string(); + if (!is_bool && std::strstr(ptype,"unsigned")==ptype) + std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); + else + std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype); + + cimglist_for(*this,l) { + const CImg& img = _data[l]; + std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + Bytef *cbuf = 0; + uLongf csiz = 0; + + if (is_bool) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = ref._bool2uchar(siz,false); + csiz = siz + siz/100 + 16; + cbuf = new Bytef[csiz]; + failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz); + if (!failed_to_compress) { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + } + delete[] buf; + } else { // Non-boolean data + const ulongT siz = sizeof(T)*ref.size(); + csiz = siz + siz/100 + 16; + cbuf = new Bytef[csiz]; + failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz); + if (!failed_to_compress) { + std::fprintf(nfile," #%lu\n",csiz); + cimg::fwrite(cbuf,csiz,nfile); + } + } + if (failed_to_compress) + cimg::warn(_cimglist_instance + "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.", + cimglist_instance, + filename?filename:"(FILE*)"); + delete[] cbuf; +#endif + } + if (failed_to_compress) { // Write non-compressed + std::fputc('\n',nfile); + if (is_bool) { // Boolean data (bitwise) + ulongT siz; + const unsigned char *const buf = ref._bool2uchar(siz,false); + cimg::fwrite(buf,siz,nfile); + delete[] buf; + } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data + } + } else std::fputc('\n',nfile); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Save list into a .cimg file. + /** + \param file File to write data to. + \param is_compressed Tells if data compression must be enabled. + **/ + const CImgList& save_cimg(std::FILE *file, const bool is_compressed=false) const { + return _save_cimg(file,0,is_compressed); + } + + const CImgList& _save_cimg(std::FILE *const file, const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { +#define _cimg_save_cimg_case(Ts,Tss) \ + if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l0) { \ + if (l=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \ + else { \ + const CImg& img = (*this)[l - n0]; \ + const T *ptrs = img._data; \ + const unsigned int \ + x1 = x0 + img._width - 1, \ + y1 = y0 + img._height - 1, \ + z1 = z0 + img._depth - 1, \ + c1 = c0 + img._spectrum - 1, \ + nx1 = x1>=W?W - 1:x1, \ + ny1 = y1>=H?H - 1:y1, \ + nz1 = z1>=D?D - 1:z1, \ + nc1 = c1>=C?C - 1:c1; \ + CImg raw(1 + nx1 - x0); \ + const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \ + if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \ + for (unsigned int v = 1 + nc1 - c0; v; --v) { \ + const unsigned int skipzb = z0*W*H*sizeof(Tss); \ + if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \ + for (unsigned int z = 1 + nz1 - z0; z; --z) { \ + const unsigned int skipyb = y0*W*sizeof(Tss); \ + if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \ + for (unsigned int y = 1 + ny1 - y0; y; --y) { \ + const unsigned int skipxb = x0*sizeof(Tss); \ + if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \ + raw.assign(ptrs, raw._width); \ + ptrs+=img._width; \ + if (endian) cimg::invert_endianness(raw._data,raw._width); \ + cimg::fwrite(raw._data,raw._width,nfile); \ + const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \ + if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \ + } \ + const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \ + if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \ + } \ + const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \ + if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \ + } \ + const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \ + if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \ + } \ + } \ + } \ + saved = true; \ + } + + if (!file && !filename) + throw CImgArgumentException(_cimglist_instance + "save_cimg(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_cimg(): Empty instance, for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + + std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); + bool saved = false, endian = cimg::endianness(); + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N, W, H, D, C; + int i, err; + j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0; + err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z64_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data); + if (err<2) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "save_cimg(): CImg header not found in file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)"); + } + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + const unsigned int lmax = std::min(N,n0 + _width); + _cimg_save_cimg_case("bool",bool); + _cimg_save_cimg_case("unsigned_char",unsigned char); + _cimg_save_cimg_case("uchar",unsigned char); + _cimg_save_cimg_case("char",char); + _cimg_save_cimg_case("unsigned_short",unsigned short); + _cimg_save_cimg_case("ushort",unsigned short); + _cimg_save_cimg_case("short",short); + _cimg_save_cimg_case("unsigned_int",unsigned int); + _cimg_save_cimg_case("uint",unsigned int); + _cimg_save_cimg_case("int",int); + _cimg_save_cimg_case("unsigned_int64",uint64T); + _cimg_save_cimg_case("uint64",uint64T); + _cimg_save_cimg_case("int64",int64T); + _cimg_save_cimg_case("float",float); + _cimg_save_cimg_case("double",double); + if (!saved) { + if (!file) cimg::fclose(nfile); + throw CImgIOException(_cimglist_instance + "save_cimg(): Unsupported data type '%s' for file '%s'.", + cimglist_instance, + filename?filename:"(FILE*)",str_pixeltype._data); + } + if (!file) cimg::fclose(nfile); + return *this; + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param filename Filename to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(const char *const filename, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(0,filename,n0,x0,y0,z0,c0); + } + + //! Insert the image instance into into an existing .cimg file, at specified coordinates. + /** + \param file File to write data to. + \param n0 Starting index of images to write. + \param x0 Starting X-coordinates of image regions to write. + \param y0 Starting Y-coordinates of image regions to write. + \param z0 Starting Z-coordinates of image regions to write. + \param c0 Starting C-coordinates of image regions to write. + **/ + const CImgList& save_cimg(std::FILE *const file, + const unsigned int n0, + const unsigned int x0, const unsigned int y0, + const unsigned int z0, const unsigned int c0) const { + return _save_cimg(file,0,n0,x0,y0,z0,c0); + } + + static void _save_empty_cimg(std::FILE *const file, const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy, + const unsigned int dz, const unsigned int dc) { + std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); + const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T); + std::fprintf(nfile,"%u %s\n",nb,pixel_type()); + for (unsigned int i=nb; i; --i) { + std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc); + for (ulongT off = siz; off; --off) std::fputc(0,nfile); + } + if (!file) cimg::fclose(nfile); + } + + //! Save empty (non-compressed) .cimg file with specified dimensions. + /** + \param filename Filename to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(const char *const filename, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc); + } + + //! Save empty .cimg file with specified dimensions. + /** + \param file File to write data to. + \param nb Number of images to write. + \param dx Width of images in the written file. + \param dy Height of images in the written file. + \param dz Depth of images in the written file. + \param dc Spectrum of images in the written file. + **/ + static void save_empty_cimg(std::FILE *const file, + const unsigned int nb, + const unsigned int dx, const unsigned int dy=1, + const unsigned int dz=1, const unsigned int dc=1) { + return _save_empty_cimg(file,0,nb,dx,dy,dz,dc); + } + + //! Save list as a TIFF file. + /** + \param filename Filename to write data to. + \param compression_type Compression mode used to write data. + \param voxel_size Voxel size, to be stored in the filename. + \param description Description, to be stored in the filename. + \param use_bigtiff Allow to save big tiff files (>4Gb). + **/ + const CImgList& save_tiff(const char *const filename, const unsigned int compression_type=0, + const float *const voxel_size=0, const char *const description=0, + const bool use_bigtiff=true) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_tiff(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + +#ifndef cimg_use_tiff + if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff); + else cimglist_for(*this,l) { + CImg nfilename(1024); + cimg::number_filename(filename,l,6,nfilename); + _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff); + } +#else + ulongT siz = 0; + cimglist_for(*this,l) siz+=_data[l].size(); + const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images + TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4"); + if (tif) { + for (unsigned int dir = 0, l = 0; l<_width; ++l) { + const CImg& img = (*this)[l]; + cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description); + } + TIFFClose(tif); + } else + throw CImgIOException(_cimglist_instance + "save_tiff(): Failed to open stream for file '%s'.", + cimglist_instance, + filename); +#endif + return *this; + } + + //! Save list as a gzipped file, using external tool 'gzip'. + /** + \param filename Filename to write data to. + **/ + const CImgList& save_gzip_external(const char *const filename) const { + if (!filename) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Specified filename is (null).", + cimglist_instance); + CImg command(1024), filename_tmp(256), body(256); + const char + *ext = cimg::split_filename(filename,body), + *ext2 = cimg::split_filename(body,0); + std::FILE *file; + do { + if (!cimg::strcasecmp(ext,"gz")) { + if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } else { + if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext); + else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + } + if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file); + } while (file); + + if (is_saveable(body)) { + save(filename_tmp); + cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"", + cimg::gzip_path(), + CImg::string(filename_tmp)._system_strescape().data(), + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::gzip_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.", + cimglist_instance, + filename); + else cimg::fclose(file); + std::remove(filename_tmp); + } else { + CImg nfilename(1024); + cimglist_for(*this,l) { + cimg::number_filename(body,l,6,nfilename); + if (*ext) cimg_sprintf(nfilename._data + std::strlen(nfilename),".%s",ext); + _data[l].save_gzip_external(nfilename); + } + } + return *this; + } + + //! Save image sequence (using the OpenCV library when available). + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs). + \param keep_open Tells if the video writer associated to the specified filename + must be kept open or not (to allow frames to be added in the same file afterwards). + **/ + const CImgList& save_video(const char *const filename, const unsigned int fps=25, + const char *codec=0, const bool keep_open=false) const { +#ifndef cimg_use_opencv + cimg::unused(codec,keep_open); + return save_custom_external(filename,fps); +#else + try { + static cv::VideoWriter *writers[32] = { 0 }; + static CImgList filenames(32); + static CImg sizes(32,2,1,1,0); + static int last_used_index = -1; + + // Detect if a video writer already exists for the specified filename. + cimg::mutex(9); + int index = -1; + if (filename) { + if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) { + index = last_used_index; + } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) { + index = l; break; + } + } else index = last_used_index; + cimg::mutex(9,0); + + // Find empty slot for capturing video stream. + if (index<0) { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_video(): No already open video writer found. You must specify a " + "non-(null) filename argument for the first call.", + cimglist_instance); + else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); } + if (index<0) + throw CImgIOException(_cimglist_instance + "save_video(): File '%s', no video writer slots available. " + "You have to release some of your previously opened videos.", + cimglist_instance,filename); + if (is_empty()) + throw CImgInstanceException(_cimglist_instance + "save_video(): Instance list is empty.", + cimglist_instance); + const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0; + if (!W || !H) + throw CImgInstanceException(_cimglist_instance + "save_video(): Frame [0] is an empty image.", + cimglist_instance); + const char + *const _codec = codec && *codec?codec:"h264", + codec0 = cimg::uppercase(_codec[0]), + codec1 = _codec[0]?cimg::uppercase(_codec[1]):0, + codec2 = _codec[1]?cimg::uppercase(_codec[2]):0, + codec3 = _codec[2]?cimg::uppercase(_codec[3]):0; + cimg::mutex(9); + writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H)); + if (!writers[index]->isOpened()) { + delete writers[index]; + writers[index] = 0; + cimg::mutex(9,0); + throw CImgIOException(_cimglist_instance + "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.", + cimglist_instance,filename, + codec0,codec1,codec2,codec3); + } + CImg::string(filename).move_to(filenames[index]); + sizes(index,0) = W; + sizes(index,1) = H; + cimg::mutex(9,0); + } + + if (!is_empty()) { + const unsigned int W = sizes(index,0), H = sizes(index,1); + cimg::mutex(9); + cimglist_for(*this,l) { + CImg &src = _data[l]; + if (src.is_empty()) + cimg::warn(_cimglist_instance + "save_video(): Skip empty frame %d for file '%s'.", + cimglist_instance,l,filename); + if (src._depth>1 || src._spectrum>3) + cimg::warn(_cimglist_instance + "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). " + "Some image data may be ignored when writing frame into video file '%s'.", + cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename); + if (src._width==W && src._height==H && src._spectrum==3) + writers[index]->write(CImg(src)._cimg2cvmat()); + else { + CImg _src(src,false); + _src.channels(0,std::min(_src._spectrum - 1,2U)).resize(W,H); + _src.resize(W,H,1,3,_src._spectrum==1); + writers[index]->write(_src._cimg2cvmat()); + } + } + cimg::mutex(9,0); + } + + cimg::mutex(9); + if (!keep_open) { + delete writers[index]; + writers[index] = 0; + filenames[index].assign(); + sizes(index,0) = sizes(index,1) = 0; + last_used_index = -1; + } else last_used_index = index; + cimg::mutex(9,0); + } catch (CImgIOException &e) { + if (!keep_open) return save_custom_external(filename,fps); + throw e; + } + return *this; +#endif + } + + //! Save image sequence, using the external tool 'custom'. + /** + \param filename Filename to write data to. + \param fps Number of frames per second. + \param codec Type of compression. + \param bitrate Output bitrate + **/ + const CImgList& save_custom_external(const char *const filename, const unsigned int fps=25, + const char *const codec=0, const unsigned int bitrate=2048) const { + if (!filename) + throw CImgArgumentException(_cimglist_instance + "save_custom_external(): Specified filename is (null).", + cimglist_instance); + if (is_empty()) { cimg::fempty(0,filename); return *this; } + + const char + *const ext = cimg::split_filename(filename), + *const _codec = codec?codec: + !cimg::strcasecmp(ext,"flv")?"flv": + !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video"; + + CImg command(1024), filename_tmp(256), filename_tmp2(256); + CImgList filenames; + std::FILE *file = 0; + cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0])) + throw CImgInstanceException(_cimglist_instance + "save_custom_external(): Invalid instance dimensions for file '%s'.", + cimglist_instance, + filename); + do { + cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand()); + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data); + if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file); + } while (file); + cimglist_for(*this,l) { + cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,l + 1); + CImg::string(filename_tmp2).move_to(filenames); + CImg tmp = _data[l].get_shared(); + if (tmp._width%2 || tmp._height%2) // Force output to have an even number of columns and rows + tmp.assign(tmp.get_resize(tmp._width + (tmp._width%2),tmp._height + (tmp._height%2),1,-100,0),false); + if (tmp._depth>1 || tmp._spectrum!=3) // Force output to be one slice, in color + tmp.assign(tmp.get_resize(-100,-100,1,3),false); + tmp.save_pnm(filename_tmp2); + } + cimg_snprintf(command,command._width, + "\"%s\" -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"", + cimg::custom_path(), + CImg::string(filename_tmp)._system_strescape().data(), + _codec,bitrate,fps, + CImg::string(filename)._system_strescape().data()); + cimg::system(command, cimg::custom_path()); + file = cimg::std_fopen(filename,"rb"); + if (!file) + throw CImgIOException(_cimglist_instance + "save_custom_external(): Failed to save file '%s' with external command 'custom'.", + cimglist_instance, + filename); + else cimg::fclose(file); + cimglist_for(*this,l) std::remove(filenames[l]); + return *this; + } + + //! Serialize a CImgList instance into a raw CImg buffer. + /** + \param is_compressed tells if zlib compression must be used for serialization + (this requires 'cimg_use_zlib' been enabled). + **/ + CImg get_serialize(const bool is_compressed=false) const { +#ifndef cimg_use_zlib + if (is_compressed) + cimg::warn(_cimglist_instance + "get_serialize(): Unable to compress data unless zlib is enabled, " + "storing them uncompressed.", + cimglist_instance); +#endif + CImgList stream; + CImg tmpstr(128); + const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little"; + if (std::strstr(ptype,"unsigned")==ptype) + cimg_snprintf(tmpstr,tmpstr._width,"%u unsigned_%s %s_endian\n",_width,ptype + 9,etype); + else + cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype); + CImg::string(tmpstr,false).move_to(stream); + cimglist_for(*this,l) { + const CImg& img = _data[l]; + cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum); + CImg::string(tmpstr,false).move_to(stream); + if (img._data) { + CImg tmp; + if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); } + const CImg& ref = cimg::endianness()?tmp:img; + bool failed_to_compress = true; + if (is_compressed) { +#ifdef cimg_use_zlib + const ulongT siz = sizeof(T)*ref.size(); + uLongf csiz = (ulongT)compressBound(siz); + Bytef *const cbuf = new Bytef[csiz]; + if (compress(cbuf,&csiz,(Bytef*)ref._data,siz)) + cimg::warn(_cimglist_instance + "get_serialize(): Failed to save compressed data, saving them uncompressed.", + cimglist_instance); + else { + cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz); + CImg::string(tmpstr,false).move_to(stream); + CImg(cbuf,csiz).move_to(stream); + delete[] cbuf; + failed_to_compress = false; + } +#endif + } + if (failed_to_compress) { // Write in a non-compressed way + CImg::string("\n",false).move_to(stream); + stream.insert(1); + stream.back().assign((unsigned char*)ref._data,ref.size()*sizeof(T),1,1,1,true); + } + } else CImg::string("\n",false).move_to(stream); + } + cimglist_apply(stream,unroll)('y'); + return stream>'y'; + } + + //! Unserialize a CImg serialized buffer into a CImgList list. + template + static CImgList get_unserialize(const CImg& buffer) { +#ifdef cimg_use_zlib +#define _cimgz_unserialize_case(Tss) { \ + Bytef *cbuf = 0; \ + if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type::string()) { \ + cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \ + for (ulongT k = 0; k::get_unserialize(): Unable to unserialize compressed data " \ + "unless zlib is enabled.", \ + pixel_type()); +#endif + +#define _cimg_unserialize_case(Ts,Tss) \ + if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ + for (unsigned int l = 0; l::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \ + "image #%u in serialized buffer.", \ + pixel_type(),W,H,D,C,l); \ + if (W*H*D*C>0) { \ + CImg raw; \ + CImg &img = res._data[l]; \ + if (err==5) _cimgz_unserialize_case(Tss) \ + else { \ + raw.assign(W,H,D,C); \ + CImg _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \ + if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \ + else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \ + } \ + if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \ + raw.move_to(img); \ + } \ + } \ + loaded = true; \ + } + + if (buffer.is_empty()) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).", + pixel_type()); + CImgList res; + const t *stream = buffer._data, *const estream = buffer._data + buffer.size(); + bool loaded = false, endian = cimg::endianness(), is_bytef = false; + CImg tmp(256), str_pixeltype(256), str_endian(256); + *tmp = *str_pixeltype = *str_endian = 0; + unsigned int j, N = 0, W, H, D, C; + uint64T csiz; + int i, err; + cimg::unused(is_bytef); + do { + j = 0; while ((i=(int)*stream)!='\n' && stream::get_unserialize(): CImg header not found in serialized buffer.", + pixel_type()); + if (!cimg::strncasecmp("little",str_endian,6)) endian = false; + else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; + res.assign(N); + _cimg_unserialize_case("bool",bool); + _cimg_unserialize_case("unsigned_char",unsigned char); + _cimg_unserialize_case("uchar",unsigned char); + _cimg_unserialize_case("char",char); + _cimg_unserialize_case("unsigned_short",unsigned short); + _cimg_unserialize_case("ushort",unsigned short); + _cimg_unserialize_case("short",short); + _cimg_unserialize_case("unsigned_int",unsigned int); + _cimg_unserialize_case("uint",unsigned int); + _cimg_unserialize_case("int",int); + _cimg_unserialize_case("unsigned_int64",uint64T); + _cimg_unserialize_case("uint64",uint64T); + _cimg_unserialize_case("int64",int64T); + _cimg_unserialize_case("float",float); + _cimg_unserialize_case("double",double); + if (!loaded) + throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined " + "in serialized buffer.", + pixel_type(),str_pixeltype._data); + return res; + } + + //@} + //---------------------------------- + // + //! \name Others + //@{ + //---------------------------------- + + //! Return a CImg pre-defined font with requested height. + /** + \param font_height Height of the desired font (exact match for 13,23,53,103). + \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width. + **/ + static const CImgList& font(const unsigned int requested_height, const bool is_variable_width=true) { + if (!requested_height) return CImgList::const_empty(); + cimg::mutex(11); + static const unsigned char font_resizemap[] = { + 0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30, + 32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52, + 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165, + 166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179, + 180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192, + 193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205, + 206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218, + 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231, + 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, + 244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 }; + static const char *const *font_data[] = { + cimg::data_font_small, + cimg::data_font_normal, + cimg::data_font_large, + cimg::data_font_huge }; + static const unsigned int + font_width[] = { 10,26,52,104 }, + font_height[] = { 13,32,64,128 }, + font_M[] = { 86,91,91,47 }, + font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*), + sizeof(cimg::data_font_normal)/sizeof(char*), + sizeof(cimg::data_font_large)/sizeof(char*), + sizeof(cimg::data_font_huge)/sizeof(char*) }; + static const unsigned char font_is_binary[] = { 1,0,0,1 }; + static CImg font_base[4]; + + unsigned int ind = + requested_height<=font_height[0]?0U: + requested_height<=font_height[1]?1U: + requested_height<=font_height[2]?2U:3U; + + // Decompress nearest base font data if needed. + CImg &basef = font_base[ind]; + if (!basef) { + basef.assign(256*font_width[ind],font_height[ind]); + + unsigned char *ptrd = basef; + const unsigned char *const ptrde = basef.end(); + + // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb). + CImg dataf; + for (unsigned int k = 0; k::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x'); + + // Uncompress font data (decode RLE). + const unsigned int M = font_M[ind]; + if (font_is_binary[ind]) + for (const char *ptrs = dataf; *ptrs; ++ptrs) { + const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n; + if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } + else { std::memset(ptrd,v,ptrde - ptrd); break; } + } + else + for (const char *ptrs = dataf; *ptrs; ++ptrs) { + int n = (int)*ptrs - M - 32, v = 0; + if (n>=0) { v = 85*n; n = 1; } + else { + n = -n; + v = (int)*(++ptrs) - M - 32; + if (v<0) { v = 0; --ptrs; } else v*=85; + } + if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; } + else { std::memset(ptrd,v,ptrde - ptrd); break; } + } + } + + // Find optimal font cache location to return. + static CImgList fonts[16]; + static bool is_variable_widths[16] = { 0 }; + ind = ~0U; + for (int i = 0; i<16; ++i) + if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) { + ind = (unsigned int)i; break; // Found empty slot or cached font + } + if (ind==~0U) { // No empty slots nor existing font in cache + fonts->assign(); + std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList)); + std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool)); + std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList)); // Free a slot in cache for new font + } + CImgList &font = fonts[ind]; + + // Render requested font. + if (!font) { + is_variable_widths[ind] = is_variable_width; + basef.get_split('x',256).move_to(font); + +// cimg::tic(); + + if (requested_height!=font[0]._height) + cimglist_for(font,l) { + font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5); + cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr]; + } + +// cimg::toc(); +// std::exit(0); + + if (is_variable_width) { // Crop font + cimglist_for(font,l) { + CImg& letter = font[l]; + int xmin = letter.width(), xmax = 0; + cimg_forX(letter,x) { // Find xmin + cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; } + if (xmin!=letter.width()) break; + } + cimg_rofX(letter,x) { // Find xmax + cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; } + if (xmax) break; + } + if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1); + } + font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0); + if (' ' + 256& FFT(const char axis, const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + CImg::FFT(_data[0],_data[1],axis,invert); + return *this; + } + + //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance. + CImgList get_FFT(const char axis, const bool invert=false) const { + return CImgList(*this,false).FFT(axis,invert); + } + + //! Compute n-D Fast Fourier Transform. + /** + \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed. + **/ + CImgList& FFT(const bool invert=false) { + if (is_empty()) return *this; + if (_width==1) insert(1); + if (_width>2) + cimg::warn(_cimglist_instance + "FFT(): Instance has more than 2 images", + cimglist_instance); + + CImg::FFT(_data[0],_data[1],invert); + return *this; + } + + //! Compute n-D Fast Fourier Transform \newinstance. + CImgList get_FFT(const bool invert=false) const { + return CImgList(*this,false).FFT(invert); + } + + //! Reverse primitives orientations of a 3D object. + /** + **/ + CImgList& reverse_object3d() { + cimglist_for(*this,l) { + CImg& p = _data[l]; + switch (p.size()) { + case 2 : case 3: cimg::swap(p[0],p[1]); break; + case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break; + case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break; + case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break; + case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break; + } + } + return *this; + } + + //! Reverse primitives orientations of a 3D object \newinstance. + CImgList get_reverse_object3d() const { + return (+*this).reverse_object3d(); + } + + //@} + }; // struct CImgList { ... + + // Completion of previously declared functions + //-------------------------------------------- + namespace cimg { + + // Functions to return standard streams 'stdin', 'stdout' and 'stderr'. + // (throw a CImgIOException when macro 'cimg_use_r' is defined). + inline FILE* _stdin(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stdin; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + inline FILE* _stdout(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stdout; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + inline FILE* _stderr(const bool throw_exception) { +#ifndef cimg_use_r + cimg::unused(throw_exception); + return stderr; +#else + if (throw_exception) { + cimg::exception_mode(0); + throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode " + "('cimg_use_r' is defined)."); + } + return 0; +#endif + } + + // Open a file (similar to std:: fopen(), but with wide character support on Windows). + inline std::FILE *std_fopen(const char *const path, const char *const mode) { + std::FILE *const res = std::fopen(path,mode); + if (res) return res; +#if cimg_OS==2 + // Try alternative method, with wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (err) { + CImg wpath((unsigned int)err); + err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err); + if (err) { // Convert 'mode' to a wide-character string + err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0); + if (err) { + CImg wmode((unsigned int)err); + if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err)) + return _wfopen(wpath,wmode); + } + } + } +#endif + return 0; + } + + //! Search path of an executable (Windows only). +#if cimg_OS==2 + inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) { + char *ptr = 0; + DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr); + return err!=0; + } +#endif + + //! Get the file or directory attributes with support for UTF-8 paths (Windows only). +#if cimg_OS==2 + inline DWORD win_getfileattributes(const char *const path) { + DWORD res = GetFileAttributesA(path); + if (res==INVALID_FILE_ATTRIBUTES) { + // Try alternative method, with wide-character string. + int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0); + if (err) { + CImg wpath((unsigned int)err); + if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath); + } + } + return res; + } +#endif + + //! Get/set path to the Program Files/ directory (Windows only). + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the program files. + **/ +#if cimg_OS==2 + inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(MAX_PATH); + *s_path = 0; + // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). +#if !defined(__INTEL_COMPILER) + if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) { + const char *const pfPath = std::getenv("PROGRAMFILES"); + if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1); + else std::strcpy(s_path,"C:\\PROGRA~1"); + } +#else + std::strcpy(s_path,"C:\\PROGRA~1"); +#endif + } + cimg::mutex(7,0); + return s_path; + } +#endif + + //! Get/set path to the \c curl binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c curl binary. + **/ + inline const char *curl_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\curl.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"curl.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./curl"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"curl"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c dcraw binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c dcraw binary. + **/ + inline const char *dcraw_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\dcraw.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"dcraw.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./dcraw"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"dcraw"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the custom's \c custom binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c custom binary. + **/ + inline const char *custom_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("custom.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\custom.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"custom.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./custom"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"custom"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the GraphicsMagick's \c gm binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gm binary. + **/ + inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\gm.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gm.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gm"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gm"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c gunzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gunzip binary. + **/ + inline const char *gunzip_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\gunzip.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gunzip.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gunzip"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gunzip"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c gzip binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c gzip binary. + **/ + inline const char *gzip_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\gzip.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gzip.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./gzip"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"gzip"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the ImageMagick's \c convert binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c convert binary. + **/ + inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + for (int l = 0; l<2 && !path_found; ++l) { + const char *const s_exe = l?"convert":"magick"; + cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=10 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 9; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + for (int k = 32; k>=0 && !path_found; --k) { + cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe); + } +#else + std::strcpy(s_path,"./magick"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + if (!path_found) { + std::strcpy(s_path,"./convert"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"convert"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the Medcon's \c medcon binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c medcon binary. + **/ + inline const char* medcon_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true; + const char *const pf_path = win_programfiles_path(); + if (!path_found) { + std::strcpy(s_path,".\\medcon.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) { + std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"medcon.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./medcon"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"medcon"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to store temporary files. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path where temporary files can be saved. + **/ + inline const char* temporary_path(const char *const user_path, const bool reinit_path) { +#define _cimg_test_temporary_path(p) \ + if (!path_found) { \ + cimg_snprintf(s_path,s_path._width,"%s",p); \ + cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \ + if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \ + } + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + CImg tmp(1024), filename_tmp(256); + std::FILE *file = 0; + cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand()); + char *tmpPath = std::getenv("TMP"); + if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); } + if (tmpPath) _cimg_test_temporary_path(tmpPath); +#if cimg_OS==2 + _cimg_test_temporary_path("C:\\WINNT\\Temp"); + _cimg_test_temporary_path("C:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("C:\\Temp"); + _cimg_test_temporary_path("C:"); + _cimg_test_temporary_path("D:\\WINNT\\Temp"); + _cimg_test_temporary_path("D:\\WINDOWS\\Temp"); + _cimg_test_temporary_path("D:\\Temp"); + _cimg_test_temporary_path("D:"); +#else + _cimg_test_temporary_path("/tmp"); + _cimg_test_temporary_path("/var/tmp"); +#endif + if (!path_found) { + *s_path = 0; + std::strncpy(tmp,filename_tmp,tmp._width - 1); + if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } + } + if (!path_found) { + cimg::mutex(7,0); + throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n"); + } + } + cimg::mutex(7,0); + return s_path; + } + + //! Get/set path to the \c wget binary. + /** + \param user_path Specified path, or \c 0 to get the path currently used. + \param reinit_path Force path to be recalculated (may take some time). + \return Path containing the \c wget binary. + **/ + inline const char *wget_path(const char *const user_path, const bool reinit_path) { + static CImg s_path; + cimg::mutex(7); + if (reinit_path) s_path.assign(); + if (user_path) { + if (!s_path) s_path.assign(1024); + std::strncpy(s_path,user_path,1023); + } else if (!s_path) { + s_path.assign(1024); + bool path_found = false; + std::FILE *file = 0; +#if cimg_OS==2 + if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true; + if (!path_found) { + std::strcpy(s_path,".\\wget.exe"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"wget.exe"); +#else + if (!path_found) { + std::strcpy(s_path,"./wget"); + if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; } + } + if (!path_found) std::strcpy(s_path,"wget"); +#endif + winformat_string(s_path); + } + cimg::mutex(7,0); + return s_path; + } + + + // [internal] Sorting function, used by cimg::files(). + inline int _sort_files(const void* a, const void* b) { + const CImg &sa = *(CImg*)a, &sb = *(CImg*)b; + return std::strcmp(sa._data,sb._data); + } + + //! Generate a numbered version of a filename. + inline char* number_filename(const char *const filename, const int number, + const unsigned int digits, char *const str) { + if (!filename) { if (str) *str = 0; return 0; } + const unsigned int siz = (unsigned int)std::strlen(filename); + CImg format(16), body(siz + 32); + const char *const ext = cimg::split_filename(filename,body); + if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits); + else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits); + cimg_snprintf(str,1024,format._data,body._data,number,ext); + return str; + } + + //! Return list of files/directories in specified directory. + /** + \param path Path to the directory. Set to 0 for current directory. + \param is_pattern Tell if specified path has a matching pattern in it. + \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }. + \param include_path Tell if \c path must be included in resulting filenames. + \return A list of filenames. + **/ + inline CImgList files(const char *const path, const bool is_pattern=false, + const unsigned int mode=2, const bool include_path=false) { + if (!path || !*path) return files("*",true,mode,include_path); + CImgList res; + + // If path is a valid folder name, ignore argument 'is_pattern'. + const bool _is_pattern = is_pattern && !cimg::is_directory(path); + bool is_root = false, is_current = false; + cimg::unused(is_root,is_current); + + // Clean format of input path. + CImg pattern, _path = CImg::string(path); +#if cimg_OS==2 + for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/'; +#endif + char *pd = _path; + for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; } + *pd = 0; + unsigned int lp = (unsigned int)std::strlen(_path); + if (!_is_pattern && lp && _path[lp - 1]=='/') { + _path[lp - 1] = 0; --lp; +#if cimg_OS!=2 + is_root = !*_path; +#endif + } + + // Separate folder path and matching pattern. + if (_is_pattern) { + const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data()); + CImg::string(_path).move_to(pattern); + if (bpos) { + _path[bpos - 1] = 0; // End 'path' at last slash +#if cimg_OS!=2 + is_root = !*_path; +#endif + } else { // No path to folder specified, assuming current folder + is_current = true; *_path = 0; + } + lp = (unsigned int)std::strlen(_path); + } + + // Windows version. +#if cimg_OS==2 + if (!_is_pattern) { + pattern.assign(lp + 3); + std::memcpy(pattern,_path,lp); + pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0; + } + WIN32_FIND_DATAA file_data; + const HANDLE dir = FindFirstFileA(pattern.data(),&file_data); + if (dir==INVALID_HANDLE_VALUE) return CImgList::const_empty(); + do { + const char *const filename = file_data.cFileName; + if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { + const unsigned int lf = (unsigned int)std::strlen(filename); + const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0; + if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) { + if (include_path) { + CImg full_filename((lp?lp+1:0) + lf + 1); + if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; } + std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1); + full_filename.move_to(res); + } else CImg(filename,lf + 1).move_to(res); + } + } + } while (FindNextFileA(dir,&file_data)); + FindClose(dir); + + // Unix version (posix). +#elif cimg_OS == 1 + DIR *const dir = opendir(is_root?"/":is_current?".":_path.data()); + if (!dir) return CImgList::const_empty(); + struct dirent *ent; + while ((ent=readdir(dir))!=0) { + const char *const filename = ent->d_name; + if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) { + const unsigned int lf = (unsigned int)std::strlen(filename); + CImg full_filename(lp + lf + 2); + + if (!is_current) { + full_filename.assign(lp + lf + 2); + if (lp) std::memcpy(full_filename,_path,lp); + full_filename[lp] = '/'; + std::memcpy(full_filename._data + lp + 1,filename,lf + 1); + } else full_filename.assign(filename,lf + 1); + + struct stat st; + if (stat(full_filename,&st)==-1) continue; + const bool is_directory = (st.st_mode & S_IFDIR)!=0; + if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) { + if (include_path) { + if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) + full_filename.move_to(res); + } else { + if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0))) + CImg(filename,lf + 1).move_to(res); + } + } + } + } + closedir(dir); +#endif + + // Sort resulting list by lexicographic order. + if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg),_sort_files); + + return res; + } + + //! Try to guess format from an image file. + /** + \param file Input file (can be \c 0 if \c filename is set). + \param filename Filename, as a C-string (can be \c 0 if \c file is set). + \return C-string containing the guessed file format, or \c 0 if nothing has been guessed. + **/ + inline const char *ftype(std::FILE *const file, const char *const filename) { + if (!file && !filename) + throw CImgArgumentException("cimg::ftype(): Specified filename is (null)."); + static const char + *const _pnm = "pnm", + *const _pfm = "pfm", + *const _bmp = "bmp", + *const _gif = "gif", + *const _jpg = "jpg", + *const _off = "off", + *const _pan = "pan", + *const _png = "png", + *const _tif = "tif", + *const _inr = "inr", + *const _dcm = "dcm"; + const char *f_type = 0; + CImg header; + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + header._load_raw(file,filename,512,1,1,1,false,false,0); + const unsigned char *const uheader = (unsigned char*)header._data; + if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF + else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE + f_type = _inr; + else if (!std::strncmp(header,"PANDORE",7)) // PANDORE + f_type = _pan; + else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM + f_type = _dcm; + else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG + f_type = _jpg; + else if (header[0]=='B' && header[1]=='M') // BMP + f_type = _bmp; + else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && + (header[4]=='7' || header[4]=='9')) // GIF + f_type = _gif; + else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && + uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG + f_type = _png; + else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) // TIFF + f_type = _tif; + else { // PNM or PFM + CImgList _header = header.get_split(CImg::vector('\n'),0,false); + cimglist_for(_header,l) { + if (_header(l,0)=='#') continue; + if (_header[l]._width==2 && _header(l,0)=='P') { + const char c = _header(l,1); + if (c=='f' || c=='F') { f_type = _pfm; break; } + if (c>='1' && c<='9') { f_type = _pnm; break; } + } + f_type = 0; break; + } + } + } catch (CImgIOException&) { } + cimg::exception_mode(omode); + return f_type; + } + + //! Load file from network as a local temporary file. + /** + \param url URL of the filename, as a C-string. + \param[out] filename_local C-string containing the path to a local copy of \c filename. + \param timeout Maximum time (in seconds) authorized for downloading the file from the URL. + \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure. + \param referer Referer used, as a C-string. + \return Value of \c filename_local. + \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download. + **/ + inline char *load_network(const char *const url, char *const filename_local, + const unsigned int timeout, const bool try_fallback, + const char *const referer) { + if (!url) + throw CImgArgumentException("cimg::load_network(): Specified URL is (null)."); + if (!filename_local) + throw CImgArgumentException("cimg::load_network(): Specified destination string is (null)."); + if (!network_mode()) + throw CImgIOException("cimg::load_network(): Loading files from network is disabled."); + + const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext; + CImg ext = CImg::string(_ext); + std::FILE *file = 0; + *filename_local = 0; + if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0; + else cimg::strwindows_reserved(ext); + do { + cimg_snprintf(filename_local,256,"%s%c%s%s", + cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data); + if ((file=cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file); + } while (file); + +#ifdef cimg_use_curl + const unsigned int omode = cimg::exception_mode(); + cimg::exception_mode(0); + try { + CURL *curl = 0; + CURLcode res; + curl = curl_easy_init(); + if (curl) { + file = cimg::fopen(filename_local,"wb"); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,file); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L); + curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L); + if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout); + if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L); + if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + cimg::fseek(file,0,SEEK_END); // Check if file size is 0 + const cimg_ulong siz = cimg::ftell(file); + cimg::fclose(file); + if (siz>0 && res==CURLE_OK) { + cimg::exception_mode(omode); + return filename_local; + } else std::remove(filename_local); + } + } catch (...) { } + cimg::exception_mode(omode); + if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url); +#endif + + CImg command((unsigned int)std::strlen(url) + 64); + cimg::unused(try_fallback); + + // Try with 'curl' first. + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" -e %s -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,timeout,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -m %u -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),timeout,filename_local, + CImg::string(url)._system_strescape().data()); + } else { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" -e %s -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),referer,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -f --silent --compressed -o \"%s\" \"%s\"", + cimg::curl_path(),filename_local, + CImg::string(url)._system_strescape().data()); + } + cimg::system(command, cimg::curl_path()); + + if (!(file=cimg::std_fopen(filename_local,"rb"))) { + + // Try with 'wget' otherwise. + if (timeout) { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" --referer=%s -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,timeout,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -T %u -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),timeout,filename_local, + CImg::string(url)._system_strescape().data()); + } else { + if (referer) + cimg_snprintf(command,command._width,"\"%s\" --referer=%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),referer,filename_local, + CImg::string(url)._system_strescape().data()); + else + cimg_snprintf(command,command._width,"\"%s\" -q -r -l 0 --no-cache -O \"%s\" \"%s\"", + cimg::wget_path(),filename_local, + CImg::string(url)._system_strescape().data()); + } + cimg::system(command, cimg::wget_path()); + + if (!(file=cimg::std_fopen(filename_local,"rb"))) + throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands " + "'wget' or 'curl'.",url); + cimg::fclose(file); + + // Try gunzip it. + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(filename_local,command); + cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"", + gunzip_path(),filename_local); + cimg::system(command, gunzip_path()); + file = cimg::std_fopen(filename_local,"rb"); + if (!file) { + cimg_snprintf(command,command._width,"%s.gz",filename_local); + std::rename(command,filename_local); + file = cimg::std_fopen(filename_local,"rb"); + } + } + cimg::fseek(file,0,SEEK_END); // Check if file size is 0 + if (std::ftell(file)<=0) + throw CImgIOException("cimg::load_network(): Failed to load URL '%s' with external commands " + "'wget' or 'curl'.",url); + cimg::fclose(file); + return filename_local; + } + + // Implement a tic/toc mechanism to display elapsed time of algorithms. + inline cimg_uint64 tictoc(const bool is_tic) { + cimg::mutex(2); + static CImg times(64); + static unsigned int pos = 0; + const cimg_uint64 t1 = cimg::time(); + if (is_tic) { + // Tic + times[pos++] = t1; + if (pos>=times._width) + throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'."); + cimg::mutex(2,0); + return t1; + } + + // Toc + if (!pos) + throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made."); + const cimg_uint64 + t0 = times[--pos], + dt = t1>=t0?(t1 - t0):cimg::type::max(); + const unsigned int + edays = (unsigned int)(dt/86400000.), + ehours = (unsigned int)((dt - edays*86400000.)/3600000.), + emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.), + esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.), + ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.); + if (!edays && !ehours && !emin && !esec) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n", + cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal); + else { + if (!edays && !ehours && !emin) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal); + else { + if (!edays && !ehours) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal); + else{ + if (!edays) + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal); + else{ + std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n", + cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal); + } + } + } + } + cimg::mutex(2,0); + return dt; + } + + // Return a temporary string describing the size of a memory buffer. + inline const char *strbuffersize(const cimg_ulong size) { + static CImg res(256); + cimg::mutex(5); + if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":""); + else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); } + else if (size<1024*1024*1024LU) { + const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize); + } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); } + cimg::mutex(5,0); + return res; + } + + //! Display a simple dialog box, and wait for the user's response. + /** + \param title Title of the dialog window. + \param msg Main message displayed inside the dialog window. + \param button1_label Label of the 1st button. + \param button2_label Label of the 2nd button (\c 0 to hide button). + \param button3_label Label of the 3rd button (\c 0 to hide button). + \param button4_label Label of the 4th button (\c 0 to hide button). + \param button5_label Label of the 5th button (\c 0 to hide button). + \param button6_label Label of the 6th button (\c 0 to hide button). + \param logo Image logo displayed at the left of the main message. + \param is_centered Tells if the dialog window must be centered on the screen. + \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user. + \note + - Up to 6 buttons can be defined in the dialog window. + - The function returns when a user clicked one of the button or closed the dialog window. + - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box. + At least one button must be specified. + **/ + template + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const CImg& logo, const bool is_centered=false) { +#if cimg_display==0 + cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + logo._data,is_centered); + throw CImgIOException("cimg::dialog(): No display available."); +#else + static const unsigned char + black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; + + // Create buttons and canvas graphics + CImgList buttons, cbuttons, sbuttons; + if (button1_label) { + CImg().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons); + if (button2_label) { + CImg().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons); + if (button3_label) { + CImg().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons); + if (button4_label) { + CImg().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons); + if (button5_label) { + CImg().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons); + if (button6_label) { + CImg().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons); + }}}}}} + if (!buttons._width) + throw CImgArgumentException("cimg::dialog(): No buttons have been defined."); + cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3); + + unsigned int bw = 0, bh = 0; + cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); } + bw+=8; bh+=8; + if (bw<64) bw = 64; + if (bw>128) bw = 128; + if (bh<24) bh = 24; + if (bh>48) bh = 48; + + CImg button(bw,bh,1,3); + button.draw_rectangle(0,0,bw - 1,bh - 1,gray); + button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white); + button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black); + button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2); + CImg sbutton(bw,bh,1,3); + sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray); + sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black); + sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black); + sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white); + sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black); + sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2); + sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true). + draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); + sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false). + draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); + CImg cbutton(bw,bh,1,3); + cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2). + draw_rectangle(2,2,bw - 3,bh - 3,gray); + cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true). + draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false); + cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false). + draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false); + + cimglist_for(buttons,ll) { + CImg(cbutton). + draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]). + move_to(cbuttons); + CImg(sbutton). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). + move_to(sbuttons); + CImg(button). + draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]). + move_to(buttons[ll]); + } + + CImg canvas; + if (msg) + ((CImg().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas); + + const unsigned int + bwall = (buttons._width - 1)*(12 + bw) + bw, + w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall), + h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh), + lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)), + ly = (h - 12 - bh - logo._height)/2, + tx = lx + logo._width + 12, + ty = (h - 12 - bh - canvas._height)/2, + bx = (w - bwall)/2, + by = h - 12 - bh; + + if (canvas._data) + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w - 1,h - 1,gray). + draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). + draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black). + draw_image(tx,ty,canvas); + else + canvas = CImg(w,h,1,3). + draw_rectangle(0,0,w - 1,h - 1,gray). + draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white). + draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black); + if (logo._data) canvas.draw_image(lx,ly,logo); + + unsigned int xbuttons[6] = { 0 }; + cimglist_for(buttons,lll) { + xbuttons[lll] = bx + (bw + 12)*lll; + canvas.draw_image(xbuttons[lll],by,buttons[lll]); + } + + // Open window and enter events loop + CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false); + if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2, + (CImgDisplay::screen_height() - disp.height())/2); + bool stop_flag = false, refresh = false; + int oselected = -1, oclicked = -1, selected = -1, clicked = -1; + while (!disp.is_closed() && !stop_flag) { + if (refresh) { + if (clicked>=0) + CImg(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp); + else { + if (selected>=0) + CImg(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp); + else canvas.display(disp); + } + refresh = false; + } + disp.wait(15); + if (disp.is_resized()) disp.resize(disp,false); + + if (disp.button()&1) { + oclicked = clicked; + clicked = -1; + cimglist_for(buttons,l) + if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) && + disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) { + clicked = selected = l; + refresh = true; + } + if (clicked!=oclicked) refresh = true; + } else if (clicked>=0) stop_flag = true; + + if (disp.key()) { + oselected = selected; + switch (disp.key()) { + case cimg::keyESC : selected = -1; stop_flag = true; break; + case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break; + case cimg::keyTAB : + case cimg::keyARROWRIGHT : + case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break; + case cimg::keyARROWLEFT : + case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break; + } + disp.set_key(); + if (selected!=oselected) refresh = true; + } + } + if (!disp) selected = -1; + return selected; +#endif + } + + //! Display a simple dialog box, and wait for the user's response \specialization. + inline int dialog(const char *const title, const char *const msg, + const char *const button1_label, const char *const button2_label, + const char *const button3_label, const char *const button4_label, + const char *const button5_label, const char *const button6_label, + const bool is_centered) { + return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label, + CImg::_logo40x38(),is_centered); + } + + //! Evaluate math expression. + /** + \param expression C-string describing the formula to evaluate. + \param x Value of the pre-defined variable \c x. + \param y Value of the pre-defined variable \c y. + \param z Value of the pre-defined variable \c z. + \param c Value of the pre-defined variable \c c. + \return Result of the formula evaluation. + \note Set \c expression to \c 0 to keep evaluating the last specified \c expression. + \par Example + \code + const double + res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1' + res2 = cimg::eval(0,1,1); // will return '1' too + \endcode + **/ + inline double eval(const char *const expression, const double x, const double y, const double z, const double c) { + static const CImg empty; + return empty.eval(expression,x,y,z,c); + } + + template + inline CImg::type> eval(const char *const expression, const CImg& xyzc) { + static const CImg empty; + return empty.eval(expression,xyzc); + } + + } // namespace cimg { ... +} // namespace cimg_library { ... + +//! Short alias name. +namespace cil = cimg_library_suffixed; + +#ifdef _cimg_redefine_False +#define False 0 +#endif +#ifdef _cimg_redefine_True +#define True 1 +#endif +#ifdef _cimg_redefine_Status +#define Status int +#endif +#ifdef _cimg_redefine_Success +#define Success 0 +#endif +#ifdef _cimg_redefine_min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif +#ifdef _cimg_redefine_PI +#define PI 3.141592653589793238462643383 +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif + +// Local Variables: +// mode: c++ +// End: diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/demo.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/demo.h new file mode 100644 index 0000000..0aafc91 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/demo.h @@ -0,0 +1,136 @@ +/* + + */ + +#ifndef DEMO_H_ +#define DEMO_H_ +//ڴ˴ͷļ +#include "stdio.h" +#include "string.h" +#include "stdlib.h" +#include "math.h" + +#include "demo_define.h" +#include "tool.h" +#include "inital_alg_params_ynr.h" +#include "inital_alg_params_gic.h" +#include "inital_alg_params_lsc.h" +#include "inital_alg_params_lsc2.h" +#include "inital_alg_params_rk_shapren_HW.h" +#include "inital_alg_params_rk_edgefilter.h" + +#include "initial_alg_params_bayernr.h" + +#include "inital_alg_params_rkuvnr.h" +#include "inital_alg_params_rk_cnr.h" + +#include "inital_alg_params_mfnr.h" +#include "rk_aiq_awb_algo_v200.h" +#define FILE_RAW_EXT ".raw" +#define FILE_YUV_EXT ".yuv" +#define FILE_DAT_EXT ".dat" + +typedef enum YUV_FILE_FMT +{ + F_YUV_420SP = 0x00, + F_YUV_420P = 0x01, + F_YUV_422I = 0x02, + F_YUV_422SP = 0x03, + F_YUV_422P = 0x04, + F_YUV_444I = 0x05, + + F_YUV_MAX = 0x10, +}YUV_FILE_FMT_t; + +typedef enum INPUT_FILE_FMT +{ + F_IN_FMT_RAW = 0x00, + F_IN_FMT_YUV, + + + F_IN_FMT_MAX = 0x10, +}INPUT_FILE_FMT_t; + + + +//˴ +typedef struct tag_config_com +{ + int exp_info_en ; + int framenum ; + int rawwid ; + int rawhgt ; + int rawbit ; + int bayerfmt ; + int yuvbit ; + int yuvfmt ; +}tag_config_com; + +typedef struct tag_config_txt +{ + tag_config_com config_com; + + int framecnt ; + int iso ; + int exptime[3] ; + int expgain[3] ; + int rgain ; + int bgain ; + int grgain ; + int gbgain ; + int dGain ; + int lux ; +}tag_config_txt; + +typedef struct tag_ST_DEMO_INPUT_PARAMS +{ + int width; //rawͼ + int height; //rawͼ + int bayerPattern; //bayer patternʽ:0--BGGR,1--GBRG,2--GRBG,3--RGGB + int yuvFmt; //yuv file ʽ: YUV_FILE_FMT_t + int bitValue; //rawλ + int hdr_framenum; + float expGain[MAX_HDR_FRM_NUM]; // + float expTime[MAX_HDR_FRM_NUM]; //عʱ + int rGain; //wb rgain + int bGain; //wb bgain + int grGain; //wb grgain + int gbGain; //wb gbgain + int dGain; //wb gbgain + int fileFmt; //input file format:INPUT_FILE_FMT_t + int width_full; //rawͼ + int height_full; //rawͼ + int crop_width; + int crop_height; + int crop_xoffset; + int crop_yoffset; + + char pathFileCfg[256];//configļ· + char pathRawData[256];//rawͼ· + char nameRawData[256];//rawͼ + char pathExpInfo[256];//exp_infoļ· + char pathReslut[256];//ļ· + char suffix[256]; // ļ׺ַ + char pathRtlin[256]; //rtl in path + int skip_num; + int frame_end; + + int hdr_proc_mode; + int out_mode; + + + char dbgFlg[1024]; // must > ISP_CAP_MAX + int config_full; + + int exp_info_en; + int file_info_en; + FILE *fp_exp_info; +}ST_DEMO_INPUT_PARAMS; + + + +//˴ + + + +#endif // DEMO_H_ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/demo_define.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/demo_define.h new file mode 100644 index 0000000..da7d26e --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/demo_define.h @@ -0,0 +1,44 @@ +/* + + */ + +#ifndef DEFINE_H_ +#define DEFINE_H_ +//ڴ˴ͷļ + + + +#define S7_EDGE 1 +#define GZ 2 +#define DVR 3 +/* +#define ISP_BAYER_NR 1 +#define ISP_DPC 2 +#define ISP_BLC 3 +#define ISP_STAT_3A 4 +#define ISP_LSC 5 +#define ISP_AWBG 6 +#define ISP_VHDM 7 +#define ISP_GAMMA 8 +#define ISP_CSM 9 +#define ISP_DRC 10 +#define ISP_RGB2YUV 11 +#define ISP_LCE 12 +#define ISP_Y_NR 13 +#define ISP_SHARPEN 14 +#define ISP_SCALING 15 +#define ISP_GIC 16 +#define ISP_UV_NR 17 +#define ISP_HDR_MERGE 18 +#define ISP_HDR_TMO 19 +#define ISP_DPN 20 +#define ISP_CTK 21 +#define ISP_WDR 22 +*/ +#define SAVE_RESULT 1 + + +//#define GET_LSC_CALIBRATION_DATA //ȡlens shadingУ궨 + + +#endif // DEFINE_H_ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/amdgpu_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/amdgpu_drm.h new file mode 100644 index 0000000..4fe35d6 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/amdgpu_drm.h @@ -0,0 +1,1067 @@ +/* amdgpu_drm.h -- Public header for the amdgpu driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright 2014 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __AMDGPU_DRM_H__ +#define __AMDGPU_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_AMDGPU_GEM_CREATE 0x00 +#define DRM_AMDGPU_GEM_MMAP 0x01 +#define DRM_AMDGPU_CTX 0x02 +#define DRM_AMDGPU_BO_LIST 0x03 +#define DRM_AMDGPU_CS 0x04 +#define DRM_AMDGPU_INFO 0x05 +#define DRM_AMDGPU_GEM_METADATA 0x06 +#define DRM_AMDGPU_GEM_WAIT_IDLE 0x07 +#define DRM_AMDGPU_GEM_VA 0x08 +#define DRM_AMDGPU_WAIT_CS 0x09 +#define DRM_AMDGPU_GEM_OP 0x10 +#define DRM_AMDGPU_GEM_USERPTR 0x11 +#define DRM_AMDGPU_WAIT_FENCES 0x12 +#define DRM_AMDGPU_VM 0x13 +#define DRM_AMDGPU_FENCE_TO_HANDLE 0x14 +#define DRM_AMDGPU_SCHED 0x15 + +#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) +#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) +#define DRM_IOCTL_AMDGPU_CTX DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CTX, union drm_amdgpu_ctx) +#define DRM_IOCTL_AMDGPU_BO_LIST DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_BO_LIST, union drm_amdgpu_bo_list) +#define DRM_IOCTL_AMDGPU_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_CS, union drm_amdgpu_cs) +#define DRM_IOCTL_AMDGPU_INFO DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_INFO, struct drm_amdgpu_info) +#define DRM_IOCTL_AMDGPU_GEM_METADATA DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_METADATA, struct drm_amdgpu_gem_metadata) +#define DRM_IOCTL_AMDGPU_GEM_WAIT_IDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_WAIT_IDLE, union drm_amdgpu_gem_wait_idle) +#define DRM_IOCTL_AMDGPU_GEM_VA DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_VA, struct drm_amdgpu_gem_va) +#define DRM_IOCTL_AMDGPU_WAIT_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_CS, union drm_amdgpu_wait_cs) +#define DRM_IOCTL_AMDGPU_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_OP, struct drm_amdgpu_gem_op) +#define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) +#define DRM_IOCTL_AMDGPU_WAIT_FENCES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences) +#define DRM_IOCTL_AMDGPU_VM DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm) +#define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle) +#define DRM_IOCTL_AMDGPU_SCHED DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_SCHED, union drm_amdgpu_sched) + +/** + * DOC: memory domains + * + * %AMDGPU_GEM_DOMAIN_CPU System memory that is not GPU accessible. + * Memory in this pool could be swapped out to disk if there is pressure. + * + * %AMDGPU_GEM_DOMAIN_GTT GPU accessible system memory, mapped into the + * GPU's virtual address space via gart. Gart memory linearizes non-contiguous + * pages of system memory, allows GPU access system memory in a linezrized + * fashion. + * + * %AMDGPU_GEM_DOMAIN_VRAM Local video memory. For APUs, it is memory + * carved out by the BIOS. + * + * %AMDGPU_GEM_DOMAIN_GDS Global on-chip data storage used to share data + * across shader threads. + * + * %AMDGPU_GEM_DOMAIN_GWS Global wave sync, used to synchronize the + * execution of all the waves on a device. + * + * %AMDGPU_GEM_DOMAIN_OA Ordered append, used by 3D or Compute engines + * for appending data. + */ +#define AMDGPU_GEM_DOMAIN_CPU 0x1 +#define AMDGPU_GEM_DOMAIN_GTT 0x2 +#define AMDGPU_GEM_DOMAIN_VRAM 0x4 +#define AMDGPU_GEM_DOMAIN_GDS 0x8 +#define AMDGPU_GEM_DOMAIN_GWS 0x10 +#define AMDGPU_GEM_DOMAIN_OA 0x20 +#define AMDGPU_GEM_DOMAIN_MASK (AMDGPU_GEM_DOMAIN_CPU | \ + AMDGPU_GEM_DOMAIN_GTT | \ + AMDGPU_GEM_DOMAIN_VRAM | \ + AMDGPU_GEM_DOMAIN_GDS | \ + AMDGPU_GEM_DOMAIN_GWS | \ + AMDGPU_GEM_DOMAIN_OA) + +/* Flag that CPU access will be required for the case of VRAM domain */ +#define AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED (1 << 0) +/* Flag that CPU access will not work, this VRAM domain is invisible */ +#define AMDGPU_GEM_CREATE_NO_CPU_ACCESS (1 << 1) +/* Flag that USWC attributes should be used for GTT */ +#define AMDGPU_GEM_CREATE_CPU_GTT_USWC (1 << 2) +/* Flag that the memory should be in VRAM and cleared */ +#define AMDGPU_GEM_CREATE_VRAM_CLEARED (1 << 3) +/* Flag that create shadow bo(GTT) while allocating vram bo */ +#define AMDGPU_GEM_CREATE_SHADOW (1 << 4) +/* Flag that allocating the BO should use linear VRAM */ +#define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 5) +/* Flag that BO is always valid in this VM */ +#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 6) +/* Flag that BO sharing will be explicitly synchronized */ +#define AMDGPU_GEM_CREATE_EXPLICIT_SYNC (1 << 7) +/* Flag that indicates allocating MQD gart on GFX9, where the mtype + * for the second page onward should be set to NC. + */ +#define AMDGPU_GEM_CREATE_MQD_GFX9 (1 << 8) +/* Flag that BO may contain sensitive data that must be wiped before + * releasing the memory + */ +#define AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE (1 << 9) + +struct drm_amdgpu_gem_create_in { + /** the requested memory size */ + __u64 bo_size; + /** physical start_addr alignment in bytes for some HW requirements */ + __u64 alignment; + /** the requested memory domains */ + __u64 domains; + /** allocation flags */ + __u64 domain_flags; +}; + +struct drm_amdgpu_gem_create_out { + /** returned GEM object handle */ + __u32 handle; + __u32 _pad; +}; + +union drm_amdgpu_gem_create { + struct drm_amdgpu_gem_create_in in; + struct drm_amdgpu_gem_create_out out; +}; + +/** Opcode to create new residency list. */ +#define AMDGPU_BO_LIST_OP_CREATE 0 +/** Opcode to destroy previously created residency list */ +#define AMDGPU_BO_LIST_OP_DESTROY 1 +/** Opcode to update resource information in the list */ +#define AMDGPU_BO_LIST_OP_UPDATE 2 + +struct drm_amdgpu_bo_list_in { + /** Type of operation */ + __u32 operation; + /** Handle of list or 0 if we want to create one */ + __u32 list_handle; + /** Number of BOs in list */ + __u32 bo_number; + /** Size of each element describing BO */ + __u32 bo_info_size; + /** Pointer to array describing BOs */ + __u64 bo_info_ptr; +}; + +struct drm_amdgpu_bo_list_entry { + /** Handle of BO */ + __u32 bo_handle; + /** New (if specified) BO priority to be used during migration */ + __u32 bo_priority; +}; + +struct drm_amdgpu_bo_list_out { + /** Handle of resource list */ + __u32 list_handle; + __u32 _pad; +}; + +union drm_amdgpu_bo_list { + struct drm_amdgpu_bo_list_in in; + struct drm_amdgpu_bo_list_out out; +}; + +/* context related */ +#define AMDGPU_CTX_OP_ALLOC_CTX 1 +#define AMDGPU_CTX_OP_FREE_CTX 2 +#define AMDGPU_CTX_OP_QUERY_STATE 3 +#define AMDGPU_CTX_OP_QUERY_STATE2 4 + +/* GPU reset status */ +#define AMDGPU_CTX_NO_RESET 0 +/* this the context caused it */ +#define AMDGPU_CTX_GUILTY_RESET 1 +/* some other context caused it */ +#define AMDGPU_CTX_INNOCENT_RESET 2 +/* unknown cause */ +#define AMDGPU_CTX_UNKNOWN_RESET 3 + +/* indicate gpu reset occured after ctx created */ +#define AMDGPU_CTX_QUERY2_FLAGS_RESET (1<<0) +/* indicate vram lost occured after ctx created */ +#define AMDGPU_CTX_QUERY2_FLAGS_VRAMLOST (1<<1) +/* indicate some job from this context once cause gpu hang */ +#define AMDGPU_CTX_QUERY2_FLAGS_GUILTY (1<<2) +/* indicate some errors are detected by RAS */ +#define AMDGPU_CTX_QUERY2_FLAGS_RAS_CE (1<<3) +#define AMDGPU_CTX_QUERY2_FLAGS_RAS_UE (1<<4) + +/* Context priority level */ +#define AMDGPU_CTX_PRIORITY_UNSET -2048 +#define AMDGPU_CTX_PRIORITY_VERY_LOW -1023 +#define AMDGPU_CTX_PRIORITY_LOW -512 +#define AMDGPU_CTX_PRIORITY_NORMAL 0 +/* + * When used in struct drm_amdgpu_ctx_in, a priority above NORMAL requires + * CAP_SYS_NICE or DRM_MASTER +*/ +#define AMDGPU_CTX_PRIORITY_HIGH 512 +#define AMDGPU_CTX_PRIORITY_VERY_HIGH 1023 + +struct drm_amdgpu_ctx_in { + /** AMDGPU_CTX_OP_* */ + __u32 op; + /** For future use, no flags defined so far */ + __u32 flags; + __u32 ctx_id; + /** AMDGPU_CTX_PRIORITY_* */ + __s32 priority; +}; + +union drm_amdgpu_ctx_out { + struct { + __u32 ctx_id; + __u32 _pad; + } alloc; + + struct { + /** For future use, no flags defined so far */ + __u64 flags; + /** Number of resets caused by this context so far. */ + __u32 hangs; + /** Reset status since the last call of the ioctl. */ + __u32 reset_status; + } state; +}; + +union drm_amdgpu_ctx { + struct drm_amdgpu_ctx_in in; + union drm_amdgpu_ctx_out out; +}; + +/* vm ioctl */ +#define AMDGPU_VM_OP_RESERVE_VMID 1 +#define AMDGPU_VM_OP_UNRESERVE_VMID 2 + +struct drm_amdgpu_vm_in { + /** AMDGPU_VM_OP_* */ + __u32 op; + __u32 flags; +}; + +struct drm_amdgpu_vm_out { + /** For future use, no flags defined so far */ + __u64 flags; +}; + +union drm_amdgpu_vm { + struct drm_amdgpu_vm_in in; + struct drm_amdgpu_vm_out out; +}; + +/* sched ioctl */ +#define AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE 1 +#define AMDGPU_SCHED_OP_CONTEXT_PRIORITY_OVERRIDE 2 + +struct drm_amdgpu_sched_in { + /* AMDGPU_SCHED_OP_* */ + __u32 op; + __u32 fd; + /** AMDGPU_CTX_PRIORITY_* */ + __s32 priority; + __u32 ctx_id; +}; + +union drm_amdgpu_sched { + struct drm_amdgpu_sched_in in; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define AMDGPU_GEM_USERPTR_READONLY (1 << 0) +#define AMDGPU_GEM_USERPTR_ANONONLY (1 << 1) +#define AMDGPU_GEM_USERPTR_VALIDATE (1 << 2) +#define AMDGPU_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_amdgpu_gem_userptr { + __u64 addr; + __u64 size; + /* AMDGPU_GEM_USERPTR_* */ + __u32 flags; + /* Resulting GEM handle */ + __u32 handle; +}; + +/* SI-CI-VI: */ +/* same meaning as the GB_TILE_MODE and GL_MACRO_TILE_MODE fields */ +#define AMDGPU_TILING_ARRAY_MODE_SHIFT 0 +#define AMDGPU_TILING_ARRAY_MODE_MASK 0xf +#define AMDGPU_TILING_PIPE_CONFIG_SHIFT 4 +#define AMDGPU_TILING_PIPE_CONFIG_MASK 0x1f +#define AMDGPU_TILING_TILE_SPLIT_SHIFT 9 +#define AMDGPU_TILING_TILE_SPLIT_MASK 0x7 +#define AMDGPU_TILING_MICRO_TILE_MODE_SHIFT 12 +#define AMDGPU_TILING_MICRO_TILE_MODE_MASK 0x7 +#define AMDGPU_TILING_BANK_WIDTH_SHIFT 15 +#define AMDGPU_TILING_BANK_WIDTH_MASK 0x3 +#define AMDGPU_TILING_BANK_HEIGHT_SHIFT 17 +#define AMDGPU_TILING_BANK_HEIGHT_MASK 0x3 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_SHIFT 19 +#define AMDGPU_TILING_MACRO_TILE_ASPECT_MASK 0x3 +#define AMDGPU_TILING_NUM_BANKS_SHIFT 21 +#define AMDGPU_TILING_NUM_BANKS_MASK 0x3 + +/* GFX9 and later: */ +#define AMDGPU_TILING_SWIZZLE_MODE_SHIFT 0 +#define AMDGPU_TILING_SWIZZLE_MODE_MASK 0x1f +#define AMDGPU_TILING_DCC_OFFSET_256B_SHIFT 5 +#define AMDGPU_TILING_DCC_OFFSET_256B_MASK 0xFFFFFF +#define AMDGPU_TILING_DCC_PITCH_MAX_SHIFT 29 +#define AMDGPU_TILING_DCC_PITCH_MAX_MASK 0x3FFF +#define AMDGPU_TILING_DCC_INDEPENDENT_64B_SHIFT 43 +#define AMDGPU_TILING_DCC_INDEPENDENT_64B_MASK 0x1 + +/* Set/Get helpers for tiling flags. */ +#define AMDGPU_TILING_SET(field, value) \ + (((__u64)(value) & AMDGPU_TILING_##field##_MASK) << AMDGPU_TILING_##field##_SHIFT) +#define AMDGPU_TILING_GET(value, field) \ + (((__u64)(value) >> AMDGPU_TILING_##field##_SHIFT) & AMDGPU_TILING_##field##_MASK) + +#define AMDGPU_GEM_METADATA_OP_SET_METADATA 1 +#define AMDGPU_GEM_METADATA_OP_GET_METADATA 2 + +/** The same structure is shared for input/output */ +struct drm_amdgpu_gem_metadata { + /** GEM Object handle */ + __u32 handle; + /** Do we want get or set metadata */ + __u32 op; + struct { + /** For future use, no flags defined so far */ + __u64 flags; + /** family specific tiling info */ + __u64 tiling_info; + __u32 data_size_bytes; + __u32 data[64]; + } data; +}; + +struct drm_amdgpu_gem_mmap_in { + /** the GEM object handle */ + __u32 handle; + __u32 _pad; +}; + +struct drm_amdgpu_gem_mmap_out { + /** mmap offset from the vma offset manager */ + __u64 addr_ptr; +}; + +union drm_amdgpu_gem_mmap { + struct drm_amdgpu_gem_mmap_in in; + struct drm_amdgpu_gem_mmap_out out; +}; + +struct drm_amdgpu_gem_wait_idle_in { + /** GEM object handle */ + __u32 handle; + /** For future use, no flags defined so far */ + __u32 flags; + /** Absolute timeout to wait */ + __u64 timeout; +}; + +struct drm_amdgpu_gem_wait_idle_out { + /** BO status: 0 - BO is idle, 1 - BO is busy */ + __u32 status; + /** Returned current memory domain */ + __u32 domain; +}; + +union drm_amdgpu_gem_wait_idle { + struct drm_amdgpu_gem_wait_idle_in in; + struct drm_amdgpu_gem_wait_idle_out out; +}; + +struct drm_amdgpu_wait_cs_in { + /* Command submission handle + * handle equals 0 means none to wait for + * handle equals ~0ull means wait for the latest sequence number + */ + __u64 handle; + /** Absolute timeout to wait */ + __u64 timeout; + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u32 ctx_id; +}; + +struct drm_amdgpu_wait_cs_out { + /** CS status: 0 - CS completed, 1 - CS still busy */ + __u64 status; +}; + +union drm_amdgpu_wait_cs { + struct drm_amdgpu_wait_cs_in in; + struct drm_amdgpu_wait_cs_out out; +}; + +struct drm_amdgpu_fence { + __u32 ctx_id; + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u64 seq_no; +}; + +struct drm_amdgpu_wait_fences_in { + /** This points to uint64_t * which points to fences */ + __u64 fences; + __u32 fence_count; + __u32 wait_all; + __u64 timeout_ns; +}; + +struct drm_amdgpu_wait_fences_out { + __u32 status; + __u32 first_signaled; +}; + +union drm_amdgpu_wait_fences { + struct drm_amdgpu_wait_fences_in in; + struct drm_amdgpu_wait_fences_out out; +}; + +#define AMDGPU_GEM_OP_GET_GEM_CREATE_INFO 0 +#define AMDGPU_GEM_OP_SET_PLACEMENT 1 + +/* Sets or returns a value associated with a buffer. */ +struct drm_amdgpu_gem_op { + /** GEM object handle */ + __u32 handle; + /** AMDGPU_GEM_OP_* */ + __u32 op; + /** Input or return value */ + __u64 value; +}; + +#define AMDGPU_VA_OP_MAP 1 +#define AMDGPU_VA_OP_UNMAP 2 +#define AMDGPU_VA_OP_CLEAR 3 +#define AMDGPU_VA_OP_REPLACE 4 + +/* Delay the page table update till the next CS */ +#define AMDGPU_VM_DELAY_UPDATE (1 << 0) + +/* Mapping flags */ +/* readable mapping */ +#define AMDGPU_VM_PAGE_READABLE (1 << 1) +/* writable mapping */ +#define AMDGPU_VM_PAGE_WRITEABLE (1 << 2) +/* executable mapping, new for VI */ +#define AMDGPU_VM_PAGE_EXECUTABLE (1 << 3) +/* partially resident texture */ +#define AMDGPU_VM_PAGE_PRT (1 << 4) +/* MTYPE flags use bit 5 to 8 */ +#define AMDGPU_VM_MTYPE_MASK (0xf << 5) +/* Default MTYPE. Pre-AI must use this. Recommended for newer ASICs. */ +#define AMDGPU_VM_MTYPE_DEFAULT (0 << 5) +/* Use NC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_NC (1 << 5) +/* Use WC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_WC (2 << 5) +/* Use CC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_CC (3 << 5) +/* Use UC MTYPE instead of default MTYPE */ +#define AMDGPU_VM_MTYPE_UC (4 << 5) + +struct drm_amdgpu_gem_va { + /** GEM object handle */ + __u32 handle; + __u32 _pad; + /** AMDGPU_VA_OP_* */ + __u32 operation; + /** AMDGPU_VM_PAGE_* */ + __u32 flags; + /** va address to assign . Must be correctly aligned.*/ + __u64 va_address; + /** Specify offset inside of BO to assign. Must be correctly aligned.*/ + __u64 offset_in_bo; + /** Specify mapping size. Must be correctly aligned. */ + __u64 map_size; +}; + +#define AMDGPU_HW_IP_GFX 0 +#define AMDGPU_HW_IP_COMPUTE 1 +#define AMDGPU_HW_IP_DMA 2 +#define AMDGPU_HW_IP_UVD 3 +#define AMDGPU_HW_IP_VCE 4 +#define AMDGPU_HW_IP_UVD_ENC 5 +#define AMDGPU_HW_IP_VCN_DEC 6 +#define AMDGPU_HW_IP_VCN_ENC 7 +#define AMDGPU_HW_IP_VCN_JPEG 8 +#define AMDGPU_HW_IP_NUM 9 + +#define AMDGPU_HW_IP_INSTANCE_MAX_COUNT 1 + +#define AMDGPU_CHUNK_ID_IB 0x01 +#define AMDGPU_CHUNK_ID_FENCE 0x02 +#define AMDGPU_CHUNK_ID_DEPENDENCIES 0x03 +#define AMDGPU_CHUNK_ID_SYNCOBJ_IN 0x04 +#define AMDGPU_CHUNK_ID_SYNCOBJ_OUT 0x05 +#define AMDGPU_CHUNK_ID_BO_HANDLES 0x06 +#define AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES 0x07 +#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT 0x08 +#define AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL 0x09 + +struct drm_amdgpu_cs_chunk { + __u32 chunk_id; + __u32 length_dw; + __u64 chunk_data; +}; + +struct drm_amdgpu_cs_in { + /** Rendering context id */ + __u32 ctx_id; + /** Handle of resource list associated with CS */ + __u32 bo_list_handle; + __u32 num_chunks; + __u32 _pad; + /** this points to __u64 * which point to cs chunks */ + __u64 chunks; +}; + +struct drm_amdgpu_cs_out { + __u64 handle; +}; + +union drm_amdgpu_cs { + struct drm_amdgpu_cs_in in; + struct drm_amdgpu_cs_out out; +}; + +/* Specify flags to be used for IB */ + +/* This IB should be submitted to CE */ +#define AMDGPU_IB_FLAG_CE (1<<0) + +/* Preamble flag, which means the IB could be dropped if no context switch */ +#define AMDGPU_IB_FLAG_PREAMBLE (1<<1) + +/* Preempt flag, IB should set Pre_enb bit if PREEMPT flag detected */ +#define AMDGPU_IB_FLAG_PREEMPT (1<<2) + +/* The IB fence should do the L2 writeback but not invalidate any shader + * caches (L2/vL1/sL1/I$). */ +#define AMDGPU_IB_FLAG_TC_WB_NOT_INVALIDATE (1 << 3) + +/* Set GDS_COMPUTE_MAX_WAVE_ID = DEFAULT before PACKET3_INDIRECT_BUFFER. + * This will reset wave ID counters for the IB. + */ +#define AMDGPU_IB_FLAG_RESET_GDS_MAX_WAVE_ID (1 << 4) + +struct drm_amdgpu_cs_chunk_ib { + __u32 _pad; + /** AMDGPU_IB_FLAG_* */ + __u32 flags; + /** Virtual address to begin IB execution */ + __u64 va_start; + /** Size of submission */ + __u32 ib_bytes; + /** HW IP to submit to */ + __u32 ip_type; + /** HW IP index of the same type to submit to */ + __u32 ip_instance; + /** Ring index to submit to */ + __u32 ring; +}; + +struct drm_amdgpu_cs_chunk_dep { + __u32 ip_type; + __u32 ip_instance; + __u32 ring; + __u32 ctx_id; + __u64 handle; +}; + +struct drm_amdgpu_cs_chunk_fence { + __u32 handle; + __u32 offset; +}; + +struct drm_amdgpu_cs_chunk_sem { + __u32 handle; +}; + +struct drm_amdgpu_cs_chunk_syncobj { + __u32 handle; + __u32 flags; + __u64 point; +}; + +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ 0 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD 1 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNC_FILE_FD 2 + +union drm_amdgpu_fence_to_handle { + struct { + struct drm_amdgpu_fence fence; + __u32 what; + __u32 pad; + } in; + struct { + __u32 handle; + } out; +}; + +struct drm_amdgpu_cs_chunk_data { + union { + struct drm_amdgpu_cs_chunk_ib ib_data; + struct drm_amdgpu_cs_chunk_fence fence_data; + }; +}; + +/** + * Query h/w info: Flag that this is integrated (a.h.a. fusion) GPU + * + */ +#define AMDGPU_IDS_FLAGS_FUSION 0x1 +#define AMDGPU_IDS_FLAGS_PREEMPTION 0x2 + +/* indicate if acceleration can be working */ +#define AMDGPU_INFO_ACCEL_WORKING 0x00 +/* get the crtc_id from the mode object id? */ +#define AMDGPU_INFO_CRTC_FROM_ID 0x01 +/* query hw IP info */ +#define AMDGPU_INFO_HW_IP_INFO 0x02 +/* query hw IP instance count for the specified type */ +#define AMDGPU_INFO_HW_IP_COUNT 0x03 +/* timestamp for GL_ARB_timer_query */ +#define AMDGPU_INFO_TIMESTAMP 0x05 +/* Query the firmware version */ +#define AMDGPU_INFO_FW_VERSION 0x0e + /* Subquery id: Query VCE firmware version */ + #define AMDGPU_INFO_FW_VCE 0x1 + /* Subquery id: Query UVD firmware version */ + #define AMDGPU_INFO_FW_UVD 0x2 + /* Subquery id: Query GMC firmware version */ + #define AMDGPU_INFO_FW_GMC 0x03 + /* Subquery id: Query GFX ME firmware version */ + #define AMDGPU_INFO_FW_GFX_ME 0x04 + /* Subquery id: Query GFX PFP firmware version */ + #define AMDGPU_INFO_FW_GFX_PFP 0x05 + /* Subquery id: Query GFX CE firmware version */ + #define AMDGPU_INFO_FW_GFX_CE 0x06 + /* Subquery id: Query GFX RLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC 0x07 + /* Subquery id: Query GFX MEC firmware version */ + #define AMDGPU_INFO_FW_GFX_MEC 0x08 + /* Subquery id: Query SMC firmware version */ + #define AMDGPU_INFO_FW_SMC 0x0a + /* Subquery id: Query SDMA firmware version */ + #define AMDGPU_INFO_FW_SDMA 0x0b + /* Subquery id: Query PSP SOS firmware version */ + #define AMDGPU_INFO_FW_SOS 0x0c + /* Subquery id: Query PSP ASD firmware version */ + #define AMDGPU_INFO_FW_ASD 0x0d + /* Subquery id: Query VCN firmware version */ + #define AMDGPU_INFO_FW_VCN 0x0e + /* Subquery id: Query GFX RLC SRLC firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_CNTL 0x0f + /* Subquery id: Query GFX RLC SRLG firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_GPM_MEM 0x10 + /* Subquery id: Query GFX RLC SRLS firmware version */ + #define AMDGPU_INFO_FW_GFX_RLC_RESTORE_LIST_SRM_MEM 0x11 + /* Subquery id: Query DMCU firmware version */ + #define AMDGPU_INFO_FW_DMCU 0x12 + #define AMDGPU_INFO_FW_TA 0x13 +/* number of bytes moved for TTM migration */ +#define AMDGPU_INFO_NUM_BYTES_MOVED 0x0f +/* the used VRAM size */ +#define AMDGPU_INFO_VRAM_USAGE 0x10 +/* the used GTT size */ +#define AMDGPU_INFO_GTT_USAGE 0x11 +/* Information about GDS, etc. resource configuration */ +#define AMDGPU_INFO_GDS_CONFIG 0x13 +/* Query information about VRAM and GTT domains */ +#define AMDGPU_INFO_VRAM_GTT 0x14 +/* Query information about register in MMR address space*/ +#define AMDGPU_INFO_READ_MMR_REG 0x15 +/* Query information about device: rev id, family, etc. */ +#define AMDGPU_INFO_DEV_INFO 0x16 +/* visible vram usage */ +#define AMDGPU_INFO_VIS_VRAM_USAGE 0x17 +/* number of TTM buffer evictions */ +#define AMDGPU_INFO_NUM_EVICTIONS 0x18 +/* Query memory about VRAM and GTT domains */ +#define AMDGPU_INFO_MEMORY 0x19 +/* Query vce clock table */ +#define AMDGPU_INFO_VCE_CLOCK_TABLE 0x1A +/* Query vbios related information */ +#define AMDGPU_INFO_VBIOS 0x1B + /* Subquery id: Query vbios size */ + #define AMDGPU_INFO_VBIOS_SIZE 0x1 + /* Subquery id: Query vbios image */ + #define AMDGPU_INFO_VBIOS_IMAGE 0x2 +/* Query UVD handles */ +#define AMDGPU_INFO_NUM_HANDLES 0x1C +/* Query sensor related information */ +#define AMDGPU_INFO_SENSOR 0x1D + /* Subquery id: Query GPU shader clock */ + #define AMDGPU_INFO_SENSOR_GFX_SCLK 0x1 + /* Subquery id: Query GPU memory clock */ + #define AMDGPU_INFO_SENSOR_GFX_MCLK 0x2 + /* Subquery id: Query GPU temperature */ + #define AMDGPU_INFO_SENSOR_GPU_TEMP 0x3 + /* Subquery id: Query GPU load */ + #define AMDGPU_INFO_SENSOR_GPU_LOAD 0x4 + /* Subquery id: Query average GPU power */ + #define AMDGPU_INFO_SENSOR_GPU_AVG_POWER 0x5 + /* Subquery id: Query northbridge voltage */ + #define AMDGPU_INFO_SENSOR_VDDNB 0x6 + /* Subquery id: Query graphics voltage */ + #define AMDGPU_INFO_SENSOR_VDDGFX 0x7 + /* Subquery id: Query GPU stable pstate shader clock */ + #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_SCLK 0x8 + /* Subquery id: Query GPU stable pstate memory clock */ + #define AMDGPU_INFO_SENSOR_STABLE_PSTATE_GFX_MCLK 0x9 +/* Number of VRAM page faults on CPU access. */ +#define AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS 0x1E +#define AMDGPU_INFO_VRAM_LOST_COUNTER 0x1F +/* query ras mask of enabled features*/ +#define AMDGPU_INFO_RAS_ENABLED_FEATURES 0x20 + +/* RAS MASK: UMC (VRAM) */ +#define AMDGPU_INFO_RAS_ENABLED_UMC (1 << 0) +/* RAS MASK: SDMA */ +#define AMDGPU_INFO_RAS_ENABLED_SDMA (1 << 1) +/* RAS MASK: GFX */ +#define AMDGPU_INFO_RAS_ENABLED_GFX (1 << 2) +/* RAS MASK: MMHUB */ +#define AMDGPU_INFO_RAS_ENABLED_MMHUB (1 << 3) +/* RAS MASK: ATHUB */ +#define AMDGPU_INFO_RAS_ENABLED_ATHUB (1 << 4) +/* RAS MASK: PCIE */ +#define AMDGPU_INFO_RAS_ENABLED_PCIE (1 << 5) +/* RAS MASK: HDP */ +#define AMDGPU_INFO_RAS_ENABLED_HDP (1 << 6) +/* RAS MASK: XGMI */ +#define AMDGPU_INFO_RAS_ENABLED_XGMI (1 << 7) +/* RAS MASK: DF */ +#define AMDGPU_INFO_RAS_ENABLED_DF (1 << 8) +/* RAS MASK: SMN */ +#define AMDGPU_INFO_RAS_ENABLED_SMN (1 << 9) +/* RAS MASK: SEM */ +#define AMDGPU_INFO_RAS_ENABLED_SEM (1 << 10) +/* RAS MASK: MP0 */ +#define AMDGPU_INFO_RAS_ENABLED_MP0 (1 << 11) +/* RAS MASK: MP1 */ +#define AMDGPU_INFO_RAS_ENABLED_MP1 (1 << 12) +/* RAS MASK: FUSE */ +#define AMDGPU_INFO_RAS_ENABLED_FUSE (1 << 13) + +#define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0 +#define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff +#define AMDGPU_INFO_MMR_SH_INDEX_SHIFT 8 +#define AMDGPU_INFO_MMR_SH_INDEX_MASK 0xff + +struct drm_amdgpu_query_fw { + /** AMDGPU_INFO_FW_* */ + __u32 fw_type; + /** + * Index of the IP if there are more IPs of + * the same type. + */ + __u32 ip_instance; + /** + * Index of the engine. Whether this is used depends + * on the firmware type. (e.g. MEC, SDMA) + */ + __u32 index; + __u32 _pad; +}; + +/* Input structure for the INFO ioctl */ +struct drm_amdgpu_info { + /* Where the return value will be stored */ + __u64 return_pointer; + /* The size of the return value. Just like "size" in "snprintf", + * it limits how many bytes the kernel can write. */ + __u32 return_size; + /* The query request id. */ + __u32 query; + + union { + struct { + __u32 id; + __u32 _pad; + } mode_crtc; + + struct { + /** AMDGPU_HW_IP_* */ + __u32 type; + /** + * Index of the IP if there are more IPs of the same + * type. Ignored by AMDGPU_INFO_HW_IP_COUNT. + */ + __u32 ip_instance; + } query_hw_ip; + + struct { + __u32 dword_offset; + /** number of registers to read */ + __u32 count; + __u32 instance; + /** For future use, no flags defined so far */ + __u32 flags; + } read_mmr_reg; + + struct drm_amdgpu_query_fw query_fw; + + struct { + __u32 type; + __u32 offset; + } vbios_info; + + struct { + __u32 type; + } sensor_info; + }; +}; + +struct drm_amdgpu_info_gds { + /** GDS GFX partition size */ + __u32 gds_gfx_partition_size; + /** GDS compute partition size */ + __u32 compute_partition_size; + /** total GDS memory size */ + __u32 gds_total_size; + /** GWS size per GFX partition */ + __u32 gws_per_gfx_partition; + /** GSW size per compute partition */ + __u32 gws_per_compute_partition; + /** OA size per GFX partition */ + __u32 oa_per_gfx_partition; + /** OA size per compute partition */ + __u32 oa_per_compute_partition; + __u32 _pad; +}; + +struct drm_amdgpu_info_vram_gtt { + __u64 vram_size; + __u64 vram_cpu_accessible_size; + __u64 gtt_size; +}; + +struct drm_amdgpu_heap_info { + /** max. physical memory */ + __u64 total_heap_size; + + /** Theoretical max. available memory in the given heap */ + __u64 usable_heap_size; + + /** + * Number of bytes allocated in the heap. This includes all processes + * and private allocations in the kernel. It changes when new buffers + * are allocated, freed, and moved. It cannot be larger than + * heap_size. + */ + __u64 heap_usage; + + /** + * Theoretical possible max. size of buffer which + * could be allocated in the given heap + */ + __u64 max_allocation; +}; + +struct drm_amdgpu_memory_info { + struct drm_amdgpu_heap_info vram; + struct drm_amdgpu_heap_info cpu_accessible_vram; + struct drm_amdgpu_heap_info gtt; +}; + +struct drm_amdgpu_info_firmware { + __u32 ver; + __u32 feature; +}; + +#define AMDGPU_VRAM_TYPE_UNKNOWN 0 +#define AMDGPU_VRAM_TYPE_GDDR1 1 +#define AMDGPU_VRAM_TYPE_DDR2 2 +#define AMDGPU_VRAM_TYPE_GDDR3 3 +#define AMDGPU_VRAM_TYPE_GDDR4 4 +#define AMDGPU_VRAM_TYPE_GDDR5 5 +#define AMDGPU_VRAM_TYPE_HBM 6 +#define AMDGPU_VRAM_TYPE_DDR3 7 +#define AMDGPU_VRAM_TYPE_DDR4 8 +#define AMDGPU_VRAM_TYPE_GDDR6 9 + +struct drm_amdgpu_info_device { + /** PCI Device ID */ + __u32 device_id; + /** Internal chip revision: A0, A1, etc.) */ + __u32 chip_rev; + __u32 external_rev; + /** Revision id in PCI Config space */ + __u32 pci_rev; + __u32 family; + __u32 num_shader_engines; + __u32 num_shader_arrays_per_engine; + /* in KHz */ + __u32 gpu_counter_freq; + __u64 max_engine_clock; + __u64 max_memory_clock; + /* cu information */ + __u32 cu_active_number; + /* NOTE: cu_ao_mask is INVALID, DON'T use it */ + __u32 cu_ao_mask; + __u32 cu_bitmap[4][4]; + /** Render backend pipe mask. One render backend is CB+DB. */ + __u32 enabled_rb_pipes_mask; + __u32 num_rb_pipes; + __u32 num_hw_gfx_contexts; + __u32 _pad; + __u64 ids_flags; + /** Starting virtual address for UMDs. */ + __u64 virtual_address_offset; + /** The maximum virtual address */ + __u64 virtual_address_max; + /** Required alignment of virtual addresses. */ + __u32 virtual_address_alignment; + /** Page table entry - fragment size */ + __u32 pte_fragment_size; + __u32 gart_page_size; + /** constant engine ram size*/ + __u32 ce_ram_size; + /** video memory type info*/ + __u32 vram_type; + /** video memory bit width*/ + __u32 vram_bit_width; + /* vce harvesting instance */ + __u32 vce_harvest_config; + /* gfx double offchip LDS buffers */ + __u32 gc_double_offchip_lds_buf; + /* NGG Primitive Buffer */ + __u64 prim_buf_gpu_addr; + /* NGG Position Buffer */ + __u64 pos_buf_gpu_addr; + /* NGG Control Sideband */ + __u64 cntl_sb_buf_gpu_addr; + /* NGG Parameter Cache */ + __u64 param_buf_gpu_addr; + __u32 prim_buf_size; + __u32 pos_buf_size; + __u32 cntl_sb_buf_size; + __u32 param_buf_size; + /* wavefront size*/ + __u32 wave_front_size; + /* shader visible vgprs*/ + __u32 num_shader_visible_vgprs; + /* CU per shader array*/ + __u32 num_cu_per_sh; + /* number of tcc blocks*/ + __u32 num_tcc_blocks; + /* gs vgt table depth*/ + __u32 gs_vgt_table_depth; + /* gs primitive buffer depth*/ + __u32 gs_prim_buffer_depth; + /* max gs wavefront per vgt*/ + __u32 max_gs_waves_per_vgt; + __u32 _pad1; + /* always on cu bitmap */ + __u32 cu_ao_bitmap[4][4]; + /** Starting high virtual address for UMDs. */ + __u64 high_va_offset; + /** The maximum high virtual address */ + __u64 high_va_max; + /* gfx10 pa_sc_tile_steering_override */ + __u32 pa_sc_tile_steering_override; + /* disabled TCCs */ + __u64 tcc_disabled_mask; +}; + +struct drm_amdgpu_info_hw_ip { + /** Version of h/w IP */ + __u32 hw_ip_version_major; + __u32 hw_ip_version_minor; + /** Capabilities */ + __u64 capabilities_flags; + /** command buffer address start alignment*/ + __u32 ib_start_alignment; + /** command buffer size alignment*/ + __u32 ib_size_alignment; + /** Bitmask of available rings. Bit 0 means ring 0, etc. */ + __u32 available_rings; + __u32 _pad; +}; + +struct drm_amdgpu_info_num_handles { + /** Max handles as supported by firmware for UVD */ + __u32 uvd_max_handles; + /** Handles currently in use for UVD */ + __u32 uvd_used_handles; +}; + +#define AMDGPU_VCE_CLOCK_TABLE_ENTRIES 6 + +struct drm_amdgpu_info_vce_clock_table_entry { + /** System clock */ + __u32 sclk; + /** Memory clock */ + __u32 mclk; + /** VCE clock */ + __u32 eclk; + __u32 pad; +}; + +struct drm_amdgpu_info_vce_clock_table { + struct drm_amdgpu_info_vce_clock_table_entry entries[AMDGPU_VCE_CLOCK_TABLE_ENTRIES]; + __u32 num_valid_entries; + __u32 pad; +}; + +/* + * Supported GPU families + */ +#define AMDGPU_FAMILY_UNKNOWN 0 +#define AMDGPU_FAMILY_SI 110 /* Hainan, Oland, Verde, Pitcairn, Tahiti */ +#define AMDGPU_FAMILY_CI 120 /* Bonaire, Hawaii */ +#define AMDGPU_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */ +#define AMDGPU_FAMILY_VI 130 /* Iceland, Tonga */ +#define AMDGPU_FAMILY_CZ 135 /* Carrizo, Stoney */ +#define AMDGPU_FAMILY_AI 141 /* Vega10 */ +#define AMDGPU_FAMILY_RV 142 /* Raven */ +#define AMDGPU_FAMILY_NV 143 /* Navi10 */ + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm.h new file mode 100644 index 0000000..438abde --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm.h @@ -0,0 +1,1042 @@ +/** + * \file drm.h + * Header for the Direct Rendering Manager + * + * \author Rickard E. (Rik) Faith + * + * \par Acknowledgments: + * Dec 1999, Richard Henderson , move to generic \c cmpxchg. + */ + +/* + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#if defined(__linux__) + +#include +#include +typedef unsigned int drm_handle_t; + +#else /* One of the BSDs */ + +#include +#include +#include +typedef int8_t __s8; +typedef uint8_t __u8; +typedef int16_t __s16; +typedef uint16_t __u16; +typedef int32_t __s32; +typedef uint32_t __u32; +typedef int64_t __s64; +typedef uint64_t __u64; +typedef size_t __kernel_size_t; +typedef unsigned long drm_handle_t; + +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /**< Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /**< How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +typedef unsigned int drm_context_t; +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; + +/** + * Cliprect. + * + * \warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well + * + * \note KW: Actually it's illegal to change either for + * backwards-compatibility reasons. + */ +struct drm_clip_rect { + unsigned short x1; + unsigned short y1; + unsigned short x2; + unsigned short y2; +}; + +/** + * Drawable information. + */ +struct drm_drawable_info { + unsigned int num_rects; + struct drm_clip_rect *rects; +}; + +/** + * Texture region, + */ +struct drm_tex_region { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; + unsigned int age; +}; + +/** + * Hardware lock. + * + * The lock structure is a simple cache-line aligned integer. To avoid + * processor bus contention on a multiprocessor system, there should not be any + * other data stored in the same cache line. + */ +struct drm_hw_lock { + __volatile__ unsigned int lock; /**< lock variable */ + char padding[60]; /**< Pad to cache line */ +}; + +/** + * DRM_IOCTL_VERSION ioctl argument type. + * + * \sa drmGetVersion(). + */ +struct drm_version { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + __kernel_size_t name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + __kernel_size_t date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + __kernel_size_t desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ +}; + +/** + * DRM_IOCTL_GET_UNIQUE ioctl argument type. + * + * \sa drmGetBusid() and drmSetBusId(). + */ +struct drm_unique { + __kernel_size_t unique_len; /**< Length of unique */ + char *unique; /**< Unique name for driver instantiation */ +}; + +struct drm_list { + int count; /**< Length of user-space structures */ + struct drm_version *version; +}; + +struct drm_block { + int unused; +}; + +/** + * DRM_IOCTL_CONTROL ioctl argument type. + * + * \sa drmCtlInstHandler() and drmCtlUninstHandler(). + */ +struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +}; + +/** + * Type of memory to map. + */ +enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /**< WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /**< no caching, no core dump */ + _DRM_SHM = 2, /**< shared, cached */ + _DRM_AGP = 3, /**< AGP/GART */ + _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ + _DRM_CONSISTENT = 5 /**< Consistent memory for PCI DMA */ +}; + +/** + * Memory mapping flags. + */ +enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /**< Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /**< shared, cached, locked */ + _DRM_KERNEL = 0x08, /**< kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /**< use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20, /**< SHM page that contains lock */ + _DRM_REMOVABLE = 0x40, /**< Removable mapping */ + _DRM_DRIVER = 0x80 /**< Managed by driver */ +}; + +struct drm_ctx_priv_map { + unsigned int ctx_id; /**< Context requesting private mapping */ + void *handle; /**< Handle of map */ +}; + +/** + * DRM_IOCTL_GET_MAP, DRM_IOCTL_ADD_MAP and DRM_IOCTL_RM_MAP ioctls + * argument type. + * + * \sa drmAddMap(). + */ +struct drm_map { + unsigned long offset; /**< Requested physical address (0 for SAREA)*/ + unsigned long size; /**< Requested physical size (bytes) */ + enum drm_map_type type; /**< Type of memory to map */ + enum drm_map_flags flags; /**< Flags */ + void *handle; /**< User-space: "Handle" to pass to mmap() */ + /**< Kernel-space: kernel-virtual address */ + int mtrr; /**< MTRR slot used */ + /* Private data */ +}; + +/** + * DRM_IOCTL_GET_CLIENT ioctl argument type. + */ +struct drm_client { + int idx; /**< Which client desired? */ + int auth; /**< Is client authenticated? */ + unsigned long pid; /**< Process ID */ + unsigned long uid; /**< User ID */ + unsigned long magic; /**< Magic */ + unsigned long iocs; /**< Ioctl count */ +}; + +enum drm_stat_type { + _DRM_STAT_LOCK, + _DRM_STAT_OPENS, + _DRM_STAT_CLOSES, + _DRM_STAT_IOCTLS, + _DRM_STAT_LOCKS, + _DRM_STAT_UNLOCKS, + _DRM_STAT_VALUE, /**< Generic value */ + _DRM_STAT_BYTE, /**< Generic byte counter (1024bytes/K) */ + _DRM_STAT_COUNT, /**< Generic non-byte counter (1000/k) */ + + _DRM_STAT_IRQ, /**< IRQ */ + _DRM_STAT_PRIMARY, /**< Primary DMA bytes */ + _DRM_STAT_SECONDARY, /**< Secondary DMA bytes */ + _DRM_STAT_DMA, /**< DMA */ + _DRM_STAT_SPECIAL, /**< Special DMA (e.g., priority or polled) */ + _DRM_STAT_MISSED /**< Missed DMA opportunity */ + /* Add to the *END* of the list */ +}; + +/** + * DRM_IOCTL_GET_STATS ioctl argument type. + */ +struct drm_stats { + unsigned long count; + struct { + unsigned long value; + enum drm_stat_type type; + } data[15]; +}; + +/** + * Hardware locking flags. + */ +enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +}; + +/** + * DRM_IOCTL_LOCK, DRM_IOCTL_UNLOCK and DRM_IOCTL_FINISH ioctl argument type. + * + * \sa drmGetLock() and drmUnlock(). + */ +struct drm_lock { + int context; + enum drm_lock_flags flags; +}; + +/** + * DMA flags + * + * \warning + * These values \e must match xf86drm.h. + * + * \sa drm_dma. + */ +enum drm_dma_flags { + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note The buffer may not yet have + * been processed by the hardware -- + * getting a hardware lock with the + * hardware quiescent will ensure + * that the buffer has been + * processed. + */ + _DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + _DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ +}; + +/** + * DRM_IOCTL_ADD_BUFS and DRM_IOCTL_MARK_BUFS ioctl argument type. + * + * \sa drmAddBufs(). + */ +struct drm_buf_desc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ + enum { + _DRM_PAGE_ALIGN = 0x01, /**< Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02, /**< Buffer is in AGP space */ + _DRM_SG_BUFFER = 0x04, /**< Scatter/gather memory buffer */ + _DRM_FB_BUFFER = 0x08, /**< Buffer is in frame buffer */ + _DRM_PCI_BUFFER_RO = 0x10 /**< Map PCI DMA buffer read-only */ + } flags; + unsigned long agp_start; /**< + * Start address of where the AGP buffers are + * in the AGP aperture + */ +}; + +/** + * DRM_IOCTL_INFO_BUFS ioctl argument type. + */ +struct drm_buf_info { + int count; /**< Entries in list */ + struct drm_buf_desc *list; +}; + +/** + * DRM_IOCTL_FREE_BUFS ioctl argument type. + */ +struct drm_buf_free { + int count; + int *list; +}; + +/** + * Buffer information + * + * \sa drm_buf_map. + */ +struct drm_buf_pub { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + void *address; /**< Address of buffer */ +}; + +/** + * DRM_IOCTL_MAP_BUFS ioctl argument type. + */ +struct drm_buf_map { + int count; /**< Length of the buffer list */ +#ifdef __cplusplus + void *virt; +#else + void *virtual; /**< Mmap'd area in user-virtual */ +#endif + struct drm_buf_pub *list; /**< Buffer information */ +}; + +/** + * DRM_IOCTL_DMA ioctl argument type. + * + * Indices here refer to the offset into the buffer list in drm_buf_get. + * + * \sa drmDMA(). + */ +struct drm_dma { + int context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_indices; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send */ + enum drm_dma_flags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size for buffers */ + int *request_indices; /**< Buffer information */ + int *request_sizes; + int granted_count; /**< Number of buffers granted */ +}; + +enum drm_ctx_flags { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +}; + +/** + * DRM_IOCTL_ADD_CTX ioctl argument type. + * + * \sa drmCreateContext() and drmDestroyContext(). + */ +struct drm_ctx { + drm_context_t handle; + enum drm_ctx_flags flags; +}; + +/** + * DRM_IOCTL_RES_CTX ioctl argument type. + */ +struct drm_ctx_res { + int count; + struct drm_ctx *contexts; +}; + +/** + * DRM_IOCTL_ADD_DRAW and DRM_IOCTL_RM_DRAW ioctl argument type. + */ +struct drm_draw { + drm_drawable_t handle; +}; + +/** + * DRM_IOCTL_UPDATE_DRAW ioctl argument type. + */ +typedef enum { + DRM_DRAWABLE_CLIPRECTS +} drm_drawable_info_type_t; + +struct drm_update_draw { + drm_drawable_t handle; + unsigned int type; + unsigned int num; + unsigned long long data; +}; + +/** + * DRM_IOCTL_GET_MAGIC and DRM_IOCTL_AUTH_MAGIC ioctl argument type. + */ +struct drm_auth { + drm_magic_t magic; +}; + +/** + * DRM_IOCTL_IRQ_BUSID ioctl argument type. + * + * \sa drmGetInterruptFromBusID(). + */ +struct drm_irq_busid { + int irq; /**< IRQ number */ + int busnum; /**< bus number */ + int devnum; /**< device number */ + int funcnum; /**< function number */ +}; + +enum drm_vblank_seq_type { + _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + /* bits 1-6 are reserved for high crtcs */ + _DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, + _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking, unsupported */ +}; +#define _DRM_VBLANK_HIGH_CRTC_SHIFT 1 + +#define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE) +#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \ + _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS) + +struct drm_wait_vblank_request { + enum drm_vblank_seq_type type; + unsigned int sequence; + unsigned long signal; +}; + +struct drm_wait_vblank_reply { + enum drm_vblank_seq_type type; + unsigned int sequence; + long tval_sec; + long tval_usec; +}; + +/** + * DRM_IOCTL_WAIT_VBLANK ioctl argument type. + * + * \sa drmWaitVBlank(). + */ +union drm_wait_vblank { + struct drm_wait_vblank_request request; + struct drm_wait_vblank_reply reply; +}; + +#define _DRM_PRE_MODESET 1 +#define _DRM_POST_MODESET 2 + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +struct drm_modeset_ctl { + __u32 crtc; + __u32 cmd; +}; + +/** + * DRM_IOCTL_AGP_ENABLE ioctl argument type. + * + * \sa drmAgpEnable(). + */ +struct drm_agp_mode { + unsigned long mode; /**< AGP mode */ +}; + +/** + * DRM_IOCTL_AGP_ALLOC and DRM_IOCTL_AGP_FREE ioctls argument type. + * + * \sa drmAgpAlloc() and drmAgpFree(). + */ +struct drm_agp_buffer { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for binding / unbinding */ + unsigned long type; /**< Type of memory to allocate */ + unsigned long physical; /**< Physical used by i810 */ +}; + +/** + * DRM_IOCTL_AGP_BIND and DRM_IOCTL_AGP_UNBIND ioctls argument type. + * + * \sa drmAgpBind() and drmAgpUnbind(). + */ +struct drm_agp_binding { + unsigned long handle; /**< From drm_agp_buffer */ + unsigned long offset; /**< In bytes -- will round to page boundary */ +}; + +/** + * DRM_IOCTL_AGP_INFO ioctl argument type. + * + * \sa drmAgpVersionMajor(), drmAgpVersionMinor(), drmAgpGetMode(), + * drmAgpBase(), drmAgpSize(), drmAgpMemoryUsed(), drmAgpMemoryAvail(), + * drmAgpVendorId() and drmAgpDeviceId(). + */ +struct drm_agp_info { + int agp_version_major; + int agp_version_minor; + unsigned long mode; + unsigned long aperture_base; /* physical address */ + unsigned long aperture_size; /* bytes */ + unsigned long memory_allowed; /* bytes */ + unsigned long memory_used; + + /* PCI information */ + unsigned short id_vendor; + unsigned short id_device; +}; + +/** + * DRM_IOCTL_SG_ALLOC ioctl argument type. + */ +struct drm_scatter_gather { + unsigned long size; /**< In bytes -- will round to page boundary */ + unsigned long handle; /**< Used for mapping / unmapping */ +}; + +/** + * DRM_IOCTL_SET_VERSION ioctl argument type. + */ +struct drm_set_version { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +}; + +/** DRM_IOCTL_GEM_CLOSE ioctl argument type */ +struct drm_gem_close { + /** Handle of the object to be closed. */ + __u32 handle; + __u32 pad; +}; + +/** DRM_IOCTL_GEM_FLINK ioctl argument type */ +struct drm_gem_flink { + /** Handle for the object being named */ + __u32 handle; + + /** Returned global name */ + __u32 name; +}; + +/** DRM_IOCTL_GEM_OPEN ioctl argument type */ +struct drm_gem_open { + /** Name of object being opened */ + __u32 name; + + /** Returned handle for the object */ + __u32 handle; + + /** Returned size of the object */ + __u64 size; +}; + +#define DRM_CAP_DUMB_BUFFER 0x1 +#define DRM_CAP_VBLANK_HIGH_CRTC 0x2 +#define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 +#define DRM_CAP_DUMB_PREFER_SHADOW 0x4 +#define DRM_CAP_PRIME 0x5 +#define DRM_PRIME_CAP_IMPORT 0x1 +#define DRM_PRIME_CAP_EXPORT 0x2 +#define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 +#define DRM_CAP_ASYNC_PAGE_FLIP 0x7 +/* + * The CURSOR_WIDTH and CURSOR_HEIGHT capabilities return a valid widthxheight + * combination for the hardware cursor. The intention is that a hardware + * agnostic userspace can query a cursor plane size to use. + * + * Note that the cross-driver contract is to merely return a valid size; + * drivers are free to attach another meaning on top, eg. i915 returns the + * maximum plane size. + */ +#define DRM_CAP_CURSOR_WIDTH 0x8 +#define DRM_CAP_CURSOR_HEIGHT 0x9 +#define DRM_CAP_ADDFB2_MODIFIERS 0x10 +#define DRM_CAP_PAGE_FLIP_TARGET 0x11 +#define DRM_CAP_CRTC_IN_VBLANK_EVENT 0x12 +#define DRM_CAP_SYNCOBJ 0x13 +#define DRM_CAP_SYNCOBJ_TIMELINE 0x14 + +/** DRM_IOCTL_GET_CAP ioctl argument type */ +struct drm_get_cap { + __u64 capability; + __u64 value; +}; + +/** + * DRM_CLIENT_CAP_STEREO_3D + * + * if set to 1, the DRM core will expose the stereo 3D capabilities of the + * monitor by advertising the supported 3D layouts in the flags of struct + * drm_mode_modeinfo. + */ +#define DRM_CLIENT_CAP_STEREO_3D 1 + +/** + * DRM_CLIENT_CAP_UNIVERSAL_PLANES + * + * If set to 1, the DRM core will expose all planes (overlay, primary, and + * cursor) to userspace. + */ +#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 + +/** + * DRM_CLIENT_CAP_ATOMIC + * + * If set to 1, the DRM core will expose atomic properties to userspace + */ +#define DRM_CLIENT_CAP_ATOMIC 3 + +/** + * DRM_CLIENT_CAP_ASPECT_RATIO + * + * If set to 1, the DRM core will provide aspect ratio information in modes. + */ +#define DRM_CLIENT_CAP_ASPECT_RATIO 4 + +/** + * DRM_CLIENT_CAP_WRITEBACK_CONNECTORS + * + * If set to 1, the DRM core will expose special connectors to be used for + * writing back to memory the scene setup in the commit. Depends on client + * also supporting DRM_CLIENT_CAP_ATOMIC + */ +#define DRM_CLIENT_CAP_WRITEBACK_CONNECTORS 5 + +/** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ +struct drm_set_client_cap { + __u64 capability; + __u64 value; +}; + +#define DRM_RDWR O_RDWR +#define DRM_CLOEXEC O_CLOEXEC +struct drm_prime_handle { + __u32 handle; + + /** Flags.. only applicable for handle->fd */ + __u32 flags; + + /** Returned dmabuf file descriptor */ + __s32 fd; +}; + +struct drm_syncobj_create { + __u32 handle; +#define DRM_SYNCOBJ_CREATE_SIGNALED (1 << 0) + __u32 flags; +}; + +struct drm_syncobj_destroy { + __u32 handle; + __u32 pad; +}; + +#define DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE (1 << 0) +#define DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE (1 << 0) +struct drm_syncobj_handle { + __u32 handle; + __u32 flags; + + __s32 fd; + __u32 pad; +}; + +struct drm_syncobj_transfer { + __u32 src_handle; + __u32 dst_handle; + __u64 src_point; + __u64 dst_point; + __u32 flags; + __u32 pad; +}; + +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL (1 << 0) +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT (1 << 1) +#define DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE (1 << 2) /* wait for time point to become available */ +struct drm_syncobj_wait { + __u64 handles; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + +struct drm_syncobj_timeline_wait { + __u64 handles; + /* wait on specific timeline point for every handles*/ + __u64 points; + /* absolute timeout */ + __s64 timeout_nsec; + __u32 count_handles; + __u32 flags; + __u32 first_signaled; /* only valid when not waiting all */ + __u32 pad; +}; + + +struct drm_syncobj_array { + __u64 handles; + __u32 count_handles; + __u32 pad; +}; + +struct drm_syncobj_timeline_array { + __u64 handles; + __u64 points; + __u32 count_handles; + __u32 pad; +}; + + +/* Query current scanout sequence number */ +struct drm_crtc_get_sequence { + __u32 crtc_id; /* requested crtc_id */ + __u32 active; /* return: crtc output is active */ + __u64 sequence; /* return: most recent vblank sequence */ + __s64 sequence_ns; /* return: most recent time of first pixel out */ +}; + +/* Queue event to be delivered at specified sequence. Time stamp marks + * when the first pixel of the refresh cycle leaves the display engine + * for the display + */ +#define DRM_CRTC_SEQUENCE_RELATIVE 0x00000001 /* sequence is relative to current */ +#define DRM_CRTC_SEQUENCE_NEXT_ON_MISS 0x00000002 /* Use next sequence if we've missed */ + +struct drm_crtc_queue_sequence { + __u32 crtc_id; + __u32 flags; + __u64 sequence; /* on input, target sequence. on output, actual sequence */ + __u64 user_data; /* user data passed to event */ +}; + +#if defined(__cplusplus) +} +#endif + +#include "drm_mode.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_IOCTL_BASE 'd' +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,type) _IOR(DRM_IOCTL_BASE,nr,type) +#define DRM_IOW(nr,type) _IOW(DRM_IOCTL_BASE,nr,type) +#define DRM_IOWR(nr,type) _IOWR(DRM_IOCTL_BASE,nr,type) + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, struct drm_version) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, struct drm_unique) +#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, struct drm_auth) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, struct drm_irq_busid) +#define DRM_IOCTL_GET_MAP DRM_IOWR(0x04, struct drm_map) +#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) +#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) +#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl) +#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, struct drm_gem_close) +#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, struct drm_gem_flink) +#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, struct drm_gem_open) +#define DRM_IOCTL_GET_CAP DRM_IOWR(0x0c, struct drm_get_cap) +#define DRM_IOCTL_SET_CLIENT_CAP DRM_IOW( 0x0d, struct drm_set_client_cap) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, struct drm_block) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, struct drm_block) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, struct drm_control) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, struct drm_map) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, struct drm_buf_desc) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, struct drm_buf_desc) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, struct drm_buf_info) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, struct drm_buf_map) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, struct drm_buf_free) + +#define DRM_IOCTL_RM_MAP DRM_IOW( 0x1b, struct drm_map) + +#define DRM_IOCTL_SET_SAREA_CTX DRM_IOW( 0x1c, struct drm_ctx_priv_map) +#define DRM_IOCTL_GET_SAREA_CTX DRM_IOWR(0x1d, struct drm_ctx_priv_map) + +#define DRM_IOCTL_SET_MASTER DRM_IO(0x1e) +#define DRM_IOCTL_DROP_MASTER DRM_IO(0x1f) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, struct drm_ctx) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, struct drm_ctx) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, struct drm_ctx) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, struct drm_ctx) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, struct drm_ctx) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, struct drm_ctx) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, struct drm_ctx_res) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, struct drm_draw) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, struct drm_draw) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, struct drm_dma) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, struct drm_lock) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, struct drm_lock) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, struct drm_lock) + +#define DRM_IOCTL_PRIME_HANDLE_TO_FD DRM_IOWR(0x2d, struct drm_prime_handle) +#define DRM_IOCTL_PRIME_FD_TO_HANDLE DRM_IOWR(0x2e, struct drm_prime_handle) + +#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) +#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) +#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, struct drm_agp_mode) +#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, struct drm_agp_info) +#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, struct drm_agp_buffer) +#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, struct drm_agp_binding) +#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, struct drm_agp_binding) + +#define DRM_IOCTL_SG_ALLOC DRM_IOWR(0x38, struct drm_scatter_gather) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, struct drm_scatter_gather) + +#define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) + +#define DRM_IOCTL_CRTC_GET_SEQUENCE DRM_IOWR(0x3b, struct drm_crtc_get_sequence) +#define DRM_IOCTL_CRTC_QUEUE_SEQUENCE DRM_IOWR(0x3c, struct drm_crtc_queue_sequence) + +#define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) + +#define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) +#define DRM_IOCTL_MODE_GETCRTC DRM_IOWR(0xA1, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_SETCRTC DRM_IOWR(0xA2, struct drm_mode_crtc) +#define DRM_IOCTL_MODE_CURSOR DRM_IOWR(0xA3, struct drm_mode_cursor) +#define DRM_IOCTL_MODE_GETGAMMA DRM_IOWR(0xA4, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_SETGAMMA DRM_IOWR(0xA5, struct drm_mode_crtc_lut) +#define DRM_IOCTL_MODE_GETENCODER DRM_IOWR(0xA6, struct drm_mode_get_encoder) +#define DRM_IOCTL_MODE_GETCONNECTOR DRM_IOWR(0xA7, struct drm_mode_get_connector) +#define DRM_IOCTL_MODE_ATTACHMODE DRM_IOWR(0xA8, struct drm_mode_mode_cmd) /* deprecated (never worked) */ +#define DRM_IOCTL_MODE_DETACHMODE DRM_IOWR(0xA9, struct drm_mode_mode_cmd) /* deprecated (never worked) */ + +#define DRM_IOCTL_MODE_GETPROPERTY DRM_IOWR(0xAA, struct drm_mode_get_property) +#define DRM_IOCTL_MODE_SETPROPERTY DRM_IOWR(0xAB, struct drm_mode_connector_set_property) +#define DRM_IOCTL_MODE_GETPROPBLOB DRM_IOWR(0xAC, struct drm_mode_get_blob) +#define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd) +#define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int) +#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip) +#define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd) + +#define DRM_IOCTL_MODE_CREATE_DUMB DRM_IOWR(0xB2, struct drm_mode_create_dumb) +#define DRM_IOCTL_MODE_MAP_DUMB DRM_IOWR(0xB3, struct drm_mode_map_dumb) +#define DRM_IOCTL_MODE_DESTROY_DUMB DRM_IOWR(0xB4, struct drm_mode_destroy_dumb) +#define DRM_IOCTL_MODE_GETPLANERESOURCES DRM_IOWR(0xB5, struct drm_mode_get_plane_res) +#define DRM_IOCTL_MODE_GETPLANE DRM_IOWR(0xB6, struct drm_mode_get_plane) +#define DRM_IOCTL_MODE_SETPLANE DRM_IOWR(0xB7, struct drm_mode_set_plane) +#define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2) +#define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) +#define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) +#define DRM_IOCTL_MODE_CURSOR2 DRM_IOWR(0xBB, struct drm_mode_cursor2) +#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBC, struct drm_mode_atomic) +#define DRM_IOCTL_MODE_CREATEPROPBLOB DRM_IOWR(0xBD, struct drm_mode_create_blob) +#define DRM_IOCTL_MODE_DESTROYPROPBLOB DRM_IOWR(0xBE, struct drm_mode_destroy_blob) + +#define DRM_IOCTL_SYNCOBJ_CREATE DRM_IOWR(0xBF, struct drm_syncobj_create) +#define DRM_IOCTL_SYNCOBJ_DESTROY DRM_IOWR(0xC0, struct drm_syncobj_destroy) +#define DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD DRM_IOWR(0xC1, struct drm_syncobj_handle) +#define DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE DRM_IOWR(0xC2, struct drm_syncobj_handle) +#define DRM_IOCTL_SYNCOBJ_WAIT DRM_IOWR(0xC3, struct drm_syncobj_wait) +#define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array) +#define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array) + +#define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease) +#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees) +#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease) +#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease) + +#define DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait) +#define DRM_IOCTL_SYNCOBJ_QUERY DRM_IOWR(0xCB, struct drm_syncobj_timeline_array) +#define DRM_IOCTL_SYNCOBJ_TRANSFER DRM_IOWR(0xCC, struct drm_syncobj_transfer) +#define DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL DRM_IOWR(0xCD, struct drm_syncobj_timeline_array) + +/** + * Device specific ioctls should only be in their respective headers + * The device specific ioctl range is from 0x40 to 0x9f. + * Generic IOCTLS restart at 0xA0. + * + * \sa drmCommandNone(), drmCommandRead(), drmCommandWrite(), and + * drmCommandReadWrite(). + */ +#define DRM_COMMAND_BASE 0x40 +#define DRM_COMMAND_END 0xA0 + +/** + * Header for events written back to userspace on the drm fd. The + * type defines the type of event, the length specifies the total + * length of the event (including the header), and user_data is + * typically a 64 bit value passed with the ioctl that triggered the + * event. A read on the drm fd will always only return complete + * events, that is, if for example the read buffer is 100 bytes, and + * there are two 64 byte events pending, only one will be returned. + * + * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and + * up are chipset specific. + */ +struct drm_event { + __u32 type; + __u32 length; +}; + +#define DRM_EVENT_VBLANK 0x01 +#define DRM_EVENT_FLIP_COMPLETE 0x02 +#define DRM_EVENT_CRTC_SEQUENCE 0x03 + +struct drm_event_vblank { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 crtc_id; /* 0 on older kernels that do not support this */ +}; + +/* Event delivered at sequence. Time stamp marks when the first pixel + * of the refresh cycle leaves the display engine for the display + */ +struct drm_event_crtc_sequence { + struct drm_event base; + __u64 user_data; + __s64 time_ns; + __u64 sequence; +}; + +/* typedef area */ +typedef struct drm_clip_rect drm_clip_rect_t; +typedef struct drm_drawable_info drm_drawable_info_t; +typedef struct drm_tex_region drm_tex_region_t; +typedef struct drm_hw_lock drm_hw_lock_t; +typedef struct drm_version drm_version_t; +typedef struct drm_unique drm_unique_t; +typedef struct drm_list drm_list_t; +typedef struct drm_block drm_block_t; +typedef struct drm_control drm_control_t; +typedef enum drm_map_type drm_map_type_t; +typedef enum drm_map_flags drm_map_flags_t; +typedef struct drm_ctx_priv_map drm_ctx_priv_map_t; +typedef struct drm_map drm_map_t; +typedef struct drm_client drm_client_t; +typedef enum drm_stat_type drm_stat_type_t; +typedef struct drm_stats drm_stats_t; +typedef enum drm_lock_flags drm_lock_flags_t; +typedef struct drm_lock drm_lock_t; +typedef enum drm_dma_flags drm_dma_flags_t; +typedef struct drm_buf_desc drm_buf_desc_t; +typedef struct drm_buf_info drm_buf_info_t; +typedef struct drm_buf_free drm_buf_free_t; +typedef struct drm_buf_pub drm_buf_pub_t; +typedef struct drm_buf_map drm_buf_map_t; +typedef struct drm_dma drm_dma_t; +typedef union drm_wait_vblank drm_wait_vblank_t; +typedef struct drm_agp_mode drm_agp_mode_t; +typedef enum drm_ctx_flags drm_ctx_flags_t; +typedef struct drm_ctx drm_ctx_t; +typedef struct drm_ctx_res drm_ctx_res_t; +typedef struct drm_draw drm_draw_t; +typedef struct drm_update_draw drm_update_draw_t; +typedef struct drm_auth drm_auth_t; +typedef struct drm_irq_busid drm_irq_busid_t; +typedef enum drm_vblank_seq_type drm_vblank_seq_type_t; + +typedef struct drm_agp_buffer drm_agp_buffer_t; +typedef struct drm_agp_binding drm_agp_binding_t; +typedef struct drm_agp_info drm_agp_info_t; +typedef struct drm_scatter_gather drm_scatter_gather_t; +typedef struct drm_set_version drm_set_version_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_fourcc.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_fourcc.h new file mode 100644 index 0000000..5c69090 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_fourcc.h @@ -0,0 +1,763 @@ +/* + * Copyright 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef DRM_FOURCC_H +#define DRM_FOURCC_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * DOC: overview + * + * In the DRM subsystem, framebuffer pixel formats are described using the + * fourcc codes defined in `include/uapi/drm/drm_fourcc.h`. In addition to the + * fourcc code, a Format Modifier may optionally be provided, in order to + * further describe the buffer's format - for example tiling or compression. + * + * Format Modifiers + * ---------------- + * + * Format modifiers are used in conjunction with a fourcc code, forming a + * unique fourcc:modifier pair. This format:modifier pair must fully define the + * format and data layout of the buffer, and should be the only way to describe + * that particular buffer. + * + * Having multiple fourcc:modifier pairs which describe the same layout should + * be avoided, as such aliases run the risk of different drivers exposing + * different names for the same data format, forcing userspace to understand + * that they are aliases. + * + * Format modifiers may change any property of the buffer, including the number + * of planes and/or the required allocation size. Format modifiers are + * vendor-namespaced, and as such the relationship between a fourcc code and a + * modifier is specific to the modifer being used. For example, some modifiers + * may preserve meaning - such as number of planes - from the fourcc code, + * whereas others may not. + * + * Vendors should document their modifier usage in as much detail as + * possible, to ensure maximum compatibility across devices, drivers and + * applications. + * + * The authoritative list of format modifier codes is found in + * `include/uapi/drm/drm_fourcc.h` + */ + +#define fourcc_code(a, b, c, d) ((__u32)(a) | ((__u32)(b) << 8) | \ + ((__u32)(c) << 16) | ((__u32)(d) << 24)) + +#define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ + +/* Reserve 0 for the invalid format specifier */ +#define DRM_FORMAT_INVALID 0 + +/* color index */ +#define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ + +/* 8 bpp Red */ +#define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ + +/* 16 bpp Red */ +#define DRM_FORMAT_R16 fourcc_code('R', '1', '6', ' ') /* [15:0] R little endian */ + +/* 16 bpp RG */ +#define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ +#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ + +/* 32 bpp RG */ +#define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ +#define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */ + +/* 8 bpp RGB */ +#define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ +#define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */ + +/* 16 bpp RGB */ +#define DRM_FORMAT_XRGB4444 fourcc_code('X', 'R', '1', '2') /* [15:0] x:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_XBGR4444 fourcc_code('X', 'B', '1', '2') /* [15:0] x:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBX4444 fourcc_code('R', 'X', '1', '2') /* [15:0] R:G:B:x 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRX4444 fourcc_code('B', 'X', '1', '2') /* [15:0] B:G:R:x 4:4:4:4 little endian */ + +#define DRM_FORMAT_ARGB4444 fourcc_code('A', 'R', '1', '2') /* [15:0] A:R:G:B 4:4:4:4 little endian */ +#define DRM_FORMAT_ABGR4444 fourcc_code('A', 'B', '1', '2') /* [15:0] A:B:G:R 4:4:4:4 little endian */ +#define DRM_FORMAT_RGBA4444 fourcc_code('R', 'A', '1', '2') /* [15:0] R:G:B:A 4:4:4:4 little endian */ +#define DRM_FORMAT_BGRA4444 fourcc_code('B', 'A', '1', '2') /* [15:0] B:G:R:A 4:4:4:4 little endian */ + +#define DRM_FORMAT_XRGB1555 fourcc_code('X', 'R', '1', '5') /* [15:0] x:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_XBGR1555 fourcc_code('X', 'B', '1', '5') /* [15:0] x:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBX5551 fourcc_code('R', 'X', '1', '5') /* [15:0] R:G:B:x 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRX5551 fourcc_code('B', 'X', '1', '5') /* [15:0] B:G:R:x 5:5:5:1 little endian */ + +#define DRM_FORMAT_ARGB1555 fourcc_code('A', 'R', '1', '5') /* [15:0] A:R:G:B 1:5:5:5 little endian */ +#define DRM_FORMAT_ABGR1555 fourcc_code('A', 'B', '1', '5') /* [15:0] A:B:G:R 1:5:5:5 little endian */ +#define DRM_FORMAT_RGBA5551 fourcc_code('R', 'A', '1', '5') /* [15:0] R:G:B:A 5:5:5:1 little endian */ +#define DRM_FORMAT_BGRA5551 fourcc_code('B', 'A', '1', '5') /* [15:0] B:G:R:A 5:5:5:1 little endian */ + +#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */ +#define DRM_FORMAT_BGR565 fourcc_code('B', 'G', '1', '6') /* [15:0] B:G:R 5:6:5 little endian */ + +/* 24 bpp RGB */ +#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */ +#define DRM_FORMAT_BGR888 fourcc_code('B', 'G', '2', '4') /* [23:0] B:G:R little endian */ + +/* 32 bpp RGB */ +#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBX8888 fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRX8888 fourcc_code('B', 'X', '2', '4') /* [31:0] B:G:R:x 8:8:8:8 little endian */ + +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') /* [31:0] A:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_RGBA8888 fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */ +#define DRM_FORMAT_BGRA8888 fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */ + +#define DRM_FORMAT_XRGB2101010 fourcc_code('X', 'R', '3', '0') /* [31:0] x:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_XBGR2101010 fourcc_code('X', 'B', '3', '0') /* [31:0] x:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBX1010102 fourcc_code('R', 'X', '3', '0') /* [31:0] R:G:B:x 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRX1010102 fourcc_code('B', 'X', '3', '0') /* [31:0] B:G:R:x 10:10:10:2 little endian */ + +#define DRM_FORMAT_ARGB2101010 fourcc_code('A', 'R', '3', '0') /* [31:0] A:R:G:B 2:10:10:10 little endian */ +#define DRM_FORMAT_ABGR2101010 fourcc_code('A', 'B', '3', '0') /* [31:0] A:B:G:R 2:10:10:10 little endian */ +#define DRM_FORMAT_RGBA1010102 fourcc_code('R', 'A', '3', '0') /* [31:0] R:G:B:A 10:10:10:2 little endian */ +#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') /* [31:0] B:G:R:A 10:10:10:2 little endian */ + +/* + * Floating point 64bpp RGB + * IEEE 754-2008 binary16 half-precision float + * [15:0] sign:exponent:mantissa 1:5:10 + */ +#define DRM_FORMAT_XRGB16161616F fourcc_code('X', 'R', '4', 'H') /* [63:0] x:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_XBGR16161616F fourcc_code('X', 'B', '4', 'H') /* [63:0] x:B:G:R 16:16:16:16 little endian */ + +#define DRM_FORMAT_ARGB16161616F fourcc_code('A', 'R', '4', 'H') /* [63:0] A:R:G:B 16:16:16:16 little endian */ +#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') /* [63:0] A:B:G:R 16:16:16:16 little endian */ + +/* packed YCbCr */ +#define DRM_FORMAT_YUYV fourcc_code('Y', 'U', 'Y', 'V') /* [31:0] Cr0:Y1:Cb0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_YVYU fourcc_code('Y', 'V', 'Y', 'U') /* [31:0] Cb0:Y1:Cr0:Y0 8:8:8:8 little endian */ +#define DRM_FORMAT_UYVY fourcc_code('U', 'Y', 'V', 'Y') /* [31:0] Y1:Cr0:Y0:Cb0 8:8:8:8 little endian */ +#define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ + +#define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_VUY888 fourcc_code('V', 'U', '2', '4') /* [23:0] Cr:Cb:Y 8:8:8 little endian */ +#define DRM_FORMAT_VUY101010 fourcc_code('V', 'U', '3', '0') /* Y followed by U then V, 10:10:10. Non-linear modifier only */ + +/* + * packed Y2xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb + */ +#define DRM_FORMAT_Y210 fourcc_code('Y', '2', '1', '0') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 10:6:10:6:10:6:10:6 little endian per 2 Y pixels */ +#define DRM_FORMAT_Y212 fourcc_code('Y', '2', '1', '2') /* [63:0] Cr0:0:Y1:0:Cb0:0:Y0:0 12:4:12:4:12:4:12:4 little endian per 2 Y pixels */ +#define DRM_FORMAT_Y216 fourcc_code('Y', '2', '1', '6') /* [63:0] Cr0:Y1:Cb0:Y0 16:16:16:16 little endian per 2 Y pixels */ + +/* + * packed Y4xx indicate for each component, xx valid data occupy msb + * 16-xx padding occupy lsb except Y410 + */ +#define DRM_FORMAT_Y410 fourcc_code('Y', '4', '1', '0') /* [31:0] A:Cr:Y:Cb 2:10:10:10 little endian */ +#define DRM_FORMAT_Y412 fourcc_code('Y', '4', '1', '2') /* [63:0] A:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ +#define DRM_FORMAT_Y416 fourcc_code('Y', '4', '1', '6') /* [63:0] A:Cr:Y:Cb 16:16:16:16 little endian */ + +#define DRM_FORMAT_XVYU2101010 fourcc_code('X', 'V', '3', '0') /* [31:0] X:Cr:Y:Cb 2:10:10:10 little endian */ +#define DRM_FORMAT_XVYU12_16161616 fourcc_code('X', 'V', '3', '6') /* [63:0] X:0:Cr:0:Y:0:Cb:0 12:4:12:4:12:4:12:4 little endian */ +#define DRM_FORMAT_XVYU16161616 fourcc_code('X', 'V', '4', '8') /* [63:0] X:Cr:Y:Cb 16:16:16:16 little endian */ + +/* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ +/* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_Y0L0 fourcc_code('Y', '0', 'L', '0') +/* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_X0L0 fourcc_code('X', '0', 'L', '0') + +/* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_Y0L2 fourcc_code('Y', '0', 'L', '2') +/* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_X0L2 fourcc_code('X', '0', 'L', '2') + +/* + * 1-plane YUV 4:2:0 + * In these formats, the component ordering is specified (Y, followed by U + * then V), but the exact Linear layout is undefined. + * These formats can only be used with a non-Linear modifier. + */ +#define DRM_FORMAT_YUV420_8BIT fourcc_code('Y', 'U', '0', '8') +#define DRM_FORMAT_YUV420_10BIT fourcc_code('Y', 'U', '1', '0') + +/* + * 2 plane RGB + A + * index 0 = RGB plane, same format as the corresponding non _A8 format has + * index 1 = A plane, [7:0] A + */ +#define DRM_FORMAT_XRGB8888_A8 fourcc_code('X', 'R', 'A', '8') +#define DRM_FORMAT_XBGR8888_A8 fourcc_code('X', 'B', 'A', '8') +#define DRM_FORMAT_RGBX8888_A8 fourcc_code('R', 'X', 'A', '8') +#define DRM_FORMAT_BGRX8888_A8 fourcc_code('B', 'X', 'A', '8') +#define DRM_FORMAT_RGB888_A8 fourcc_code('R', '8', 'A', '8') +#define DRM_FORMAT_BGR888_A8 fourcc_code('B', '8', 'A', '8') +#define DRM_FORMAT_RGB565_A8 fourcc_code('R', '5', 'A', '8') +#define DRM_FORMAT_BGR565_A8 fourcc_code('B', '5', 'A', '8') + +/* + * 2 plane YCbCr + * index 0 = Y plane, [7:0] Y + * index 1 = Cr:Cb plane, [15:0] Cr:Cb little endian + * or + * index 1 = Cb:Cr plane, [15:0] Cb:Cr little endian + */ +#define DRM_FORMAT_NV12 fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ +#define DRM_FORMAT_P210 fourcc_code('P', '2', '1', '0') /* 2x1 subsampled Cr:Cb plane, 10 bit per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [10:6] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [10:6:10:6] little endian + */ +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y:x [12:4] little endian + * index 1 = Cr:Cb plane, [31:0] Cr:x:Cb:x [12:4:12:4] little endian + */ +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane 12 bits per channel */ + +/* + * 2 plane YCbCr MSB aligned + * index 0 = Y plane, [15:0] Y little endian + * index 1 = Cr:Cb plane, [31:0] Cr:Cb [16:16] little endian + */ +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane 16 bits per channel */ + +/* + * 3 plane YCbCr + * index 0: Y plane, [7:0] Y + * index 1: Cb plane, [7:0] Cb + * index 2: Cr plane, [7:0] Cr + * or + * index 1: Cr plane, [7:0] Cr + * index 2: Cb plane, [7:0] Cb + */ +#define DRM_FORMAT_YUV410 fourcc_code('Y', 'U', 'V', '9') /* 4x4 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU410 fourcc_code('Y', 'V', 'U', '9') /* 4x4 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV411 fourcc_code('Y', 'U', '1', '1') /* 4x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU411 fourcc_code('Y', 'V', '1', '1') /* 4x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV420 fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU420 fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV422 fourcc_code('Y', 'U', '1', '6') /* 2x1 subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU422 fourcc_code('Y', 'V', '1', '6') /* 2x1 subsampled Cr (1) and Cb (2) planes */ +#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */ +#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */ + + +/* + * Format Modifiers: + * + * Format modifiers describe, typically, a re-ordering or modification + * of the data in a plane of an FB. This can be used to express tiled/ + * swizzled formats, or compression, or a combination of the two. + * + * The upper 8 bits of the format modifier are a vendor-id as assigned + * below. The lower 56 bits are assigned as vendor sees fit. + */ + +/* Vendor Ids: */ +#define DRM_FORMAT_MOD_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#define DRM_FORMAT_MOD_VENDOR_INTEL 0x01 +#define DRM_FORMAT_MOD_VENDOR_AMD 0x02 +#define DRM_FORMAT_MOD_VENDOR_NVIDIA 0x03 +#define DRM_FORMAT_MOD_VENDOR_SAMSUNG 0x04 +#define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 +#define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 +#define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 +#define DRM_FORMAT_MOD_VENDOR_ARM 0x08 +#define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 + +/* add more to the end as needed */ + +#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) + +#define fourcc_mod_code(vendor, val) \ + ((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | ((val) & 0x00ffffffffffffffULL)) + +/* + * Format Modifier tokens: + * + * When adding a new token please document the layout with a code comment, + * similar to the fourcc codes above. drm_fourcc.h is considered the + * authoritative source for all of these. + */ + +/* + * Invalid Modifier + * + * This modifier can be used as a sentinel to terminate the format modifiers + * list, or to initialize a variable with an invalid modifier. It might also be + * used to report an error back to userspace for certain APIs. + */ +#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) + +/* + * Linear Layout + * + * Just plain linear layout. Note that this is different from no specifying any + * modifier (e.g. not setting DRM_MODE_FB_MODIFIERS in the DRM_ADDFB2 ioctl), + * which tells the driver to also take driver-internal information into account + * and so might actually result in a tiled framebuffer. + */ +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) + +/* Intel framebuffer modifiers */ + +/* + * Intel X-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out row-major, with + * a platform-dependent stride. On top of that the memory can apply + * platform-depending swizzling of some higher address bits into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_X_TILED fourcc_mod_code(INTEL, 1) + +/* + * Intel Y-tiling layout + * + * This is a tiled layout using 4Kb tiles (except on gen2 where the tiles 2Kb) + * in row-major layout. Within the tile bytes are laid out in OWORD (16 bytes) + * chunks column-major, with a platform-dependent height. On top of that the + * memory can apply platform-depending swizzling of some higher address bits + * into bit6. + * + * This format is highly platforms specific and not useful for cross-driver + * sharing. It exists since on a given platform it does uniquely identify the + * layout in a simple way for i915-specific userspace. + */ +#define I915_FORMAT_MOD_Y_TILED fourcc_mod_code(INTEL, 2) + +/* + * Intel Yf-tiling layout + * + * This is a tiled layout using 4Kb tiles in row-major layout. + * Within the tile pixels are laid out in 16 256 byte units / sub-tiles which + * are arranged in four groups (two wide, two high) with column-major layout. + * Each group therefore consists out of four 256 byte units, which are also laid + * out as 2x2 column-major. + * 256 byte units are made out of four 64 byte blocks of pixels, producing + * either a square block or a 2:1 unit. + * 64 byte blocks of pixels contain four pixel rows of 16 bytes, where the width + * in pixel depends on the pixel depth. + */ +#define I915_FORMAT_MOD_Yf_TILED fourcc_mod_code(INTEL, 3) + +/* + * Intel color control surface (CCS) for render compression + * + * The framebuffer format must be one of the 8:8:8:8 RGB formats. + * The main surface will be plane index 0 and must be Y/Yf-tiled, + * the CCS will be plane index 1. + * + * Each CCS tile matches a 1024x512 pixel area of the main surface. + * To match certain aspects of the 3D hardware the CCS is + * considered to be made up of normal 128Bx32 Y tiles, Thus + * the CCS pitch must be specified in multiples of 128 bytes. + * + * In reality the CCS tile appears to be a 64Bx64 Y tile, composed + * of QWORD (8 bytes) chunks instead of OWORD (16 bytes) chunks. + * But that fact is not relevant unless the memory is accessed + * directly. + */ +#define I915_FORMAT_MOD_Y_TILED_CCS fourcc_mod_code(INTEL, 4) +#define I915_FORMAT_MOD_Yf_TILED_CCS fourcc_mod_code(INTEL, 5) + +/* + * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks + * + * Macroblocks are laid in a Z-shape, and each pixel data is following the + * standard NV12 style. + * As for NV12, an image is the result of two frame buffers: one for Y, + * one for the interleaved Cb/Cr components (1/2 the height of the Y buffer). + * Alignment requirements are (for each buffer): + * - multiple of 128 pixels for the width + * - multiple of 32 pixels for the height + * + * For more information: see https://linuxtv.org/downloads/v4l-dvb-apis/re32.html + */ +#define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) + +/* + * Tiled, 16 (pixels) x 16 (lines) - sized macroblocks + * + * This is a simple tiled layout using tiles of 16x16 pixels in a row-major + * layout. For YCbCr formats Cb/Cr components are taken in such a way that + * they correspond to their 16x16 luma block. + */ +#define DRM_FORMAT_MOD_SAMSUNG_16_16_TILE fourcc_mod_code(SAMSUNG, 2) + +/* + * Qualcomm Compressed Format + * + * Refers to a compressed variant of the base format that is compressed. + * Implementation may be platform and base-format specific. + * + * Each macrotile consists of m x n (mostly 4 x 4) tiles. + * Pixel data pitch/stride is aligned with macrotile width. + * Pixel data height is aligned with macrotile height. + * Entire pixel data buffer is aligned with 4k(bytes). + */ +#define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1) + +/* Vivante framebuffer modifiers */ + +/* + * Vivante 4x4 tiling layout + * + * This is a simple tiled layout using tiles of 4x4 pixels in a row-major + * layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_TILED fourcc_mod_code(VIVANTE, 1) + +/* + * Vivante 64x64 super-tiling layout + * + * This is a tiled layout using 64x64 pixel super-tiles, where each super-tile + * contains 8x4 groups of 2x4 tiles of 4x4 pixels (like above) each, all in row- + * major layout. + * + * For more information: see + * https://github.com/etnaviv/etna_viv/blob/master/doc/hardware.md#texture-tiling + */ +#define DRM_FORMAT_MOD_VIVANTE_SUPER_TILED fourcc_mod_code(VIVANTE, 2) + +/* + * Vivante 4x4 tiling layout for dual-pipe + * + * Same as the 4x4 tiling layout, except every second 4x4 pixel tile starts at a + * different base address. Offsets from the base addresses are therefore halved + * compared to the non-split tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED fourcc_mod_code(VIVANTE, 3) + +/* + * Vivante 64x64 super-tiling layout for dual-pipe + * + * Same as the 64x64 super-tiling layout, except every second 4x4 pixel tile + * starts at a different base address. Offsets from the base addresses are + * therefore halved compared to the non-split super-tiled layout. + */ +#define DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED fourcc_mod_code(VIVANTE, 4) + +/* NVIDIA frame buffer modifiers */ + +/* + * Tegra Tiled Layout, used by Tegra 2, 3 and 4. + * + * Pixels are arranged in simple tiles of 16 x 16 bytes. + */ +#define DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED fourcc_mod_code(NVIDIA, 1) + +/* + * 16Bx2 Block Linear layout, used by desktop GPUs, and Tegra K1 and later + * + * Pixels are arranged in 64x8 Groups Of Bytes (GOBs). GOBs are then stacked + * vertically by a power of 2 (1 to 32 GOBs) to form a block. + * + * Within a GOB, data is ordered as 16B x 2 lines sectors laid in Z-shape. + * + * Parameter 'v' is the log2 encoding of the number of GOBs stacked vertically. + * Valid values are: + * + * 0 == ONE_GOB + * 1 == TWO_GOBS + * 2 == FOUR_GOBS + * 3 == EIGHT_GOBS + * 4 == SIXTEEN_GOBS + * 5 == THIRTYTWO_GOBS + * + * Chapter 20 "Pixel Memory Formats" of the Tegra X1 TRM describes this format + * in full detail. + */ +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(v) \ + fourcc_mod_code(NVIDIA, 0x10 | ((v) & 0xf)) + +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB \ + fourcc_mod_code(NVIDIA, 0x10) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB \ + fourcc_mod_code(NVIDIA, 0x11) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB \ + fourcc_mod_code(NVIDIA, 0x12) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB \ + fourcc_mod_code(NVIDIA, 0x13) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB \ + fourcc_mod_code(NVIDIA, 0x14) +#define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ + fourcc_mod_code(NVIDIA, 0x15) + +/* + * Some Broadcom modifiers take parameters, for example the number of + * vertical lines in the image. Reserve the lower 32 bits for modifier + * type, and the next 24 bits for parameters. Top 8 bits are the + * vendor code. + */ +#define __fourcc_mod_broadcom_param_shift 8 +#define __fourcc_mod_broadcom_param_bits 48 +#define fourcc_mod_broadcom_code(val, params) \ + fourcc_mod_code(BROADCOM, ((((__u64)params) << __fourcc_mod_broadcom_param_shift) | val)) +#define fourcc_mod_broadcom_param(m) \ + ((int)(((m) >> __fourcc_mod_broadcom_param_shift) & \ + ((1ULL << __fourcc_mod_broadcom_param_bits) - 1))) +#define fourcc_mod_broadcom_mod(m) \ + ((m) & ~(((1ULL << __fourcc_mod_broadcom_param_bits) - 1) << \ + __fourcc_mod_broadcom_param_shift)) + +/* + * Broadcom VC4 "T" format + * + * This is the primary layout that the V3D GPU can texture from (it + * can't do linear). The T format has: + * + * - 64b utiles of pixels in a raster-order grid according to cpp. It's 4x4 + * pixels at 32 bit depth. + * + * - 1k subtiles made of a 4x4 raster-order grid of 64b utiles (so usually + * 16x16 pixels). + * + * - 4k tiles made of a 2x2 grid of 1k subtiles (so usually 32x32 pixels). On + * even 4k tile rows, they're arranged as (BL, TL, TR, BR), and on odd rows + * they're (TR, BR, BL, TL), where bottom left is start of memory. + * + * - an image made of 4k tiles in rows either left-to-right (even rows of 4k + * tiles) or right-to-left (odd rows of 4k tiles). + */ +#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) + +/* + * Broadcom SAND format + * + * This is the native format that the H.264 codec block uses. For VC4 + * HVS, it is only valid for H.264 (NV12/21) and RGBA modes. + * + * The image can be considered to be split into columns, and the + * columns are placed consecutively into memory. The width of those + * columns can be either 32, 64, 128, or 256 pixels, but in practice + * only 128 pixel columns are used. + * + * The pitch between the start of each column is set to optimally + * switch between SDRAM banks. This is passed as the number of lines + * of column width in the modifier (we can't use the stride value due + * to various core checks that look at it , so you should set the + * stride to width*cpp). + * + * Note that the column height for this format modifier is the same + * for all of the planes, assuming that each column contains both Y + * and UV. Some SAND-using hardware stores UV in a separate tiled + * image from Y to reduce the column height, which is not supported + * with these modifiers. + */ + +#define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(2, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(3, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(4, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(5, v) + +#define DRM_FORMAT_MOD_BROADCOM_SAND32 \ + DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND64 \ + DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND128 \ + DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND256 \ + DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(0) + +/* Broadcom UIF format + * + * This is the common format for the current Broadcom multimedia + * blocks, including V3D 3.x and newer, newer video codecs, and + * displays. + * + * The image consists of utiles (64b blocks), UIF blocks (2x2 utiles), + * and macroblocks (4x4 UIF blocks). Those 4x4 UIF block groups are + * stored in columns, with padding between the columns to ensure that + * moving from one column to the next doesn't hit the same SDRAM page + * bank. + * + * To calculate the padding, it is assumed that each hardware block + * and the software driving it knows the platform's SDRAM page size, + * number of banks, and XOR address, and that it's identical between + * all blocks using the format. This tiling modifier will use XOR as + * necessary to reduce the padding. If a hardware block can't do XOR, + * the assumption is that a no-XOR tiling modifier will be created. + */ +#define DRM_FORMAT_MOD_BROADCOM_UIF fourcc_mod_code(BROADCOM, 6) + +/* + * Arm Framebuffer Compression (AFBC) modifiers + * + * AFBC is a proprietary lossless image compression protocol and format. + * It provides fine-grained random access and minimizes the amount of data + * transferred between IP blocks. + * + * AFBC has several features which may be supported and/or used, which are + * represented using bits in the modifier. Not all combinations are valid, + * and different devices or use-cases may support different combinations. + * + * Further information on the use of AFBC modifiers can be found in + * Documentation/gpu/afbc.rst + */ +#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) + +/* + * AFBC superblock size + * + * Indicates the superblock size(s) used for the AFBC buffer. The buffer + * size (in pixels) must be aligned to a multiple of the superblock size. + * Four lowest significant bits(LSBs) are reserved for block size. + * + * Where one superblock size is specified, it applies to all planes of the + * buffer (e.g. 16x16, 32x8). When multiple superblock sizes are specified, + * the first applies to the Luma plane and the second applies to the Chroma + * plane(s). e.g. (32x8_64x4 means 32x8 Luma, with 64x4 Chroma). + * Multiple superblock sizes are only valid for multi-plane YCbCr formats. + */ +#define AFBC_FORMAT_MOD_BLOCK_SIZE_MASK 0xf +#define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 (2ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_64x4 (3ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8_64x4 (4ULL) + +/* + * AFBC lossless colorspace transform + * + * Indicates that the buffer makes use of the AFBC lossless colorspace + * transform. + */ +#define AFBC_FORMAT_MOD_YTR (1ULL << 4) + +/* + * AFBC block-split + * + * Indicates that the payload of each superblock is split. The second + * half of the payload is positioned at a predefined offset from the start + * of the superblock payload. + */ +#define AFBC_FORMAT_MOD_SPLIT (1ULL << 5) + +/* + * AFBC sparse layout + * + * This flag indicates that the payload of each superblock must be stored at a + * predefined position relative to the other superblocks in the same AFBC + * buffer. This order is the same order used by the header buffer. In this mode + * each superblock is given the same amount of space as an uncompressed + * superblock of the particular format would require, rounding up to the next + * multiple of 128 bytes in size. + */ +#define AFBC_FORMAT_MOD_SPARSE (1ULL << 6) + +/* + * AFBC copy-block restrict + * + * Buffers with this flag must obey the copy-block restriction. The restriction + * is such that there are no copy-blocks referring across the border of 8x8 + * blocks. For the subsampled data the 8x8 limitation is also subsampled. + */ +#define AFBC_FORMAT_MOD_CBR (1ULL << 7) + +/* + * AFBC tiled layout + * + * The tiled layout groups superblocks in 8x8 or 4x4 tiles, where all + * superblocks inside a tile are stored together in memory. 8x8 tiles are used + * for pixel formats up to and including 32 bpp while 4x4 tiles are used for + * larger bpp formats. The order between the tiles is scan line. + * When the tiled layout is used, the buffer size (in pixels) must be aligned + * to the tile size. + */ +#define AFBC_FORMAT_MOD_TILED (1ULL << 8) + +/* + * AFBC solid color blocks + * + * Indicates that the buffer makes use of solid-color blocks, whereby bandwidth + * can be reduced if a whole superblock is a single color. + */ +#define AFBC_FORMAT_MOD_SC (1ULL << 9) + +/* + * AFBC double-buffer + * + * Indicates that the buffer is allocated in a layout safe for front-buffer + * rendering. + */ +#define AFBC_FORMAT_MOD_DB (1ULL << 10) + +/* + * AFBC buffer content hints + * + * Indicates that the buffer includes per-superblock content hints. + */ +#define AFBC_FORMAT_MOD_BCH (1ULL << 11) + +/* + * Allwinner tiled modifier + * + * This tiling mode is implemented by the VPU found on all Allwinner platforms, + * codenamed sunxi. It is associated with a YUV format that uses either 2 or 3 + * planes. + * + * With this tiling, the luminance samples are disposed in tiles representing + * 32x32 pixels and the chrominance samples in tiles representing 32x64 pixels. + * The pixel order in each tile is linear and the tiles are disposed linearly, + * both in row-major order. + */ +#define DRM_FORMAT_MOD_ALLWINNER_TILED fourcc_mod_code(ALLWINNER, 1) + +#if defined(__cplusplus) +} +#endif + +#endif /* DRM_FOURCC_H */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_mode.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_mode.h new file mode 100644 index 0000000..5fe6c64 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_mode.h @@ -0,0 +1,914 @@ +/* + * Copyright (c) 2007 Dave Airlie + * Copyright (c) 2007 Jakob Bornecrantz + * Copyright (c) 2008 Red Hat Inc. + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * Copyright (c) 2007-2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _DRM_MODE_H +#define _DRM_MODE_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) /* deprecated */ +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) /* deprecated */ +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) /* deprecated */ +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +#define DRM_MODE_TYPE_ALL (DRM_MODE_TYPE_PREFERRED | \ + DRM_MODE_TYPE_USERDEF | \ + DRM_MODE_TYPE_DRIVER) + +/* Video mode flags */ +/* bit compatible with the xrandr RR_ definitions (bits 0-13) + * + * ABI warning: Existing userspace really expects + * the mode flags to match the xrandr definitions. Any + * changes that don't match the xrandr definitions will + * likely need a new client cap or some other mechanism + * to avoid breaking existing userspace. This includes + * allocating new flags in the previously unused bits! + */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) /* deprecated */ +#define DRM_MODE_FLAG_PIXMUX (1<<11) /* deprecated */ +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) + /* + * When adding a new stereo mode don't forget to adjust DRM_MODE_FLAGS_3D_MAX + * (define not exposed to user space). + */ +#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) +#define DRM_MODE_FLAG_3D_NONE (0<<14) +#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) +#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) +#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) +#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + +/* Picture aspect ratio options */ +#define DRM_MODE_PICTURE_ASPECT_NONE 0 +#define DRM_MODE_PICTURE_ASPECT_4_3 1 +#define DRM_MODE_PICTURE_ASPECT_16_9 2 +#define DRM_MODE_PICTURE_ASPECT_64_27 3 +#define DRM_MODE_PICTURE_ASPECT_256_135 4 + +/* Content type options */ +#define DRM_MODE_CONTENT_TYPE_NO_DATA 0 +#define DRM_MODE_CONTENT_TYPE_GRAPHICS 1 +#define DRM_MODE_CONTENT_TYPE_PHOTO 2 +#define DRM_MODE_CONTENT_TYPE_CINEMA 3 +#define DRM_MODE_CONTENT_TYPE_GAME 4 + +/* Aspect ratio flag bitmask (4 bits 22:19) */ +#define DRM_MODE_FLAG_PIC_AR_MASK (0x0F<<19) +#define DRM_MODE_FLAG_PIC_AR_NONE \ + (DRM_MODE_PICTURE_ASPECT_NONE<<19) +#define DRM_MODE_FLAG_PIC_AR_4_3 \ + (DRM_MODE_PICTURE_ASPECT_4_3<<19) +#define DRM_MODE_FLAG_PIC_AR_16_9 \ + (DRM_MODE_PICTURE_ASPECT_16_9<<19) +#define DRM_MODE_FLAG_PIC_AR_64_27 \ + (DRM_MODE_PICTURE_ASPECT_64_27<<19) +#define DRM_MODE_FLAG_PIC_AR_256_135 \ + (DRM_MODE_PICTURE_ASPECT_256_135<<19) + +#define DRM_MODE_FLAG_ALL (DRM_MODE_FLAG_PHSYNC | \ + DRM_MODE_FLAG_NHSYNC | \ + DRM_MODE_FLAG_PVSYNC | \ + DRM_MODE_FLAG_NVSYNC | \ + DRM_MODE_FLAG_INTERLACE | \ + DRM_MODE_FLAG_DBLSCAN | \ + DRM_MODE_FLAG_CSYNC | \ + DRM_MODE_FLAG_PCSYNC | \ + DRM_MODE_FLAG_NCSYNC | \ + DRM_MODE_FLAG_HSKEW | \ + DRM_MODE_FLAG_DBLCLK | \ + DRM_MODE_FLAG_CLKDIV2 | \ + DRM_MODE_FLAG_3D_MASK) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NONE 0 /* Unmodified timing (display or + software can still scale) */ +#define DRM_MODE_SCALE_FULLSCREEN 1 /* Full screen, ignore aspect */ +#define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ +#define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 +#define DRM_MODE_DITHERING_AUTO 2 + +/* Dirty info options */ +#define DRM_MODE_DIRTY_OFF 0 +#define DRM_MODE_DIRTY_ON 1 +#define DRM_MODE_DIRTY_ANNOTATE 2 + +/* Link Status options */ +#define DRM_MODE_LINK_STATUS_GOOD 0 +#define DRM_MODE_LINK_STATUS_BAD 1 + +/* + * DRM_MODE_ROTATE_ + * + * Signals that a drm plane is been rotated degrees in counter + * clockwise direction. + * + * This define is provided as a convenience, looking up the property id + * using the name->prop id lookup is the preferred method. + */ +#define DRM_MODE_ROTATE_0 (1<<0) +#define DRM_MODE_ROTATE_90 (1<<1) +#define DRM_MODE_ROTATE_180 (1<<2) +#define DRM_MODE_ROTATE_270 (1<<3) + +/* + * DRM_MODE_ROTATE_MASK + * + * Bitmask used to look for drm plane rotations. + */ +#define DRM_MODE_ROTATE_MASK (\ + DRM_MODE_ROTATE_0 | \ + DRM_MODE_ROTATE_90 | \ + DRM_MODE_ROTATE_180 | \ + DRM_MODE_ROTATE_270) + +/* + * DRM_MODE_REFLECT_ + * + * Signals that the contents of a drm plane is reflected along the axis, + * in the same way as mirroring. + * See kerneldoc chapter "Plane Composition Properties" for more details. + * + * This define is provided as a convenience, looking up the property id + * using the name->prop id lookup is the preferred method. + */ +#define DRM_MODE_REFLECT_X (1<<4) +#define DRM_MODE_REFLECT_Y (1<<5) + +/* + * DRM_MODE_REFLECT_MASK + * + * Bitmask used to look for drm plane reflections. + */ +#define DRM_MODE_REFLECT_MASK (\ + DRM_MODE_REFLECT_X | \ + DRM_MODE_REFLECT_Y) + +/* Content Protection Flags */ +#define DRM_MODE_CONTENT_PROTECTION_UNDESIRED 0 +#define DRM_MODE_CONTENT_PROTECTION_DESIRED 1 +#define DRM_MODE_CONTENT_PROTECTION_ENABLED 2 + +struct drm_mode_modeinfo { + __u32 clock; + __u16 hdisplay; + __u16 hsync_start; + __u16 hsync_end; + __u16 htotal; + __u16 hskew; + __u16 vdisplay; + __u16 vsync_start; + __u16 vsync_end; + __u16 vtotal; + __u16 vscan; + + __u32 vrefresh; + + __u32 flags; + __u32 type; + char name[DRM_DISPLAY_MODE_LEN]; +}; + +struct drm_mode_card_res { + __u64 fb_id_ptr; + __u64 crtc_id_ptr; + __u64 connector_id_ptr; + __u64 encoder_id_ptr; + __u32 count_fbs; + __u32 count_crtcs; + __u32 count_connectors; + __u32 count_encoders; + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; +}; + +struct drm_mode_crtc { + __u64 set_connectors_ptr; + __u32 count_connectors; + + __u32 crtc_id; /**< Id */ + __u32 fb_id; /**< Id of framebuffer */ + + __u32 x; /**< x Position on the framebuffer */ + __u32 y; /**< y Position on the framebuffer */ + + __u32 gamma_size; + __u32 mode_valid; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_PRESENT_TOP_FIELD (1<<0) +#define DRM_MODE_PRESENT_BOTTOM_FIELD (1<<1) + +/* Planes blend with or override other bits on the CRTC */ +struct drm_mode_set_plane { + __u32 plane_id; + __u32 crtc_id; + __u32 fb_id; /* fb object contains surface format type */ + __u32 flags; /* see above flags */ + + /* Signed dest location allows it to be partially off screen */ + __s32 crtc_x; + __s32 crtc_y; + __u32 crtc_w; + __u32 crtc_h; + + /* Source values are 16.16 fixed point */ + __u32 src_x; + __u32 src_y; + __u32 src_h; + __u32 src_w; +}; + +struct drm_mode_get_plane { + __u32 plane_id; + + __u32 crtc_id; + __u32 fb_id; + + __u32 possible_crtcs; + __u32 gamma_size; + + __u32 count_format_types; + __u64 format_type_ptr; +}; + +struct drm_mode_get_plane_res { + __u64 plane_id_ptr; + __u32 count_planes; +}; + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 +#define DRM_MODE_ENCODER_VIRTUAL 5 +#define DRM_MODE_ENCODER_DSI 6 +#define DRM_MODE_ENCODER_DPMST 7 +#define DRM_MODE_ENCODER_DPI 8 + +struct drm_mode_get_encoder { + __u32 encoder_id; + __u32 encoder_type; + + __u32 crtc_id; /**< Id of crtc */ + + __u32 possible_crtcs; + __u32 possible_clones; +}; + +/* This is for connectors with multiple signal types. */ +/* Try to match DRM_MODE_CONNECTOR_X as closely as possible. */ +enum drm_mode_subconnector { + DRM_MODE_SUBCONNECTOR_Automatic = 0, + DRM_MODE_SUBCONNECTOR_Unknown = 0, + DRM_MODE_SUBCONNECTOR_DVID = 3, + DRM_MODE_SUBCONNECTOR_DVIA = 4, + DRM_MODE_SUBCONNECTOR_Composite = 5, + DRM_MODE_SUBCONNECTOR_SVIDEO = 6, + DRM_MODE_SUBCONNECTOR_Component = 8, + DRM_MODE_SUBCONNECTOR_SCART = 9, +}; + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 +#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_eDP 14 +#define DRM_MODE_CONNECTOR_VIRTUAL 15 +#define DRM_MODE_CONNECTOR_DSI 16 +#define DRM_MODE_CONNECTOR_DPI 17 +#define DRM_MODE_CONNECTOR_WRITEBACK 18 + +struct drm_mode_get_connector { + + __u64 encoders_ptr; + __u64 modes_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + + __u32 count_modes; + __u32 count_props; + __u32 count_encoders; + + __u32 encoder_id; /**< Current Encoder */ + __u32 connector_id; /**< Id */ + __u32 connector_type; + __u32 connector_type_id; + + __u32 connection; + __u32 mm_width; /**< width in millimeters */ + __u32 mm_height; /**< height in millimeters */ + __u32 subpixel; + + __u32 pad; +}; + +#define DRM_MODE_PROP_PENDING (1<<0) /* deprecated, do not use */ +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) +#define DRM_MODE_PROP_BITMASK (1<<5) /* bitmask of enumerated types */ + +/* non-extended types: legacy bitmask, one bit per type: */ +#define DRM_MODE_PROP_LEGACY_TYPE ( \ + DRM_MODE_PROP_RANGE | \ + DRM_MODE_PROP_ENUM | \ + DRM_MODE_PROP_BLOB | \ + DRM_MODE_PROP_BITMASK) + +/* extended-types: rather than continue to consume a bit per type, + * grab a chunk of the bits to use as integer type id. + */ +#define DRM_MODE_PROP_EXTENDED_TYPE 0x0000ffc0 +#define DRM_MODE_PROP_TYPE(n) ((n) << 6) +#define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) +#define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) + +/* the PROP_ATOMIC flag is used to hide properties from userspace that + * is not aware of atomic properties. This is mostly to work around + * older userspace (DDX drivers) that read/write each prop they find, + * without being aware that this could be triggering a lengthy modeset. + */ +#define DRM_MODE_PROP_ATOMIC 0x80000000 + +struct drm_mode_property_enum { + __u64 value; + char name[DRM_PROP_NAME_LEN]; +}; + +struct drm_mode_get_property { + __u64 values_ptr; /* values and blob lengths */ + __u64 enum_blob_ptr; /* enum and blob id ptrs */ + + __u32 prop_id; + __u32 flags; + char name[DRM_PROP_NAME_LEN]; + + __u32 count_values; + /* This is only used to count enum values, not blobs. The _blobs is + * simply because of a historical reason, i.e. backwards compat. */ + __u32 count_enum_blobs; +}; + +struct drm_mode_connector_set_property { + __u64 value; + __u32 prop_id; + __u32 connector_id; +}; + +#define DRM_MODE_OBJECT_CRTC 0xcccccccc +#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 +#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0 +#define DRM_MODE_OBJECT_MODE 0xdededede +#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0 +#define DRM_MODE_OBJECT_FB 0xfbfbfbfb +#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb +#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee +#define DRM_MODE_OBJECT_ANY 0 + +struct drm_mode_obj_get_properties { + __u64 props_ptr; + __u64 prop_values_ptr; + __u32 count_props; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_obj_set_property { + __u64 value; + __u32 prop_id; + __u32 obj_id; + __u32 obj_type; +}; + +struct drm_mode_get_blob { + __u32 blob_id; + __u32 length; + __u64 data; +}; + +struct drm_mode_fb_cmd { + __u32 fb_id; + __u32 width; + __u32 height; + __u32 pitch; + __u32 bpp; + __u32 depth; + /* driver specific handle */ + __u32 handle; +}; + +#define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ +#define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[] */ + +struct drm_mode_fb_cmd2 { + __u32 fb_id; + __u32 width; + __u32 height; + __u32 pixel_format; /* fourcc code from drm_fourcc.h */ + __u32 flags; /* see above flags */ + + /* + * In case of planar formats, this ioctl allows up to 4 + * buffer objects with offsets and pitches per plane. + * The pitch and offset order is dictated by the fourcc, + * e.g. NV12 (http://fourcc.org/yuv.php#NV12) is described as: + * + * YUV 4:2:0 image with a plane of 8 bit Y samples + * followed by an interleaved U/V plane containing + * 8 bit 2x2 subsampled colour difference samples. + * + * So it would consist of Y as offsets[0] and UV as + * offsets[1]. Note that offsets[0] will generally + * be 0 (but this is not required). + * + * To accommodate tiled, compressed, etc formats, a + * modifier can be specified. The default value of zero + * indicates "native" format as specified by the fourcc. + * Vendor specific modifier token. Note that even though + * it looks like we have a modifier per-plane, we in fact + * do not. The modifier for each plane must be identical. + * Thus all combinations of different data layouts for + * multi plane formats must be enumerated as separate + * modifiers. + */ + __u32 handles[4]; + __u32 pitches[4]; /* pitch for each plane */ + __u32 offsets[4]; /* offset of each plane */ + __u64 modifier[4]; /* ie, tiling, compress */ +}; + +#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 +#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02 +#define DRM_MODE_FB_DIRTY_FLAGS 0x03 + +#define DRM_MODE_FB_DIRTY_MAX_CLIPS 256 + +/* + * Mark a region of a framebuffer as dirty. + * + * Some hardware does not automatically update display contents + * as a hardware or software draw to a framebuffer. This ioctl + * allows userspace to tell the kernel and the hardware what + * regions of the framebuffer have changed. + * + * The kernel or hardware is free to update more then just the + * region specified by the clip rects. The kernel or hardware + * may also delay and/or coalesce several calls to dirty into a + * single update. + * + * Userspace may annotate the updates, the annotates are a + * promise made by the caller that the change is either a copy + * of pixels or a fill of a single color in the region specified. + * + * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then + * the number of updated regions are half of num_clips given, + * where the clip rects are paired in src and dst. The width and + * height of each one of the pairs must match. + * + * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller + * promises that the region specified of the clip rects is filled + * completely with a single color as given in the color argument. + */ + +struct drm_mode_fb_dirty_cmd { + __u32 fb_id; + __u32 flags; + __u32 color; + __u32 num_clips; + __u64 clips_ptr; +}; + +struct drm_mode_mode_cmd { + __u32 connector_id; + struct drm_mode_modeinfo mode; +}; + +#define DRM_MODE_CURSOR_BO 0x01 +#define DRM_MODE_CURSOR_MOVE 0x02 +#define DRM_MODE_CURSOR_FLAGS 0x03 + +/* + * depending on the value in flags different members are used. + * + * CURSOR_BO uses + * crtc_id + * width + * height + * handle - if 0 turns the cursor off + * + * CURSOR_MOVE uses + * crtc_id + * x + * y + */ +struct drm_mode_cursor { + __u32 flags; + __u32 crtc_id; + __s32 x; + __s32 y; + __u32 width; + __u32 height; + /* driver specific handle */ + __u32 handle; +}; + +struct drm_mode_cursor2 { + __u32 flags; + __u32 crtc_id; + __s32 x; + __s32 y; + __u32 width; + __u32 height; + /* driver specific handle */ + __u32 handle; + __s32 hot_x; + __s32 hot_y; +}; + +struct drm_mode_crtc_lut { + __u32 crtc_id; + __u32 gamma_size; + + /* pointers to arrays */ + __u64 red; + __u64 green; + __u64 blue; +}; + +struct drm_color_ctm { + /* + * Conversion matrix in S31.32 sign-magnitude + * (not two's complement!) format. + */ + __u64 matrix[9]; +}; + +struct drm_color_lut { + /* + * Values are mapped linearly to 0.0 - 1.0 range, with 0x0 == 0.0 and + * 0xffff == 1.0. + */ + __u16 red; + __u16 green; + __u16 blue; + __u16 reserved; +}; + +#define DRM_MODE_PAGE_FLIP_EVENT 0x01 +#define DRM_MODE_PAGE_FLIP_ASYNC 0x02 +#define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4 +#define DRM_MODE_PAGE_FLIP_TARGET_RELATIVE 0x8 +#define DRM_MODE_PAGE_FLIP_TARGET (DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE | \ + DRM_MODE_PAGE_FLIP_TARGET_RELATIVE) +#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT | \ + DRM_MODE_PAGE_FLIP_ASYNC | \ + DRM_MODE_PAGE_FLIP_TARGET) + +/* + * Request a page flip on the specified crtc. + * + * This ioctl will ask KMS to schedule a page flip for the specified + * crtc. Once any pending rendering targeting the specified fb (as of + * ioctl time) has completed, the crtc will be reprogrammed to display + * that fb after the next vertical refresh. The ioctl returns + * immediately, but subsequent rendering to the current fb will block + * in the execbuffer ioctl until the page flip happens. If a page + * flip is already pending as the ioctl is called, EBUSY will be + * returned. + * + * Flag DRM_MODE_PAGE_FLIP_EVENT requests that drm sends back a vblank + * event (see drm.h: struct drm_event_vblank) when the page flip is + * done. The user_data field passed in with this ioctl will be + * returned as the user_data field in the vblank event struct. + * + * Flag DRM_MODE_PAGE_FLIP_ASYNC requests that the flip happen + * 'as soon as possible', meaning that it not delay waiting for vblank. + * This may cause tearing on the screen. + * + * The reserved field must be zero. + */ + +struct drm_mode_crtc_page_flip { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 reserved; + __u64 user_data; +}; + +/* + * Request a page flip on the specified crtc. + * + * Same as struct drm_mode_crtc_page_flip, but supports new flags and + * re-purposes the reserved field: + * + * The sequence field must be zero unless either of the + * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is specified. When + * the ABSOLUTE flag is specified, the sequence field denotes the absolute + * vblank sequence when the flip should take effect. When the RELATIVE + * flag is specified, the sequence field denotes the relative (to the + * current one when the ioctl is called) vblank sequence when the flip + * should take effect. NOTE: DRM_IOCTL_WAIT_VBLANK must still be used to + * make sure the vblank sequence before the target one has passed before + * calling this ioctl. The purpose of the + * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is merely to clarify + * the target for when code dealing with a page flip runs during a + * vertical blank period. + */ + +struct drm_mode_crtc_page_flip_target { + __u32 crtc_id; + __u32 fb_id; + __u32 flags; + __u32 sequence; + __u64 user_data; +}; + +/* create a dumb scanout buffer */ +struct drm_mode_create_dumb { + __u32 height; + __u32 width; + __u32 bpp; + __u32 flags; + /* handle, pitch, size will be returned */ + __u32 handle; + __u32 pitch; + __u64 size; +}; + +/* set up for mmap of a dumb scanout buffer */ +struct drm_mode_map_dumb { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_mode_destroy_dumb { + __u32 handle; +}; + +/* page-flip flags are valid, plus: */ +#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 +#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 + +#define DRM_MODE_ATOMIC_FLAGS (\ + DRM_MODE_PAGE_FLIP_EVENT |\ + DRM_MODE_PAGE_FLIP_ASYNC |\ + DRM_MODE_ATOMIC_TEST_ONLY |\ + DRM_MODE_ATOMIC_NONBLOCK |\ + DRM_MODE_ATOMIC_ALLOW_MODESET) + +struct drm_mode_atomic { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 reserved; + __u64 user_data; +}; + +struct drm_format_modifier_blob { +#define FORMAT_BLOB_CURRENT 1 + /* Version of this blob format */ + __u32 version; + + /* Flags */ + __u32 flags; + + /* Number of fourcc formats supported */ + __u32 count_formats; + + /* Where in this blob the formats exist (in bytes) */ + __u32 formats_offset; + + /* Number of drm_format_modifiers */ + __u32 count_modifiers; + + /* Where in this blob the modifiers exist (in bytes) */ + __u32 modifiers_offset; + + /* __u32 formats[] */ + /* struct drm_format_modifier modifiers[] */ +}; + +struct drm_format_modifier { + /* Bitmask of formats in get_plane format list this info applies to. The + * offset allows a sliding window of which 64 formats (bits). + * + * Some examples: + * In today's world with < 65 formats, and formats 0, and 2 are + * supported + * 0x0000000000000005 + * ^-offset = 0, formats = 5 + * + * If the number formats grew to 128, and formats 98-102 are + * supported with the modifier: + * + * 0x0000007c00000000 0000000000000000 + * ^ + * |__offset = 64, formats = 0x7c00000000 + * + */ + __u64 formats; + __u32 offset; + __u32 pad; + + /* The modifier that applies to the >get_plane format list bitmask. */ + __u64 modifier; +}; + +/** + * Create a new 'blob' data property, copying length bytes from data pointer, + * and returning new blob ID. + */ +struct drm_mode_create_blob { + /** Pointer to data to copy. */ + __u64 data; + /** Length of data to copy. */ + __u32 length; + /** Return: new property ID. */ + __u32 blob_id; +}; + +/** + * Destroy a user-created blob property. + */ +struct drm_mode_destroy_blob { + __u32 blob_id; +}; + +/** + * Lease mode resources, creating another drm_master. + */ +struct drm_mode_create_lease { + /** Pointer to array of object ids (__u32) */ + __u64 object_ids; + /** Number of object ids */ + __u32 object_count; + /** flags for new FD (O_CLOEXEC, etc) */ + __u32 flags; + + /** Return: unique identifier for lessee. */ + __u32 lessee_id; + /** Return: file descriptor to new drm_master file */ + __u32 fd; +}; + +/** + * List lesses from a drm_master + */ +struct drm_mode_list_lessees { + /** Number of lessees. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_lessees; + __u32 pad; + + /** Pointer to lessees. + * pointer to __u64 array of lessee ids + */ + __u64 lessees_ptr; +}; + +/** + * Get leased objects + */ +struct drm_mode_get_lease { + /** Number of leased objects. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_objects; + __u32 pad; + + /** Pointer to objects. + * pointer to __u32 array of object ids + */ + __u64 objects_ptr; +}; + +/** + * Revoke lease + */ +struct drm_mode_revoke_lease { + /** Unique ID of lessee + */ + __u32 lessee_id; +}; + +/** + * struct drm_mode_rect - Two dimensional rectangle. + * @x1: Horizontal starting coordinate (inclusive). + * @y1: Vertical starting coordinate (inclusive). + * @x2: Horizontal ending coordinate (exclusive). + * @y2: Vertical ending coordinate (exclusive). + * + * With drm subsystem using struct drm_rect to manage rectangular area this + * export it to user-space. + * + * Currently used by drm_mode_atomic blob property FB_DAMAGE_CLIPS. + */ +struct drm_mode_rect { + __s32 x1; + __s32 y1; + __s32 x2; + __s32 y2; +}; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_sarea.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_sarea.h new file mode 100644 index 0000000..93025be --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/drm_sarea.h @@ -0,0 +1,92 @@ +/** + * \file drm_sarea.h + * \brief SAREA definitions + * + * \author Michel Dänzer + */ + +/* + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_SAREA_H_ +#define _DRM_SAREA_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* SAREA area needs to be at least a page */ +#if defined(__alpha__) +#define SAREA_MAX 0x2000U +#elif defined(__mips__) +#define SAREA_MAX 0x4000U +#elif defined(__ia64__) +#define SAREA_MAX 0x10000U /* 64kB */ +#else +/* Intel 830M driver needs at least 8k SAREA */ +#define SAREA_MAX 0x2000U +#endif + +/** Maximum number of drawables in the SAREA */ +#define SAREA_MAX_DRAWABLES 256 + +#define SAREA_DRAWABLE_CLAIMED_ENTRY 0x80000000 + +/** SAREA drawable */ +struct drm_sarea_drawable { + unsigned int stamp; + unsigned int flags; +}; + +/** SAREA frame */ +struct drm_sarea_frame { + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + unsigned int fullscreen; +}; + +/** SAREA */ +struct drm_sarea { + /** first thing is always the DRM locking structure */ + struct drm_hw_lock lock; + /** \todo Use readers/writer lock for drm_sarea::drawable_lock */ + struct drm_hw_lock drawable_lock; + struct drm_sarea_drawable drawableTable[SAREA_MAX_DRAWABLES]; /**< drawables */ + struct drm_sarea_frame frame; /**< frame */ + drm_context_t dummy_context; +}; + +typedef struct drm_sarea_drawable drm_sarea_drawable_t; +typedef struct drm_sarea_frame drm_sarea_frame_t; +typedef struct drm_sarea drm_sarea_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* _DRM_SAREA_H_ */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/i915_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/i915_drm.h new file mode 100644 index 0000000..72afd94 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/i915_drm.h @@ -0,0 +1,1915 @@ +/* + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _I915_DRM_H_ +#define _I915_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + */ + +/** + * DOC: uevents generated by i915 on it's device node + * + * I915_L3_PARITY_UEVENT - Generated when the driver receives a parity mismatch + * event from the gpu l3 cache. Additional information supplied is ROW, + * BANK, SUBBANK, SLICE of the affected cacheline. Userspace should keep + * track of these events and if a specific cache-line seems to have a + * persistent error remap it with the l3 remapping tool supplied in + * intel-gpu-tools. The value supplied with the event is always 1. + * + * I915_ERROR_UEVENT - Generated upon error detection, currently only via + * hangcheck. The error detection event is a good indicator of when things + * began to go badly. The value supplied with the event is a 1 upon error + * detection, and a 0 upon reset completion, signifying no more error + * exists. NOTE: Disabling hangcheck or reset via module parameter will + * cause the related events to not be seen. + * + * I915_RESET_UEVENT - Event is generated just before an attempt to reset the + * the GPU. The value supplied with the event is always 1. NOTE: Disable + * reset via module parameter will cause this event to not be seen. + */ +#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR" +#define I915_ERROR_UEVENT "ERROR" +#define I915_RESET_UEVENT "RESET" + +/* + * i915_user_extension: Base class for defining a chain of extensions + * + * Many interfaces need to grow over time. In most cases we can simply + * extend the struct and have userspace pass in more data. Another option, + * as demonstrated by Vulkan's approach to providing extensions for forward + * and backward compatibility, is to use a list of optional structs to + * provide those extra details. + * + * The key advantage to using an extension chain is that it allows us to + * redefine the interface more easily than an ever growing struct of + * increasing complexity, and for large parts of that interface to be + * entirely optional. The downside is more pointer chasing; chasing across + * the boundary with pointers encapsulated inside u64. + */ +struct i915_user_extension { + __u64 next_extension; + __u32 name; + __u32 flags; /* All undefined bits must be zero. */ + __u32 rsvd[4]; /* Reserved for future use; must be zero. */ +}; + +/* + * MOCS indexes used for GPU surfaces, defining the cacheability of the + * surface data and the coherency for this data wrt. CPU vs. GPU accesses. + */ +enum i915_mocs_table_index { + /* + * Not cached anywhere, coherency between CPU and GPU accesses is + * guaranteed. + */ + I915_MOCS_UNCACHED, + /* + * Cacheability and coherency controlled by the kernel automatically + * based on the DRM_I915_GEM_SET_CACHING IOCTL setting and the current + * usage of the surface (used for display scanout or not). + */ + I915_MOCS_PTE, + /* + * Cached in all GPU caches available on the platform. + * Coherency between CPU and GPU accesses to the surface is not + * guaranteed without extra synchronization. + */ + I915_MOCS_CACHED, +}; + +/* + * Different engines serve different roles, and there may be more than one + * engine serving each role. enum drm_i915_gem_engine_class provides a + * classification of the role of the engine, which may be used when requesting + * operations to be performed on a certain subset of engines, or for providing + * information about that group. + */ +enum drm_i915_gem_engine_class { + I915_ENGINE_CLASS_RENDER = 0, + I915_ENGINE_CLASS_COPY = 1, + I915_ENGINE_CLASS_VIDEO = 2, + I915_ENGINE_CLASS_VIDEO_ENHANCE = 3, + + /* should be kept compact */ + + I915_ENGINE_CLASS_INVALID = -1 +}; + +/** + * DOC: perf_events exposed by i915 through /sys/bus/event_sources/drivers/i915 + * + */ + +enum drm_i915_pmu_engine_sample { + I915_SAMPLE_BUSY = 0, + I915_SAMPLE_WAIT = 1, + I915_SAMPLE_SEMA = 2 +}; + +#define I915_PMU_SAMPLE_BITS (4) +#define I915_PMU_SAMPLE_MASK (0xf) +#define I915_PMU_SAMPLE_INSTANCE_BITS (8) +#define I915_PMU_CLASS_SHIFT \ + (I915_PMU_SAMPLE_BITS + I915_PMU_SAMPLE_INSTANCE_BITS) + +#define __I915_PMU_ENGINE(class, instance, sample) \ + ((class) << I915_PMU_CLASS_SHIFT | \ + (instance) << I915_PMU_SAMPLE_BITS | \ + (sample)) + +#define I915_PMU_ENGINE_BUSY(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_BUSY) + +#define I915_PMU_ENGINE_WAIT(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_WAIT) + +#define I915_PMU_ENGINE_SEMA(class, instance) \ + __I915_PMU_ENGINE(class, instance, I915_SAMPLE_SEMA) + +#define __I915_PMU_OTHER(x) (__I915_PMU_ENGINE(0xff, 0xff, 0xf) + 1 + (x)) + +#define I915_PMU_ACTUAL_FREQUENCY __I915_PMU_OTHER(0) +#define I915_PMU_REQUESTED_FREQUENCY __I915_PMU_OTHER(1) +#define I915_PMU_INTERRUPTS __I915_PMU_OTHER(2) +#define I915_PMU_RC6_RESIDENCY __I915_PMU_OTHER(3) + +#define I915_PMU_LAST I915_PMU_RC6_RESIDENCY + +/* Each region is a minimum of 16k, and there are at most 255 of them. + */ +#define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use + * of chars for next/prev indices */ +#define I915_LOG_MIN_TEX_REGION_SIZE 14 + +typedef struct _drm_i915_init { + enum { + I915_INIT_DMA = 0x01, + I915_CLEANUP_DMA = 0x02, + I915_RESUME_DMA = 0x03 + } func; + unsigned int mmio_offset; + int sarea_priv_offset; + unsigned int ring_start; + unsigned int ring_end; + unsigned int ring_size; + unsigned int front_offset; + unsigned int back_offset; + unsigned int depth_offset; + unsigned int w; + unsigned int h; + unsigned int pitch; + unsigned int pitch_bits; + unsigned int back_pitch; + unsigned int depth_pitch; + unsigned int cpp; + unsigned int chipset; +} drm_i915_init_t; + +typedef struct _drm_i915_sarea { + struct drm_tex_region texList[I915_NR_TEX_REGIONS + 1]; + int last_upload; /* last time texture was uploaded */ + int last_enqueue; /* last time a buffer was enqueued */ + int last_dispatch; /* age of the most recently dispatched buffer */ + int ctxOwner; /* last context to upload state */ + int texAge; + int pf_enabled; /* is pageflipping allowed? */ + int pf_active; + int pf_current_page; /* which buffer is being displayed? */ + int perf_boxes; /* performance boxes to be displayed */ + int width, height; /* screen size in pixels */ + + drm_handle_t front_handle; + int front_offset; + int front_size; + + drm_handle_t back_handle; + int back_offset; + int back_size; + + drm_handle_t depth_handle; + int depth_offset; + int depth_size; + + drm_handle_t tex_handle; + int tex_offset; + int tex_size; + int log_tex_granularity; + int pitch; + int rotation; /* 0, 90, 180 or 270 */ + int rotated_offset; + int rotated_size; + int rotated_pitch; + int virtualX, virtualY; + + unsigned int front_tiled; + unsigned int back_tiled; + unsigned int depth_tiled; + unsigned int rotated_tiled; + unsigned int rotated2_tiled; + + int pipeA_x; + int pipeA_y; + int pipeA_w; + int pipeA_h; + int pipeB_x; + int pipeB_y; + int pipeB_w; + int pipeB_h; + + /* fill out some space for old userspace triple buffer */ + drm_handle_t unused_handle; + __u32 unused1, unused2, unused3; + + /* buffer object handles for static buffers. May change + * over the lifetime of the client. + */ + __u32 front_bo_handle; + __u32 back_bo_handle; + __u32 unused_bo_handle; + __u32 depth_bo_handle; + +} drm_i915_sarea_t; + +/* due to userspace building against these headers we need some compat here */ +#define planeA_x pipeA_x +#define planeA_y pipeA_y +#define planeA_w pipeA_w +#define planeA_h pipeA_h +#define planeB_x pipeB_x +#define planeB_y pipeB_y +#define planeB_w pipeB_w +#define planeB_h pipeB_h + +/* Flags for perf_boxes + */ +#define I915_BOX_RING_EMPTY 0x1 +#define I915_BOX_FLIP 0x2 +#define I915_BOX_WAIT 0x4 +#define I915_BOX_TEXTURE_LOAD 0x8 +#define I915_BOX_LOST_CONTEXT 0x10 + +/* + * i915 specific ioctls. + * + * The device specific ioctl range is [DRM_COMMAND_BASE, DRM_COMMAND_END) ie + * [0x40, 0xa0) (a0 is excluded). The numbers below are defined as offset + * against DRM_COMMAND_BASE and should be between [0x0, 0x60). + */ +#define DRM_I915_INIT 0x00 +#define DRM_I915_FLUSH 0x01 +#define DRM_I915_FLIP 0x02 +#define DRM_I915_BATCHBUFFER 0x03 +#define DRM_I915_IRQ_EMIT 0x04 +#define DRM_I915_IRQ_WAIT 0x05 +#define DRM_I915_GETPARAM 0x06 +#define DRM_I915_SETPARAM 0x07 +#define DRM_I915_ALLOC 0x08 +#define DRM_I915_FREE 0x09 +#define DRM_I915_INIT_HEAP 0x0a +#define DRM_I915_CMDBUFFER 0x0b +#define DRM_I915_DESTROY_HEAP 0x0c +#define DRM_I915_SET_VBLANK_PIPE 0x0d +#define DRM_I915_GET_VBLANK_PIPE 0x0e +#define DRM_I915_VBLANK_SWAP 0x0f +#define DRM_I915_HWS_ADDR 0x11 +#define DRM_I915_GEM_INIT 0x13 +#define DRM_I915_GEM_EXECBUFFER 0x14 +#define DRM_I915_GEM_PIN 0x15 +#define DRM_I915_GEM_UNPIN 0x16 +#define DRM_I915_GEM_BUSY 0x17 +#define DRM_I915_GEM_THROTTLE 0x18 +#define DRM_I915_GEM_ENTERVT 0x19 +#define DRM_I915_GEM_LEAVEVT 0x1a +#define DRM_I915_GEM_CREATE 0x1b +#define DRM_I915_GEM_PREAD 0x1c +#define DRM_I915_GEM_PWRITE 0x1d +#define DRM_I915_GEM_MMAP 0x1e +#define DRM_I915_GEM_SET_DOMAIN 0x1f +#define DRM_I915_GEM_SW_FINISH 0x20 +#define DRM_I915_GEM_SET_TILING 0x21 +#define DRM_I915_GEM_GET_TILING 0x22 +#define DRM_I915_GEM_GET_APERTURE 0x23 +#define DRM_I915_GEM_MMAP_GTT 0x24 +#define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25 +#define DRM_I915_GEM_MADVISE 0x26 +#define DRM_I915_OVERLAY_PUT_IMAGE 0x27 +#define DRM_I915_OVERLAY_ATTRS 0x28 +#define DRM_I915_GEM_EXECBUFFER2 0x29 +#define DRM_I915_GEM_EXECBUFFER2_WR DRM_I915_GEM_EXECBUFFER2 +#define DRM_I915_GET_SPRITE_COLORKEY 0x2a +#define DRM_I915_SET_SPRITE_COLORKEY 0x2b +#define DRM_I915_GEM_WAIT 0x2c +#define DRM_I915_GEM_CONTEXT_CREATE 0x2d +#define DRM_I915_GEM_CONTEXT_DESTROY 0x2e +#define DRM_I915_GEM_SET_CACHING 0x2f +#define DRM_I915_GEM_GET_CACHING 0x30 +#define DRM_I915_REG_READ 0x31 +#define DRM_I915_GET_RESET_STATS 0x32 +#define DRM_I915_GEM_USERPTR 0x33 +#define DRM_I915_GEM_CONTEXT_GETPARAM 0x34 +#define DRM_I915_GEM_CONTEXT_SETPARAM 0x35 +#define DRM_I915_PERF_OPEN 0x36 +#define DRM_I915_PERF_ADD_CONFIG 0x37 +#define DRM_I915_PERF_REMOVE_CONFIG 0x38 +#define DRM_I915_QUERY 0x39 +/* Must be kept compact -- no holes */ + +#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) +#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) +#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP) +#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t) +#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t) +#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t) +#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, drm_i915_getparam_t) +#define DRM_IOCTL_I915_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SETPARAM, drm_i915_setparam_t) +#define DRM_IOCTL_I915_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_ALLOC, drm_i915_mem_alloc_t) +#define DRM_IOCTL_I915_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FREE, drm_i915_mem_free_t) +#define DRM_IOCTL_I915_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT_HEAP, drm_i915_mem_init_heap_t) +#define DRM_IOCTL_I915_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_CMDBUFFER, drm_i915_cmdbuffer_t) +#define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t) +#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) +#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) +#define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_EXECBUFFER2_WR DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2_WR, struct drm_i915_gem_execbuffer2) +#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) +#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) +#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) +#define DRM_IOCTL_I915_GEM_SET_CACHING DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_CACHING, struct drm_i915_gem_caching) +#define DRM_IOCTL_I915_GEM_GET_CACHING DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GET_CACHING, struct drm_i915_gem_caching) +#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) +#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT) +#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT) +#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) +#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) +#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite) +#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap) +#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt) +#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain) +#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish) +#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) +#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) +#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) +#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id) +#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise) +#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image) +#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs) +#define DRM_IOCTL_I915_SET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_SET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GET_SPRITE_COLORKEY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_SPRITE_COLORKEY, struct drm_intel_sprite_colorkey) +#define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) +#define DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create_ext) +#define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) +#define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read) +#define DRM_IOCTL_I915_GET_RESET_STATS DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GET_RESET_STATS, struct drm_i915_reset_stats) +#define DRM_IOCTL_I915_GEM_USERPTR DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr) +#define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param) +#define DRM_IOCTL_I915_PERF_OPEN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_OPEN, struct drm_i915_perf_open_param) +#define DRM_IOCTL_I915_PERF_ADD_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_ADD_CONFIG, struct drm_i915_perf_oa_config) +#define DRM_IOCTL_I915_PERF_REMOVE_CONFIG DRM_IOW(DRM_COMMAND_BASE + DRM_I915_PERF_REMOVE_CONFIG, __u64) +#define DRM_IOCTL_I915_QUERY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_QUERY, struct drm_i915_query) + +/* Allow drivers to submit batchbuffers directly to hardware, relying + * on the security mechanisms provided by hardware. + */ +typedef struct drm_i915_batchbuffer { + int start; /* agp offset */ + int used; /* nr bytes in use */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect *cliprects; /* pointer to userspace cliprects */ +} drm_i915_batchbuffer_t; + +/* As above, but pass a pointer to userspace buffer which can be + * validated by the kernel prior to sending to hardware. + */ +typedef struct _drm_i915_cmdbuffer { + char *buf; /* pointer to userspace command buffer */ + int sz; /* nr bytes in buf */ + int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */ + int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */ + int num_cliprects; /* mulitpass with multiple cliprects? */ + struct drm_clip_rect *cliprects; /* pointer to userspace cliprects */ +} drm_i915_cmdbuffer_t; + +/* Userspace can request & wait on irq's: + */ +typedef struct drm_i915_irq_emit { + int *irq_seq; +} drm_i915_irq_emit_t; + +typedef struct drm_i915_irq_wait { + int irq_seq; +} drm_i915_irq_wait_t; + +/* + * Different modes of per-process Graphics Translation Table, + * see I915_PARAM_HAS_ALIASING_PPGTT + */ +#define I915_GEM_PPGTT_NONE 0 +#define I915_GEM_PPGTT_ALIASING 1 +#define I915_GEM_PPGTT_FULL 2 + +/* Ioctl to query kernel params: + */ +#define I915_PARAM_IRQ_ACTIVE 1 +#define I915_PARAM_ALLOW_BATCHBUFFER 2 +#define I915_PARAM_LAST_DISPATCH 3 +#define I915_PARAM_CHIPSET_ID 4 +#define I915_PARAM_HAS_GEM 5 +#define I915_PARAM_NUM_FENCES_AVAIL 6 +#define I915_PARAM_HAS_OVERLAY 7 +#define I915_PARAM_HAS_PAGEFLIPPING 8 +#define I915_PARAM_HAS_EXECBUF2 9 +#define I915_PARAM_HAS_BSD 10 +#define I915_PARAM_HAS_BLT 11 +#define I915_PARAM_HAS_RELAXED_FENCING 12 +#define I915_PARAM_HAS_COHERENT_RINGS 13 +#define I915_PARAM_HAS_EXEC_CONSTANTS 14 +#define I915_PARAM_HAS_RELAXED_DELTA 15 +#define I915_PARAM_HAS_GEN7_SOL_RESET 16 +#define I915_PARAM_HAS_LLC 17 +#define I915_PARAM_HAS_ALIASING_PPGTT 18 +#define I915_PARAM_HAS_WAIT_TIMEOUT 19 +#define I915_PARAM_HAS_SEMAPHORES 20 +#define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21 +#define I915_PARAM_HAS_VEBOX 22 +#define I915_PARAM_HAS_SECURE_BATCHES 23 +#define I915_PARAM_HAS_PINNED_BATCHES 24 +#define I915_PARAM_HAS_EXEC_NO_RELOC 25 +#define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 +#define I915_PARAM_HAS_WT 27 +#define I915_PARAM_CMD_PARSER_VERSION 28 +#define I915_PARAM_HAS_COHERENT_PHYS_GTT 29 +#define I915_PARAM_MMAP_VERSION 30 +#define I915_PARAM_HAS_BSD2 31 +#define I915_PARAM_REVISION 32 +#define I915_PARAM_SUBSLICE_TOTAL 33 +#define I915_PARAM_EU_TOTAL 34 +#define I915_PARAM_HAS_GPU_RESET 35 +#define I915_PARAM_HAS_RESOURCE_STREAMER 36 +#define I915_PARAM_HAS_EXEC_SOFTPIN 37 +#define I915_PARAM_HAS_POOLED_EU 38 +#define I915_PARAM_MIN_EU_IN_POOL 39 +#define I915_PARAM_MMAP_GTT_VERSION 40 + +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports user defined execution + * priorities and the driver will attempt to execute batches in priority order. + * The param returns a capability bitmask, nonzero implies that the scheduler + * is enabled, with different features present according to the mask. + * + * The initial priority for each batch is supplied by the context and is + * controlled via I915_CONTEXT_PARAM_PRIORITY. + */ +#define I915_PARAM_HAS_SCHEDULER 41 +#define I915_SCHEDULER_CAP_ENABLED (1ul << 0) +#define I915_SCHEDULER_CAP_PRIORITY (1ul << 1) +#define I915_SCHEDULER_CAP_PREEMPTION (1ul << 2) +#define I915_SCHEDULER_CAP_SEMAPHORES (1ul << 3) + +#define I915_PARAM_HUC_STATUS 42 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to opt-out of + * synchronisation with implicit fencing on individual objects. + * See EXEC_OBJECT_ASYNC. + */ +#define I915_PARAM_HAS_EXEC_ASYNC 43 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports explicit fence support - + * both being able to pass in a sync_file fd to wait upon before executing, + * and being able to return a new sync_file fd that is signaled when the + * current request is complete. See I915_EXEC_FENCE_IN and I915_EXEC_FENCE_OUT. + */ +#define I915_PARAM_HAS_EXEC_FENCE 44 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to capture + * user specified buffers for post-mortem debugging of GPU hangs. See + * EXEC_OBJECT_CAPTURE. + */ +#define I915_PARAM_HAS_EXEC_CAPTURE 45 + +#define I915_PARAM_SLICE_MASK 46 + +/* Assuming it's uniform for each slice, this queries the mask of subslices + * per-slice for this system. + */ +#define I915_PARAM_SUBSLICE_MASK 47 + +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying the batch buffer + * as the first execobject as opposed to the last. See I915_EXEC_BATCH_FIRST. + */ +#define I915_PARAM_HAS_EXEC_BATCH_FIRST 48 + +/* Query whether DRM_I915_GEM_EXECBUFFER2 supports supplying an array of + * drm_i915_gem_exec_fence structures. See I915_EXEC_FENCE_ARRAY. + */ +#define I915_PARAM_HAS_EXEC_FENCE_ARRAY 49 + +/* + * Query whether every context (both per-file default and user created) is + * isolated (insofar as HW supports). If this parameter is not true, then + * freshly created contexts may inherit values from an existing context, + * rather than default HW values. If true, it also ensures (insofar as HW + * supports) that all state set by this context will not leak to any other + * context. + * + * As not every engine across every gen support contexts, the returned + * value reports the support of context isolation for individual engines by + * returning a bitmask of each engine class set to true if that class supports + * isolation. + */ +#define I915_PARAM_HAS_CONTEXT_ISOLATION 50 + +/* Frequency of the command streamer timestamps given by the *_TIMESTAMP + * registers. This used to be fixed per platform but from CNL onwards, this + * might vary depending on the parts. + */ +#define I915_PARAM_CS_TIMESTAMP_FREQUENCY 51 + +/* + * Once upon a time we supposed that writes through the GGTT would be + * immediately in physical memory (once flushed out of the CPU path). However, + * on a few different processors and chipsets, this is not necessarily the case + * as the writes appear to be buffered internally. Thus a read of the backing + * storage (physical memory) via a different path (with different physical tags + * to the indirect write via the GGTT) will see stale values from before + * the GGTT write. Inside the kernel, we can for the most part keep track of + * the different read/write domains in use (e.g. set-domain), but the assumption + * of coherency is baked into the ABI, hence reporting its true state in this + * parameter. + * + * Reports true when writes via mmap_gtt are immediately visible following an + * lfence to flush the WCB. + * + * Reports false when writes via mmap_gtt are indeterminately delayed in an in + * internal buffer and are _not_ immediately visible to third parties accessing + * directly via mmap_cpu/mmap_wc. Use of mmap_gtt as part of an IPC + * communications channel when reporting false is strongly disadvised. + */ +#define I915_PARAM_MMAP_GTT_COHERENT 52 + +/* Must be kept compact -- no holes and well documented */ + +typedef struct drm_i915_getparam { + __s32 param; + /* + * WARNING: Using pointers instead of fixed-size u64 means we need to write + * compat32 code. Don't repeat this mistake. + */ + int *value; +} drm_i915_getparam_t; + +/* Ioctl to set kernel params: + */ +#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1 +#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2 +#define I915_SETPARAM_ALLOW_BATCHBUFFER 3 +#define I915_SETPARAM_NUM_USED_FENCES 4 +/* Must be kept compact -- no holes */ + +typedef struct drm_i915_setparam { + int param; + int value; +} drm_i915_setparam_t; + +/* A memory manager for regions of shared memory: + */ +#define I915_MEM_REGION_AGP 1 + +typedef struct drm_i915_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or agp */ +} drm_i915_mem_alloc_t; + +typedef struct drm_i915_mem_free { + int region; + int region_offset; +} drm_i915_mem_free_t; + +typedef struct drm_i915_mem_init_heap { + int region; + int size; + int start; +} drm_i915_mem_init_heap_t; + +/* Allow memory manager to be torn down and re-initialized (eg on + * rotate): + */ +typedef struct drm_i915_mem_destroy_heap { + int region; +} drm_i915_mem_destroy_heap_t; + +/* Allow X server to configure which pipes to monitor for vblank signals + */ +#define DRM_I915_VBLANK_PIPE_A 1 +#define DRM_I915_VBLANK_PIPE_B 2 + +typedef struct drm_i915_vblank_pipe { + int pipe; +} drm_i915_vblank_pipe_t; + +/* Schedule buffer swap at given vertical blank: + */ +typedef struct drm_i915_vblank_swap { + drm_drawable_t drawable; + enum drm_vblank_seq_type seqtype; + unsigned int sequence; +} drm_i915_vblank_swap_t; + +typedef struct drm_i915_hws_addr { + __u64 addr; +} drm_i915_hws_addr_t; + +struct drm_i915_gem_init { + /** + * Beginning offset in the GTT to be managed by the DRM memory + * manager. + */ + __u64 gtt_start; + /** + * Ending offset in the GTT to be managed by the DRM memory + * manager. + */ + __u64 gtt_end; +}; + +struct drm_i915_gem_create { + /** + * Requested size for the object. + * + * The (page-aligned) allocated size for the object will be returned. + */ + __u64 size; + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; + __u32 pad; +}; + +struct drm_i915_gem_pread { + /** Handle for the object being read. */ + __u32 handle; + __u32 pad; + /** Offset into the object to read from */ + __u64 offset; + /** Length of data to read */ + __u64 size; + /** + * Pointer to write the data into. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 data_ptr; +}; + +struct drm_i915_gem_pwrite { + /** Handle for the object being written to. */ + __u32 handle; + __u32 pad; + /** Offset into the object to write to */ + __u64 offset; + /** Length of data to write */ + __u64 size; + /** + * Pointer to read the data from. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 data_ptr; +}; + +struct drm_i915_gem_mmap { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** Offset in the object to map. */ + __u64 offset; + /** + * Length of data to map. + * + * The value will be page-aligned. + */ + __u64 size; + /** + * Returned pointer the data was mapped at. + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 addr_ptr; + + /** + * Flags for extended behaviour. + * + * Added in version 2. + */ + __u64 flags; +#define I915_MMAP_WC 0x1 +}; + +struct drm_i915_gem_mmap_gtt { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + __u64 offset; +}; + +struct drm_i915_gem_set_domain { + /** Handle for the object */ + __u32 handle; + + /** New read domains */ + __u32 read_domains; + + /** New write domain */ + __u32 write_domain; +}; + +struct drm_i915_gem_sw_finish { + /** Handle for the object */ + __u32 handle; +}; + +struct drm_i915_gem_relocation_entry { + /** + * Handle of the buffer being pointed to by this relocation entry. + * + * It's appealing to make this be an index into the mm_validate_entry + * list to refer to the buffer, but this allows the driver to create + * a relocation list for state buffers and not re-write it per + * exec using the buffer. + */ + __u32 target_handle; + + /** + * Value to be added to the offset of the target buffer to make up + * the relocation entry. + */ + __u32 delta; + + /** Offset in the buffer the relocation entry will be written into */ + __u64 offset; + + /** + * Offset value of the target buffer that the relocation entry was last + * written as. + * + * If the buffer has the same offset as last time, we can skip syncing + * and writing the relocation. This value is written back out by + * the execbuffer ioctl when the relocation is written. + */ + __u64 presumed_offset; + + /** + * Target memory domains read by this operation. + */ + __u32 read_domains; + + /** + * Target memory domains written by this operation. + * + * Note that only one domain may be written by the whole + * execbuffer operation, so that where there are conflicts, + * the application will get -EINVAL back. + */ + __u32 write_domain; +}; + +/** @{ + * Intel memory domains + * + * Most of these just align with the various caches in + * the system and are used to flush and invalidate as + * objects end up cached in different domains. + */ +/** CPU cache */ +#define I915_GEM_DOMAIN_CPU 0x00000001 +/** Render cache, used by 2D and 3D drawing */ +#define I915_GEM_DOMAIN_RENDER 0x00000002 +/** Sampler cache, used by texture engine */ +#define I915_GEM_DOMAIN_SAMPLER 0x00000004 +/** Command queue, used to load batch buffers */ +#define I915_GEM_DOMAIN_COMMAND 0x00000008 +/** Instruction cache, used by shader programs */ +#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010 +/** Vertex address cache */ +#define I915_GEM_DOMAIN_VERTEX 0x00000020 +/** GTT domain - aperture and scanout */ +#define I915_GEM_DOMAIN_GTT 0x00000040 +/** WC domain - uncached access */ +#define I915_GEM_DOMAIN_WC 0x00000080 +/** @} */ + +struct drm_i915_gem_exec_object { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + __u32 handle; + + /** Number of relocations to be performed on this buffer */ + __u32 relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + __u64 relocs_ptr; + + /** Required alignment in graphics aperture */ + __u64 alignment; + + /** + * Returned value of the updated offset of the object, for future + * presumed_offset writes. + */ + __u64 offset; +}; + +struct drm_i915_gem_execbuffer { + /** + * List of buffers to be validated with their relocations to be + * performend on them. + * + * This is a pointer to an array of struct drm_i915_gem_validate_entry. + * + * These buffers must be listed in an order such that all relocations + * a buffer is performing refer to buffers that have already appeared + * in the validate list. + */ + __u64 buffers_ptr; + __u32 buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + __u32 batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + __u32 batch_len; + __u32 DR1; + __u32 DR4; + __u32 num_cliprects; + /** This is a struct drm_clip_rect *cliprects */ + __u64 cliprects_ptr; +}; + +struct drm_i915_gem_exec_object2 { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + __u32 handle; + + /** Number of relocations to be performed on this buffer */ + __u32 relocation_count; + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + __u64 relocs_ptr; + + /** Required alignment in graphics aperture */ + __u64 alignment; + + /** + * When the EXEC_OBJECT_PINNED flag is specified this is populated by + * the user with the GTT offset at which this object will be pinned. + * When the I915_EXEC_NO_RELOC flag is specified this must contain the + * presumed_offset of the object. + * During execbuffer2 the kernel populates it with the value of the + * current GTT offset of the object, for future presumed_offset writes. + */ + __u64 offset; + +#define EXEC_OBJECT_NEEDS_FENCE (1<<0) +#define EXEC_OBJECT_NEEDS_GTT (1<<1) +#define EXEC_OBJECT_WRITE (1<<2) +#define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3) +#define EXEC_OBJECT_PINNED (1<<4) +#define EXEC_OBJECT_PAD_TO_SIZE (1<<5) +/* The kernel implicitly tracks GPU activity on all GEM objects, and + * synchronises operations with outstanding rendering. This includes + * rendering on other devices if exported via dma-buf. However, sometimes + * this tracking is too coarse and the user knows better. For example, + * if the object is split into non-overlapping ranges shared between different + * clients or engines (i.e. suballocating objects), the implicit tracking + * by kernel assumes that each operation affects the whole object rather + * than an individual range, causing needless synchronisation between clients. + * The kernel will also forgo any CPU cache flushes prior to rendering from + * the object as the client is expected to be also handling such domain + * tracking. + * + * The kernel maintains the implicit tracking in order to manage resources + * used by the GPU - this flag only disables the synchronisation prior to + * rendering with this object in this execbuf. + * + * Opting out of implicit synhronisation requires the user to do its own + * explicit tracking to avoid rendering corruption. See, for example, + * I915_PARAM_HAS_EXEC_FENCE to order execbufs and execute them asynchronously. + */ +#define EXEC_OBJECT_ASYNC (1<<6) +/* Request that the contents of this execobject be copied into the error + * state upon a GPU hang involving this batch for post-mortem debugging. + * These buffers are recorded in no particular order as "user" in + * /sys/class/drm/cardN/error. Query I915_PARAM_HAS_EXEC_CAPTURE to see + * if the kernel supports this flag. + */ +#define EXEC_OBJECT_CAPTURE (1<<7) +/* All remaining bits are MBZ and RESERVED FOR FUTURE USE */ +#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_CAPTURE<<1) + __u64 flags; + + union { + __u64 rsvd1; + __u64 pad_to_size; + }; + __u64 rsvd2; +}; + +struct drm_i915_gem_exec_fence { + /** + * User's handle for a drm_syncobj to wait on or signal. + */ + __u32 handle; + +#define I915_EXEC_FENCE_WAIT (1<<0) +#define I915_EXEC_FENCE_SIGNAL (1<<1) +#define __I915_EXEC_FENCE_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_SIGNAL << 1)) + __u32 flags; +}; + +struct drm_i915_gem_execbuffer2 { + /** + * List of gem_exec_object2 structs + */ + __u64 buffers_ptr; + __u32 buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + __u32 batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + __u32 batch_len; + __u32 DR1; + __u32 DR4; + __u32 num_cliprects; + /** + * This is a struct drm_clip_rect *cliprects if I915_EXEC_FENCE_ARRAY + * is not set. If I915_EXEC_FENCE_ARRAY is set, then this is a + * struct drm_i915_gem_exec_fence *fences. + */ + __u64 cliprects_ptr; +#define I915_EXEC_RING_MASK (0x3f) +#define I915_EXEC_DEFAULT (0<<0) +#define I915_EXEC_RENDER (1<<0) +#define I915_EXEC_BSD (2<<0) +#define I915_EXEC_BLT (3<<0) +#define I915_EXEC_VEBOX (4<<0) + +/* Used for switching the constants addressing mode on gen4+ RENDER ring. + * Gen6+ only supports relative addressing to dynamic state (default) and + * absolute addressing. + * + * These flags are ignored for the BSD and BLT rings. + */ +#define I915_EXEC_CONSTANTS_MASK (3<<6) +#define I915_EXEC_CONSTANTS_REL_GENERAL (0<<6) /* default */ +#define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6) +#define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */ + __u64 flags; + __u64 rsvd1; /* now used for context info */ + __u64 rsvd2; +}; + +/** Resets the SO write offset registers for transform feedback on gen7. */ +#define I915_EXEC_GEN7_SOL_RESET (1<<8) + +/** Request a privileged ("secure") batch buffer. Note only available for + * DRM_ROOT_ONLY | DRM_MASTER processes. + */ +#define I915_EXEC_SECURE (1<<9) + +/** Inform the kernel that the batch is and will always be pinned. This + * negates the requirement for a workaround to be performed to avoid + * an incoherent CS (such as can be found on 830/845). If this flag is + * not passed, the kernel will endeavour to make sure the batch is + * coherent with the CS before execution. If this flag is passed, + * userspace assumes the responsibility for ensuring the same. + */ +#define I915_EXEC_IS_PINNED (1<<10) + +/** Provide a hint to the kernel that the command stream and auxiliary + * state buffers already holds the correct presumed addresses and so the + * relocation process may be skipped if no buffers need to be moved in + * preparation for the execbuffer. + */ +#define I915_EXEC_NO_RELOC (1<<11) + +/** Use the reloc.handle as an index into the exec object array rather + * than as the per-file handle. + */ +#define I915_EXEC_HANDLE_LUT (1<<12) + +/** Used for switching BSD rings on the platforms with two BSD rings */ +#define I915_EXEC_BSD_SHIFT (13) +#define I915_EXEC_BSD_MASK (3 << I915_EXEC_BSD_SHIFT) +/* default ping-pong mode */ +#define I915_EXEC_BSD_DEFAULT (0 << I915_EXEC_BSD_SHIFT) +#define I915_EXEC_BSD_RING1 (1 << I915_EXEC_BSD_SHIFT) +#define I915_EXEC_BSD_RING2 (2 << I915_EXEC_BSD_SHIFT) + +/** Tell the kernel that the batchbuffer is processed by + * the resource streamer. + */ +#define I915_EXEC_RESOURCE_STREAMER (1<<15) + +/* Setting I915_EXEC_FENCE_IN implies that lower_32_bits(rsvd2) represent + * a sync_file fd to wait upon (in a nonblocking manner) prior to executing + * the batch. + * + * Returns -EINVAL if the sync_file fd cannot be found. + */ +#define I915_EXEC_FENCE_IN (1<<16) + +/* Setting I915_EXEC_FENCE_OUT causes the ioctl to return a sync_file fd + * in the upper_32_bits(rsvd2) upon success. Ownership of the fd is given + * to the caller, and it should be close() after use. (The fd is a regular + * file descriptor and will be cleaned up on process termination. It holds + * a reference to the request, but nothing else.) + * + * The sync_file fd can be combined with other sync_file and passed either + * to execbuf using I915_EXEC_FENCE_IN, to atomic KMS ioctls (so that a flip + * will only occur after this request completes), or to other devices. + * + * Using I915_EXEC_FENCE_OUT requires use of + * DRM_IOCTL_I915_GEM_EXECBUFFER2_WR ioctl so that the result is written + * back to userspace. Failure to do so will cause the out-fence to always + * be reported as zero, and the real fence fd to be leaked. + */ +#define I915_EXEC_FENCE_OUT (1<<17) + +/* + * Traditionally the execbuf ioctl has only considered the final element in + * the execobject[] to be the executable batch. Often though, the client + * will known the batch object prior to construction and being able to place + * it into the execobject[] array first can simplify the relocation tracking. + * Setting I915_EXEC_BATCH_FIRST tells execbuf to use element 0 of the + * execobject[] as the * batch instead (the default is to use the last + * element). + */ +#define I915_EXEC_BATCH_FIRST (1<<18) + +/* Setting I915_FENCE_ARRAY implies that num_cliprects and cliprects_ptr + * define an array of i915_gem_exec_fence structures which specify a set of + * dma fences to wait upon or signal. + */ +#define I915_EXEC_FENCE_ARRAY (1<<19) + +#define __I915_EXEC_UNKNOWN_FLAGS (-(I915_EXEC_FENCE_ARRAY<<1)) + +#define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) +#define i915_execbuffer2_set_context_id(eb2, context) \ + (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK +#define i915_execbuffer2_get_context_id(eb2) \ + ((eb2).rsvd1 & I915_EXEC_CONTEXT_ID_MASK) + +struct drm_i915_gem_pin { + /** Handle of the buffer to be pinned. */ + __u32 handle; + __u32 pad; + + /** alignment required within the aperture */ + __u64 alignment; + + /** Returned GTT offset of the buffer. */ + __u64 offset; +}; + +struct drm_i915_gem_unpin { + /** Handle of the buffer to be unpinned. */ + __u32 handle; + __u32 pad; +}; + +struct drm_i915_gem_busy { + /** Handle of the buffer to check for busy */ + __u32 handle; + + /** Return busy status + * + * A return of 0 implies that the object is idle (after + * having flushed any pending activity), and a non-zero return that + * the object is still in-flight on the GPU. (The GPU has not yet + * signaled completion for all pending requests that reference the + * object.) An object is guaranteed to become idle eventually (so + * long as no new GPU commands are executed upon it). Due to the + * asynchronous nature of the hardware, an object reported + * as busy may become idle before the ioctl is completed. + * + * Furthermore, if the object is busy, which engine is busy is only + * provided as a guide and only indirectly by reporting its class + * (there may be more than one engine in each class). There are race + * conditions which prevent the report of which engines are busy from + * being always accurate. However, the converse is not true. If the + * object is idle, the result of the ioctl, that all engines are idle, + * is accurate. + * + * The returned dword is split into two fields to indicate both + * the engine classess on which the object is being read, and the + * engine class on which it is currently being written (if any). + * + * The low word (bits 0:15) indicate if the object is being written + * to by any engine (there can only be one, as the GEM implicit + * synchronisation rules force writes to be serialised). Only the + * engine class (offset by 1, I915_ENGINE_CLASS_RENDER is reported as + * 1 not 0 etc) for the last write is reported. + * + * The high word (bits 16:31) are a bitmask of which engines classes + * are currently reading from the object. Multiple engines may be + * reading from the object simultaneously. + * + * The value of each engine class is the same as specified in the + * I915_CONTEXT_SET_ENGINES parameter and via perf, i.e. + * I915_ENGINE_CLASS_RENDER, I915_ENGINE_CLASS_COPY, etc. + * reported as active itself. Some hardware may have parallel + * execution engines, e.g. multiple media engines, which are + * mapped to the same class identifier and so are not separately + * reported for busyness. + * + * Caveat emptor: + * Only the boolean result of this query is reliable; that is whether + * the object is idle or busy. The report of which engines are busy + * should be only used as a heuristic. + */ + __u32 busy; +}; + +/** + * I915_CACHING_NONE + * + * GPU access is not coherent with cpu caches. Default for machines without an + * LLC. + */ +#define I915_CACHING_NONE 0 +/** + * I915_CACHING_CACHED + * + * GPU access is coherent with cpu caches and furthermore the data is cached in + * last-level caches shared between cpu cores and the gpu GT. Default on + * machines with HAS_LLC. + */ +#define I915_CACHING_CACHED 1 +/** + * I915_CACHING_DISPLAY + * + * Special GPU caching mode which is coherent with the scanout engines. + * Transparently falls back to I915_CACHING_NONE on platforms where no special + * cache mode (like write-through or gfdt flushing) is available. The kernel + * automatically sets this mode when using a buffer as a scanout target. + * Userspace can manually set this mode to avoid a costly stall and clflush in + * the hotpath of drawing the first frame. + */ +#define I915_CACHING_DISPLAY 2 + +struct drm_i915_gem_caching { + /** + * Handle of the buffer to set/get the caching level of. */ + __u32 handle; + + /** + * Caching level to apply or return value + * + * bits0-15 are for generic caching control (i.e. the above defined + * values). bits16-31 are reserved for platform-specific variations + * (e.g. l3$ caching on gen7). */ + __u32 caching; +}; + +#define I915_TILING_NONE 0 +#define I915_TILING_X 1 +#define I915_TILING_Y 2 +#define I915_TILING_LAST I915_TILING_Y + +#define I915_BIT_6_SWIZZLE_NONE 0 +#define I915_BIT_6_SWIZZLE_9 1 +#define I915_BIT_6_SWIZZLE_9_10 2 +#define I915_BIT_6_SWIZZLE_9_11 3 +#define I915_BIT_6_SWIZZLE_9_10_11 4 +/* Not seen by userland */ +#define I915_BIT_6_SWIZZLE_UNKNOWN 5 +/* Seen by userland. */ +#define I915_BIT_6_SWIZZLE_9_17 6 +#define I915_BIT_6_SWIZZLE_9_10_17 7 + +struct drm_i915_gem_set_tiling { + /** Handle of the buffer to have its tiling state updated */ + __u32 handle; + + /** + * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + * + * This value is to be set on request, and will be updated by the + * kernel on successful return with the actual chosen tiling layout. + * + * The tiling mode may be demoted to I915_TILING_NONE when the system + * has bit 6 swizzling that can't be managed correctly by GEM. + * + * Buffer contents become undefined when changing tiling_mode. + */ + __u32 tiling_mode; + + /** + * Stride in bytes for the object when in I915_TILING_X or + * I915_TILING_Y. + */ + __u32 stride; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + __u32 swizzle_mode; +}; + +struct drm_i915_gem_get_tiling { + /** Handle of the buffer to get tiling state for. */ + __u32 handle; + + /** + * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + */ + __u32 tiling_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + __u32 swizzle_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping whilst bound. + */ + __u32 phys_swizzle_mode; +}; + +struct drm_i915_gem_get_aperture { + /** Total size of the aperture used by i915_gem_execbuffer, in bytes */ + __u64 aper_size; + + /** + * Available space in the aperture used by i915_gem_execbuffer, in + * bytes + */ + __u64 aper_available_size; +}; + +struct drm_i915_get_pipe_from_crtc_id { + /** ID of CRTC being requested **/ + __u32 crtc_id; + + /** pipe of requested CRTC **/ + __u32 pipe; +}; + +#define I915_MADV_WILLNEED 0 +#define I915_MADV_DONTNEED 1 +#define __I915_MADV_PURGED 2 /* internal state */ + +struct drm_i915_gem_madvise { + /** Handle of the buffer to change the backing store advice */ + __u32 handle; + + /* Advice: either the buffer will be needed again in the near future, + * or wont be and could be discarded under memory pressure. + */ + __u32 madv; + + /** Whether the backing store still exists. */ + __u32 retained; +}; + +/* flags */ +#define I915_OVERLAY_TYPE_MASK 0xff +#define I915_OVERLAY_YUV_PLANAR 0x01 +#define I915_OVERLAY_YUV_PACKED 0x02 +#define I915_OVERLAY_RGB 0x03 + +#define I915_OVERLAY_DEPTH_MASK 0xff00 +#define I915_OVERLAY_RGB24 0x1000 +#define I915_OVERLAY_RGB16 0x2000 +#define I915_OVERLAY_RGB15 0x3000 +#define I915_OVERLAY_YUV422 0x0100 +#define I915_OVERLAY_YUV411 0x0200 +#define I915_OVERLAY_YUV420 0x0300 +#define I915_OVERLAY_YUV410 0x0400 + +#define I915_OVERLAY_SWAP_MASK 0xff0000 +#define I915_OVERLAY_NO_SWAP 0x000000 +#define I915_OVERLAY_UV_SWAP 0x010000 +#define I915_OVERLAY_Y_SWAP 0x020000 +#define I915_OVERLAY_Y_AND_UV_SWAP 0x030000 + +#define I915_OVERLAY_FLAGS_MASK 0xff000000 +#define I915_OVERLAY_ENABLE 0x01000000 + +struct drm_intel_overlay_put_image { + /* various flags and src format description */ + __u32 flags; + /* source picture description */ + __u32 bo_handle; + /* stride values and offsets are in bytes, buffer relative */ + __u16 stride_Y; /* stride for packed formats */ + __u16 stride_UV; + __u32 offset_Y; /* offset for packet formats */ + __u32 offset_U; + __u32 offset_V; + /* in pixels */ + __u16 src_width; + __u16 src_height; + /* to compensate the scaling factors for partially covered surfaces */ + __u16 src_scan_width; + __u16 src_scan_height; + /* output crtc description */ + __u32 crtc_id; + __u16 dst_x; + __u16 dst_y; + __u16 dst_width; + __u16 dst_height; +}; + +/* flags */ +#define I915_OVERLAY_UPDATE_ATTRS (1<<0) +#define I915_OVERLAY_UPDATE_GAMMA (1<<1) +#define I915_OVERLAY_DISABLE_DEST_COLORKEY (1<<2) +struct drm_intel_overlay_attrs { + __u32 flags; + __u32 color_key; + __s32 brightness; + __u32 contrast; + __u32 saturation; + __u32 gamma0; + __u32 gamma1; + __u32 gamma2; + __u32 gamma3; + __u32 gamma4; + __u32 gamma5; +}; + +/* + * Intel sprite handling + * + * Color keying works with a min/mask/max tuple. Both source and destination + * color keying is allowed. + * + * Source keying: + * Sprite pixels within the min & max values, masked against the color channels + * specified in the mask field, will be transparent. All other pixels will + * be displayed on top of the primary plane. For RGB surfaces, only the min + * and mask fields will be used; ranged compares are not allowed. + * + * Destination keying: + * Primary plane pixels that match the min value, masked against the color + * channels specified in the mask field, will be replaced by corresponding + * pixels from the sprite plane. + * + * Note that source & destination keying are exclusive; only one can be + * active on a given plane. + */ + +#define I915_SET_COLORKEY_NONE (1<<0) /* Deprecated. Instead set + * flags==0 to disable colorkeying. + */ +#define I915_SET_COLORKEY_DESTINATION (1<<1) +#define I915_SET_COLORKEY_SOURCE (1<<2) +struct drm_intel_sprite_colorkey { + __u32 plane_id; + __u32 min_value; + __u32 channel_mask; + __u32 max_value; + __u32 flags; +}; + +struct drm_i915_gem_wait { + /** Handle of BO we shall wait on */ + __u32 bo_handle; + __u32 flags; + /** Number of nanoseconds to wait, Returns time remaining. */ + __s64 timeout_ns; +}; + +struct drm_i915_gem_context_create { + __u32 ctx_id; /* output: id of new context*/ + __u32 pad; +}; + +struct drm_i915_gem_context_create_ext { + __u32 ctx_id; /* output: id of new context*/ + __u32 flags; +#define I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS (1u << 0) +#define I915_CONTEXT_CREATE_FLAGS_UNKNOWN \ + (-(I915_CONTEXT_CREATE_FLAGS_USE_EXTENSIONS << 1)) + __u64 extensions; +}; + +struct drm_i915_gem_context_param { + __u32 ctx_id; + __u32 size; + __u64 param; +#define I915_CONTEXT_PARAM_BAN_PERIOD 0x1 +#define I915_CONTEXT_PARAM_NO_ZEROMAP 0x2 +#define I915_CONTEXT_PARAM_GTT_SIZE 0x3 +#define I915_CONTEXT_PARAM_NO_ERROR_CAPTURE 0x4 +#define I915_CONTEXT_PARAM_BANNABLE 0x5 +#define I915_CONTEXT_PARAM_PRIORITY 0x6 +#define I915_CONTEXT_MAX_USER_PRIORITY 1023 /* inclusive */ +#define I915_CONTEXT_DEFAULT_PRIORITY 0 +#define I915_CONTEXT_MIN_USER_PRIORITY -1023 /* inclusive */ + /* + * When using the following param, value should be a pointer to + * drm_i915_gem_context_param_sseu. + */ +#define I915_CONTEXT_PARAM_SSEU 0x7 + +/* + * Not all clients may want to attempt automatic recover of a context after + * a hang (for example, some clients may only submit very small incremental + * batches relying on known logical state of previous batches which will never + * recover correctly and each attempt will hang), and so would prefer that + * the context is forever banned instead. + * + * If set to false (0), after a reset, subsequent (and in flight) rendering + * from this context is discarded, and the client will need to create a new + * context to use instead. + * + * If set to true (1), the kernel will automatically attempt to recover the + * context by skipping the hanging batch and executing the next batch starting + * from the default context state (discarding the incomplete logical context + * state lost due to the reset). + * + * On creation, all new contexts are marked as recoverable. + */ +#define I915_CONTEXT_PARAM_RECOVERABLE 0x8 +/* Must be kept compact -- no holes and well documented */ + + __u64 value; +}; + +/** + * Context SSEU programming + * + * It may be necessary for either functional or performance reason to configure + * a context to run with a reduced number of SSEU (where SSEU stands for Slice/ + * Sub-slice/EU). + * + * This is done by configuring SSEU configuration using the below + * @struct drm_i915_gem_context_param_sseu for every supported engine which + * userspace intends to use. + * + * Not all GPUs or engines support this functionality in which case an error + * code -ENODEV will be returned. + * + * Also, flexibility of possible SSEU configuration permutations varies between + * GPU generations and software imposed limitations. Requesting such a + * combination will return an error code of -EINVAL. + * + * NOTE: When perf/OA is active the context's SSEU configuration is ignored in + * favour of a single global setting. + */ +struct drm_i915_gem_context_param_sseu { + /* + * Engine class & instance to be configured or queried. + */ + __u16 engine_class; + __u16 engine_instance; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * Mask of slices to enable for the context. Valid values are a subset + * of the bitmask value returned for I915_PARAM_SLICE_MASK. + */ + __u64 slice_mask; + + /* + * Mask of subslices to enable for the context. Valid values are a + * subset of the bitmask value return by I915_PARAM_SUBSLICE_MASK. + */ + __u64 subslice_mask; + + /* + * Minimum/Maximum number of EUs to enable per subslice for the + * context. min_eus_per_subslice must be inferior or equal to + * max_eus_per_subslice. + */ + __u16 min_eus_per_subslice; + __u16 max_eus_per_subslice; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 rsvd; +}; + +struct drm_i915_gem_context_create_ext_setparam { +#define I915_CONTEXT_CREATE_EXT_SETPARAM 0 + struct i915_user_extension base; + struct drm_i915_gem_context_param param; +}; + +struct drm_i915_gem_context_destroy { + __u32 ctx_id; + __u32 pad; +}; + +/* + * DRM_I915_GEM_VM_CREATE - + * + * Create a new virtual memory address space (ppGTT) for use within a context + * on the same file. Extensions can be provided to configure exactly how the + * address space is setup upon creation. + * + * The id of new VM (bound to the fd) for use with I915_CONTEXT_PARAM_VM is + * returned in the outparam @id. + * + * No flags are defined, with all bits reserved and must be zero. + * + * An extension chain maybe provided, starting with @extensions, and terminated + * by the @next_extension being 0. Currently, no extensions are defined. + * + * DRM_I915_GEM_VM_DESTROY - + * + * Destroys a previously created VM id, specified in @id. + * + * No extensions or flags are allowed currently, and so must be zero. + */ +struct drm_i915_gem_vm_control { + __u64 extensions; + __u32 flags; + __u32 vm_id; +}; + +struct drm_i915_reg_read { + /* + * Register offset. + * For 64bit wide registers where the upper 32bits don't immediately + * follow the lower 32bits, the offset of the lower 32bits must + * be specified + */ + __u64 offset; +#define I915_REG_READ_8B_WA (1ul << 0) + + __u64 val; /* Return value */ +}; + +/* Known registers: + * + * Render engine timestamp - 0x2358 + 64bit - gen7+ + * - Note this register returns an invalid value if using the default + * single instruction 8byte read, in order to workaround that pass + * flag I915_REG_READ_8B_WA in offset field. + * + */ + +struct drm_i915_reset_stats { + __u32 ctx_id; + __u32 flags; + + /* All resets since boot/module reload, for all contexts */ + __u32 reset_count; + + /* Number of batches lost when active in GPU, for this context */ + __u32 batch_active; + + /* Number of batches lost pending for execution, for this context */ + __u32 batch_pending; + + __u32 pad; +}; + +struct drm_i915_gem_userptr { + __u64 user_ptr; + __u64 user_size; + __u32 flags; +#define I915_USERPTR_READ_ONLY 0x1 +#define I915_USERPTR_UNSYNCHRONIZED 0x80000000 + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + __u32 handle; +}; + +enum drm_i915_oa_format { + I915_OA_FORMAT_A13 = 1, /* HSW only */ + I915_OA_FORMAT_A29, /* HSW only */ + I915_OA_FORMAT_A13_B8_C8, /* HSW only */ + I915_OA_FORMAT_B4_C8, /* HSW only */ + I915_OA_FORMAT_A45_B8_C8, /* HSW only */ + I915_OA_FORMAT_B4_C8_A16, /* HSW only */ + I915_OA_FORMAT_C4_B8, /* HSW+ */ + + /* Gen8+ */ + I915_OA_FORMAT_A12, + I915_OA_FORMAT_A12_B8_C8, + I915_OA_FORMAT_A32u40_A4u32_B8_C8, + + I915_OA_FORMAT_MAX /* non-ABI */ +}; + +enum drm_i915_perf_property_id { + /** + * Open the stream for a specific context handle (as used with + * execbuffer2). A stream opened for a specific context this way + * won't typically require root privileges. + */ + DRM_I915_PERF_PROP_CTX_HANDLE = 1, + + /** + * A value of 1 requests the inclusion of raw OA unit reports as + * part of stream samples. + */ + DRM_I915_PERF_PROP_SAMPLE_OA, + + /** + * The value specifies which set of OA unit metrics should be + * be configured, defining the contents of any OA unit reports. + */ + DRM_I915_PERF_PROP_OA_METRICS_SET, + + /** + * The value specifies the size and layout of OA unit reports. + */ + DRM_I915_PERF_PROP_OA_FORMAT, + + /** + * Specifying this property implicitly requests periodic OA unit + * sampling and (at least on Haswell) the sampling frequency is derived + * from this exponent as follows: + * + * 80ns * 2^(period_exponent + 1) + */ + DRM_I915_PERF_PROP_OA_EXPONENT, + + DRM_I915_PERF_PROP_MAX /* non-ABI */ +}; + +struct drm_i915_perf_open_param { + __u32 flags; +#define I915_PERF_FLAG_FD_CLOEXEC (1<<0) +#define I915_PERF_FLAG_FD_NONBLOCK (1<<1) +#define I915_PERF_FLAG_DISABLED (1<<2) + + /** The number of u64 (id, value) pairs */ + __u32 num_properties; + + /** + * Pointer to array of u64 (id, value) pairs configuring the stream + * to open. + */ + __u64 properties_ptr; +}; + +/** + * Enable data capture for a stream that was either opened in a disabled state + * via I915_PERF_FLAG_DISABLED or was later disabled via + * I915_PERF_IOCTL_DISABLE. + * + * It is intended to be cheaper to disable and enable a stream than it may be + * to close and re-open a stream with the same configuration. + * + * It's undefined whether any pending data for the stream will be lost. + */ +#define I915_PERF_IOCTL_ENABLE _IO('i', 0x0) + +/** + * Disable data capture for a stream. + * + * It is an error to try and read a stream that is disabled. + */ +#define I915_PERF_IOCTL_DISABLE _IO('i', 0x1) + +/** + * Common to all i915 perf records + */ +struct drm_i915_perf_record_header { + __u32 type; + __u16 pad; + __u16 size; +}; + +enum drm_i915_perf_record_type { + + /** + * Samples are the work horse record type whose contents are extensible + * and defined when opening an i915 perf stream based on the given + * properties. + * + * Boolean properties following the naming convention + * DRM_I915_PERF_SAMPLE_xyz_PROP request the inclusion of 'xyz' data in + * every sample. + * + * The order of these sample properties given by userspace has no + * affect on the ordering of data within a sample. The order is + * documented here. + * + * struct { + * struct drm_i915_perf_record_header header; + * + * { u32 oa_report[]; } && DRM_I915_PERF_PROP_SAMPLE_OA + * }; + */ + DRM_I915_PERF_RECORD_SAMPLE = 1, + + /* + * Indicates that one or more OA reports were not written by the + * hardware. This can happen for example if an MI_REPORT_PERF_COUNT + * command collides with periodic sampling - which would be more likely + * at higher sampling frequencies. + */ + DRM_I915_PERF_RECORD_OA_REPORT_LOST = 2, + + /** + * An error occurred that resulted in all pending OA reports being lost. + */ + DRM_I915_PERF_RECORD_OA_BUFFER_LOST = 3, + + DRM_I915_PERF_RECORD_MAX /* non-ABI */ +}; + +/** + * Structure to upload perf dynamic configuration into the kernel. + */ +struct drm_i915_perf_oa_config { + /** String formatted like "%08x-%04x-%04x-%04x-%012x" */ + char uuid[36]; + + __u32 n_mux_regs; + __u32 n_boolean_regs; + __u32 n_flex_regs; + + /* + * These fields are pointers to tuples of u32 values (register address, + * value). For example the expected length of the buffer pointed by + * mux_regs_ptr is (2 * sizeof(u32) * n_mux_regs). + */ + __u64 mux_regs_ptr; + __u64 boolean_regs_ptr; + __u64 flex_regs_ptr; +}; + +struct drm_i915_query_item { + __u64 query_id; +#define DRM_I915_QUERY_TOPOLOGY_INFO 1 +/* Must be kept compact -- no holes and well documented */ + + /* + * When set to zero by userspace, this is filled with the size of the + * data to be written at the data_ptr pointer. The kernel sets this + * value to a negative value to signal an error on a particular query + * item. + */ + __s32 length; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * Data will be written at the location pointed by data_ptr when the + * value of length matches the length of the data to be written by the + * kernel. + */ + __u64 data_ptr; +}; + +struct drm_i915_query { + __u32 num_items; + + /* + * Unused for now. Must be cleared to zero. + */ + __u32 flags; + + /* + * This points to an array of num_items drm_i915_query_item structures. + */ + __u64 items_ptr; +}; + +/* + * Data written by the kernel with query DRM_I915_QUERY_TOPOLOGY_INFO : + * + * data: contains the 3 pieces of information : + * + * - the slice mask with one bit per slice telling whether a slice is + * available. The availability of slice X can be queried with the following + * formula : + * + * (data[X / 8] >> (X % 8)) & 1 + * + * - the subslice mask for each slice with one bit per subslice telling + * whether a subslice is available. The availability of subslice Y in slice + * X can be queried with the following formula : + * + * (data[subslice_offset + + * X * subslice_stride + + * Y / 8] >> (Y % 8)) & 1 + * + * - the EU mask for each subslice in each slice with one bit per EU telling + * whether an EU is available. The availability of EU Z in subslice Y in + * slice X can be queried with the following formula : + * + * (data[eu_offset + + * (X * max_subslices + Y) * eu_stride + + * Z / 8] >> (Z % 8)) & 1 + */ +struct drm_i915_query_topology_info { + /* + * Unused for now. Must be cleared to zero. + */ + __u16 flags; + + __u16 max_slices; + __u16 max_subslices; + __u16 max_eus_per_subslice; + + /* + * Offset in data[] at which the subslice masks are stored. + */ + __u16 subslice_offset; + + /* + * Stride at which each of the subslice masks for each slice are + * stored. + */ + __u16 subslice_stride; + + /* + * Offset in data[] at which the EU masks are stored. + */ + __u16 eu_offset; + + /* + * Stride at which each of the EU masks for each subslice are stored. + */ + __u16 eu_stride; + + __u8 data[]; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _I915_DRM_H_ */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mach64_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mach64_drm.h new file mode 100644 index 0000000..1f5fd84 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mach64_drm.h @@ -0,0 +1,256 @@ +/* mach64_drm.h -- Public header for the mach64 driver -*- linux-c -*- + * Created: Thu Nov 30 20:04:32 2000 by gareth@valinux.com + */ +/* + * Copyright 2000 Gareth Hughes + * Copyright 2002 Frank C. Earl + * Copyright 2002-2003 Leif Delgass + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * Frank C. Earl + * Leif Delgass + */ + +#ifndef __MACH64_DRM_H__ +#define __MACH64_DRM_H__ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mach64_sarea.h) + */ +#ifndef __MACH64_SAREA_DEFINES__ +#define __MACH64_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + * GH: We're going to be pedantic about this. We want the card to do as + * little as possible, so let's avoid having it fetch a whole bunch of + * register values that don't change all that often, if at all. + */ +#define MACH64_UPLOAD_DST_OFF_PITCH 0x0001 +#define MACH64_UPLOAD_Z_OFF_PITCH 0x0002 +#define MACH64_UPLOAD_Z_ALPHA_CNTL 0x0004 +#define MACH64_UPLOAD_SCALE_3D_CNTL 0x0008 +#define MACH64_UPLOAD_DP_FOG_CLR 0x0010 +#define MACH64_UPLOAD_DP_WRITE_MASK 0x0020 +#define MACH64_UPLOAD_DP_PIX_WIDTH 0x0040 +#define MACH64_UPLOAD_SETUP_CNTL 0x0080 +#define MACH64_UPLOAD_MISC 0x0100 +#define MACH64_UPLOAD_TEXTURE 0x0200 +#define MACH64_UPLOAD_TEX0IMAGE 0x0400 +#define MACH64_UPLOAD_TEX1IMAGE 0x0800 +#define MACH64_UPLOAD_CLIPRECTS 0x1000 /* handled client-side */ +#define MACH64_UPLOAD_CONTEXT 0x00ff +#define MACH64_UPLOAD_ALL 0x1fff + +/* DMA buffer size + */ +#define MACH64_BUFFER_SIZE 16384 + +/* Max number of swaps allowed on the ring + * before the client must wait + */ +#define MACH64_MAX_QUEUED_FRAMES 3U + +/* Byte offsets for host blit buffer data + */ +#define MACH64_HOSTDATA_BLIT_OFFSET 104 + +/* Keep these small for testing. + */ +#define MACH64_NR_SAREA_CLIPRECTS 8 + +#define MACH64_CARD_HEAP 0 +#define MACH64_AGP_HEAP 1 +#define MACH64_NR_TEX_HEAPS 2 +#define MACH64_NR_TEX_REGIONS 64 +#define MACH64_LOG_TEX_GRANULARITY 16 + +#define MACH64_TEX_MAXLEVELS 1 + +#define MACH64_NR_CONTEXT_REGS 15 +#define MACH64_NR_TEXTURE_REGS 4 + +#endif /* __MACH64_SAREA_DEFINES__ */ + +typedef struct { + unsigned int dst_off_pitch; + + unsigned int z_off_pitch; + unsigned int z_cntl; + unsigned int alpha_tst_cntl; + + unsigned int scale_3d_cntl; + + unsigned int sc_left_right; + unsigned int sc_top_bottom; + + unsigned int dp_fog_clr; + unsigned int dp_write_mask; + unsigned int dp_pix_width; + unsigned int dp_mix; + unsigned int dp_src; + + unsigned int clr_cmp_cntl; + unsigned int gui_traj_cntl; + + unsigned int setup_cntl; + + unsigned int tex_size_pitch; + unsigned int tex_cntl; + unsigned int secondary_tex_off; + unsigned int tex_offset; +} drm_mach64_context_regs_t; + +typedef struct drm_mach64_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex dma buffer. + */ + drm_mach64_context_regs_t context_state; + unsigned int dirty; + unsigned int vertsize; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[MACH64_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int frames_queued; + + /* Texture memory LRU. + */ + struct drm_tex_region tex_list[MACH64_NR_TEX_HEAPS][MACH64_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[MACH64_NR_TEX_HEAPS]; + int ctx_owner; +} drm_mach64_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mach64_common.h) + */ + +/* Mach64 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ + +#define DRM_MACH64_INIT 0x00 +#define DRM_MACH64_IDLE 0x01 +#define DRM_MACH64_RESET 0x02 +#define DRM_MACH64_SWAP 0x03 +#define DRM_MACH64_CLEAR 0x04 +#define DRM_MACH64_VERTEX 0x05 +#define DRM_MACH64_BLIT 0x06 +#define DRM_MACH64_FLUSH 0x07 +#define DRM_MACH64_GETPARAM 0x08 + +#define DRM_IOCTL_MACH64_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_INIT, drm_mach64_init_t) +#define DRM_IOCTL_MACH64_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_IDLE ) +#define DRM_IOCTL_MACH64_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_RESET ) +#define DRM_IOCTL_MACH64_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_SWAP ) +#define DRM_IOCTL_MACH64_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_CLEAR, drm_mach64_clear_t) +#define DRM_IOCTL_MACH64_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_VERTEX, drm_mach64_vertex_t) +#define DRM_IOCTL_MACH64_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MACH64_BLIT, drm_mach64_blit_t) +#define DRM_IOCTL_MACH64_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_MACH64_FLUSH ) +#define DRM_IOCTL_MACH64_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_MACH64_GETPARAM, drm_mach64_getparam_t) + +/* Buffer flags for clears + */ +#define MACH64_FRONT 0x1 +#define MACH64_BACK 0x2 +#define MACH64_DEPTH 0x4 + +/* Primitive types for vertex buffers + */ +#define MACH64_PRIM_POINTS 0x00000000 +#define MACH64_PRIM_LINES 0x00000001 +#define MACH64_PRIM_LINE_LOOP 0x00000002 +#define MACH64_PRIM_LINE_STRIP 0x00000003 +#define MACH64_PRIM_TRIANGLES 0x00000004 +#define MACH64_PRIM_TRIANGLE_STRIP 0x00000005 +#define MACH64_PRIM_TRIANGLE_FAN 0x00000006 +#define MACH64_PRIM_QUADS 0x00000007 +#define MACH64_PRIM_QUAD_STRIP 0x00000008 +#define MACH64_PRIM_POLYGON 0x00000009 + +typedef enum _drm_mach64_dma_mode_t { + MACH64_MODE_DMA_ASYNC, + MACH64_MODE_DMA_SYNC, + MACH64_MODE_MMIO +} drm_mach64_dma_mode_t; + +typedef struct drm_mach64_init { + enum { + DRM_MACH64_INIT_DMA = 0x01, + DRM_MACH64_CLEANUP_DMA = 0x02 + } func; + + unsigned long sarea_priv_offset; + int is_pci; + drm_mach64_dma_mode_t dma_mode; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; +} drm_mach64_init_t; + +typedef struct drm_mach64_clear { + unsigned int flags; + int x, y, w, h; + unsigned int clear_color; + unsigned int clear_depth; +} drm_mach64_clear_t; + +typedef struct drm_mach64_vertex { + int prim; + void *buf; /* Address of vertex buffer */ + unsigned long used; /* Number of bytes in buffer */ + int discard; /* Client finished with buffer? */ +} drm_mach64_vertex_t; + +typedef struct drm_mach64_blit { + void *buf; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_mach64_blit_t; + +typedef struct drm_mach64_getparam { + enum { + MACH64_PARAM_FRAMES_QUEUED = 0x01, + MACH64_PARAM_IRQ_NR = 0x02 + } param; + void *value; +} drm_mach64_getparam_t; + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mga_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mga_drm.h new file mode 100644 index 0000000..7930011 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/mga_drm.h @@ -0,0 +1,427 @@ +/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jeff Hartmann + * Keith Whitwell + * + * Rewritten by: + * Gareth Hughes + */ + +#ifndef __MGA_DRM_H__ +#define __MGA_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (mga_sarea.h) + */ + +#ifndef __MGA_SAREA_DEFINES__ +#define __MGA_SAREA_DEFINES__ + +/* WARP pipe flags + */ +#define MGA_F 0x1 /* fog */ +#define MGA_A 0x2 /* alpha */ +#define MGA_S 0x4 /* specular */ +#define MGA_T2 0x8 /* multitexture */ + +#define MGA_WARP_TGZ 0 +#define MGA_WARP_TGZF (MGA_F) +#define MGA_WARP_TGZA (MGA_A) +#define MGA_WARP_TGZAF (MGA_F|MGA_A) +#define MGA_WARP_TGZS (MGA_S) +#define MGA_WARP_TGZSF (MGA_S|MGA_F) +#define MGA_WARP_TGZSA (MGA_S|MGA_A) +#define MGA_WARP_TGZSAF (MGA_S|MGA_F|MGA_A) +#define MGA_WARP_T2GZ (MGA_T2) +#define MGA_WARP_T2GZF (MGA_T2|MGA_F) +#define MGA_WARP_T2GZA (MGA_T2|MGA_A) +#define MGA_WARP_T2GZAF (MGA_T2|MGA_A|MGA_F) +#define MGA_WARP_T2GZS (MGA_T2|MGA_S) +#define MGA_WARP_T2GZSF (MGA_T2|MGA_S|MGA_F) +#define MGA_WARP_T2GZSA (MGA_T2|MGA_S|MGA_A) +#define MGA_WARP_T2GZSAF (MGA_T2|MGA_S|MGA_F|MGA_A) + +#define MGA_MAX_G200_PIPES 8 /* no multitex */ +#define MGA_MAX_G400_PIPES 16 +#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES +#define MGA_WARP_UCODE_SIZE 32768 /* in bytes */ + +#define MGA_CARD_TYPE_G200 1 +#define MGA_CARD_TYPE_G400 2 +#define MGA_CARD_TYPE_G450 3 /* not currently used */ +#define MGA_CARD_TYPE_G550 4 + +#define MGA_FRONT 0x1 +#define MGA_BACK 0x2 +#define MGA_DEPTH 0x4 + +/* What needs to be changed for the current vertex dma buffer? + */ +#define MGA_UPLOAD_CONTEXT 0x1 +#define MGA_UPLOAD_TEX0 0x2 +#define MGA_UPLOAD_TEX1 0x4 +#define MGA_UPLOAD_PIPE 0x8 +#define MGA_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */ +#define MGA_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */ +#define MGA_UPLOAD_2D 0x40 +#define MGA_WAIT_AGE 0x80 /* handled client-side */ +#define MGA_UPLOAD_CLIPRECTS 0x100 /* handled client-side */ +#if 0 +#define MGA_DMA_FLUSH 0x200 /* set when someone gets the lock + quiescent */ +#endif + +/* 32 buffers of 64k each, total 2 meg. + */ +#define MGA_BUFFER_SIZE (1 << 16) +#define MGA_NUM_BUFFERS 128 + +/* Keep these small for testing. + */ +#define MGA_NR_SAREA_CLIPRECTS 8 + +/* 2 heaps (1 for card, 1 for agp), each divided into up to 128 + * regions, subject to a minimum region size of (1<<16) == 64k. + * + * Clients may subdivide regions internally, but when sharing between + * clients, the region size is the minimum granularity. + */ + +#define MGA_CARD_HEAP 0 +#define MGA_AGP_HEAP 1 +#define MGA_NR_TEX_HEAPS 2 +#define MGA_NR_TEX_REGIONS 16 +#define MGA_LOG_MIN_TEX_REGION_SIZE 16 + +#define DRM_MGA_IDLE_RETRY 2048 + +#endif /* __MGA_SAREA_DEFINES__ */ + +/* Setup registers for 3D context + */ +typedef struct { + unsigned int dstorg; + unsigned int maccess; + unsigned int plnwt; + unsigned int dwgctl; + unsigned int alphactrl; + unsigned int fogcolor; + unsigned int wflag; + unsigned int tdualstage0; + unsigned int tdualstage1; + unsigned int fcol; + unsigned int stencil; + unsigned int stencilctl; +} drm_mga_context_regs_t; + +/* Setup registers for 2D, X server + */ +typedef struct { + unsigned int pitch; +} drm_mga_server_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int texctl; + unsigned int texctl2; + unsigned int texfilter; + unsigned int texbordercol; + unsigned int texorg; + unsigned int texwidth; + unsigned int texheight; + unsigned int texorg1; + unsigned int texorg2; + unsigned int texorg3; + unsigned int texorg4; +} drm_mga_texture_regs_t; + +/* General aging mechanism + */ +typedef struct { + unsigned int head; /* Position of head pointer */ + unsigned int wrap; /* Primary DMA wrap count */ +} drm_mga_age_t; + +typedef struct _drm_mga_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex dma buffer. + */ + drm_mga_context_regs_t context_state; + drm_mga_server_regs_t server_state; + drm_mga_texture_regs_t tex_state[2]; + unsigned int warp_pipe; + unsigned int dirty; + unsigned int vertsize; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[MGA_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Information about the most recently used 3d drawable. The + * client fills in the req_* fields, the server fills in the + * exported_ fields and puts the cliprects into boxes, above. + * + * The client clears the exported_drawable field before + * clobbering the boxes data. + */ + unsigned int req_drawable; /* the X drawable id */ + unsigned int req_draw_buffer; /* MGA_FRONT or MGA_BACK */ + + unsigned int exported_drawable; + unsigned int exported_index; + unsigned int exported_stamp; + unsigned int exported_buffers; + unsigned int exported_nfront; + unsigned int exported_nback; + int exported_back_x, exported_front_x, exported_w; + int exported_back_y, exported_front_y, exported_h; + struct drm_clip_rect exported_boxes[MGA_NR_SAREA_CLIPRECTS]; + + /* Counters for aging textures and for client-side throttling. + */ + unsigned int status[4]; + unsigned int last_wrap; + + drm_mga_age_t last_frame; + unsigned int last_enqueue; /* last time a buffer was enqueued */ + unsigned int last_dispatch; /* age of the most recently dispatched buffer */ + unsigned int last_quiescent; /* */ + + /* LRU lists for texture memory in agp space and on the card. + */ + struct drm_tex_region texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS + 1]; + unsigned int texAge[MGA_NR_TEX_HEAPS]; + + /* Mechanism to validate card state. + */ + int ctxOwner; +} drm_mga_sarea_t; + +/* MGA specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_MGA_INIT 0x00 +#define DRM_MGA_FLUSH 0x01 +#define DRM_MGA_RESET 0x02 +#define DRM_MGA_SWAP 0x03 +#define DRM_MGA_CLEAR 0x04 +#define DRM_MGA_VERTEX 0x05 +#define DRM_MGA_INDICES 0x06 +#define DRM_MGA_ILOAD 0x07 +#define DRM_MGA_BLIT 0x08 +#define DRM_MGA_GETPARAM 0x09 + +/* 3.2: + * ioctls for operating on fences. + */ +#define DRM_MGA_SET_FENCE 0x0a +#define DRM_MGA_WAIT_FENCE 0x0b +#define DRM_MGA_DMA_BOOTSTRAP 0x0c + +#define DRM_IOCTL_MGA_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_INIT, drm_mga_init_t) +#define DRM_IOCTL_MGA_FLUSH DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_FLUSH, struct drm_lock) +#define DRM_IOCTL_MGA_RESET DRM_IO( DRM_COMMAND_BASE + DRM_MGA_RESET) +#define DRM_IOCTL_MGA_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_MGA_SWAP) +#define DRM_IOCTL_MGA_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_CLEAR, drm_mga_clear_t) +#define DRM_IOCTL_MGA_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_VERTEX, drm_mga_vertex_t) +#define DRM_IOCTL_MGA_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_INDICES, drm_mga_indices_t) +#define DRM_IOCTL_MGA_ILOAD DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_ILOAD, drm_mga_iload_t) +#define DRM_IOCTL_MGA_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_BLIT, drm_mga_blit_t) +#define DRM_IOCTL_MGA_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_GETPARAM, drm_mga_getparam_t) +#define DRM_IOCTL_MGA_SET_FENCE DRM_IOW( DRM_COMMAND_BASE + DRM_MGA_SET_FENCE, __u32) +#define DRM_IOCTL_MGA_WAIT_FENCE DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_WAIT_FENCE, __u32) +#define DRM_IOCTL_MGA_DMA_BOOTSTRAP DRM_IOWR(DRM_COMMAND_BASE + DRM_MGA_DMA_BOOTSTRAP, drm_mga_dma_bootstrap_t) + +typedef struct _drm_mga_warp_index { + int installed; + unsigned long phys_addr; + int size; +} drm_mga_warp_index_t; + +typedef struct drm_mga_init { + enum { + MGA_INIT_DMA = 0x01, + MGA_CLEANUP_DMA = 0x02 + } func; + + unsigned long sarea_priv_offset; + + int chipset; + int sgram; + + unsigned int maccess; + + unsigned int fb_cpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + + unsigned int depth_cpp; + unsigned int depth_offset, depth_pitch; + + unsigned int texture_offset[MGA_NR_TEX_HEAPS]; + unsigned int texture_size[MGA_NR_TEX_HEAPS]; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long status_offset; + unsigned long warp_offset; + unsigned long primary_offset; + unsigned long buffers_offset; +} drm_mga_init_t; + +typedef struct drm_mga_dma_bootstrap { + /** + * \name AGP texture region + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, these fields will + * be filled in with the actual AGP texture settings. + * + * \warning + * If these fields are non-zero, but dma_mga_dma_bootstrap::agp_mode + * is zero, it means that PCI memory (most likely through the use of + * an IOMMU) is being used for "AGP" textures. + */ + /*@{ */ + unsigned long texture_handle; /**< Handle used to map AGP textures. */ + __u32 texture_size; /**< Size of the AGP texture region. */ + /*@} */ + + /** + * Requested size of the primary DMA region. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual AGP mode. If AGP was not available + */ + __u32 primary_size; + + /** + * Requested number of secondary DMA buffers. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual number of secondary DMA buffers + * allocated. Particularly when PCI DMA is used, this may be + * (subtantially) less than the number requested. + */ + __u32 secondary_bin_count; + + /** + * Requested size of each secondary DMA buffer. + * + * While the kernel \b is free to reduce + * dma_mga_dma_bootstrap::secondary_bin_count, it is \b not allowed + * to reduce dma_mga_dma_bootstrap::secondary_bin_size. + */ + __u32 secondary_bin_size; + + /** + * Bit-wise mask of AGPSTAT2_* values. Currently only \c AGPSTAT2_1X, + * \c AGPSTAT2_2X, and \c AGPSTAT2_4X are supported. If this value is + * zero, it means that PCI DMA should be used, even if AGP is + * possible. + * + * On return from the DRM_MGA_DMA_BOOTSTRAP ioctl, this field will be + * filled in with the actual AGP mode. If AGP was not available + * (i.e., PCI DMA was used), this value will be zero. + */ + __u32 agp_mode; + + /** + * Desired AGP GART size, measured in megabytes. + */ + __u8 agp_size; +} drm_mga_dma_bootstrap_t; + +typedef struct drm_mga_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; +} drm_mga_clear_t; + +typedef struct drm_mga_vertex { + int idx; /* buffer to queue */ + int used; /* bytes in use */ + int discard; /* client finished with buffer? */ +} drm_mga_vertex_t; + +typedef struct drm_mga_indices { + int idx; /* buffer to queue */ + unsigned int start; + unsigned int end; + int discard; /* client finished with buffer? */ +} drm_mga_indices_t; + +typedef struct drm_mga_iload { + int idx; + unsigned int dstorg; + unsigned int length; +} drm_mga_iload_t; + +typedef struct _drm_mga_blit { + unsigned int planemask; + unsigned int srcorg; + unsigned int dstorg; + int src_pitch, dst_pitch; + int delta_sx, delta_sy; + int delta_dx, delta_dy; + int height, ydir; /* flip image vertically */ + int source_pitch, dest_pitch; +} drm_mga_blit_t; + +/* 3.1: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define MGA_PARAM_IRQ_NR 1 + +/* 3.2: Query the actual card type. The DDX only distinguishes between + * G200 chips and non-G200 chips, which it calls G400. It turns out that + * there are some very sublte differences between the G4x0 chips and the G550 + * chips. Using this parameter query, a client-side driver can detect the + * difference between a G4x0 and a G550. + */ +#define MGA_PARAM_CARD_TYPE 2 + +typedef struct drm_mga_getparam { + int param; + void *value; +} drm_mga_getparam_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/nouveau_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/nouveau_drm.h new file mode 100644 index 0000000..d42105c --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/nouveau_drm.h @@ -0,0 +1,221 @@ +/* + * Copyright 2005 Stephane Marchesin. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NOUVEAU_DRM_H__ +#define __NOUVEAU_DRM_H__ + +#define NOUVEAU_DRM_HEADER_PATCHLEVEL 16 + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +struct drm_nouveau_channel_alloc { + uint32_t fb_ctxdma_handle; + uint32_t tt_ctxdma_handle; + + int channel; + uint32_t pushbuf_domains; + + /* Notifier memory */ + uint32_t notifier_handle; + + /* DRM-enforced subchannel assignments */ + struct { + uint32_t handle; + uint32_t grclass; + } subchan[8]; + uint32_t nr_subchan; +}; + +struct drm_nouveau_channel_free { + int channel; +}; + +struct drm_nouveau_grobj_alloc { + int channel; + uint32_t handle; + int class; +}; + +struct drm_nouveau_notifierobj_alloc { + uint32_t channel; + uint32_t handle; + uint32_t size; + uint32_t offset; +}; + +struct drm_nouveau_gpuobj_free { + int channel; + uint32_t handle; +}; + +/* FIXME : maybe unify {GET,SET}PARAMs */ +#define NOUVEAU_GETPARAM_PCI_VENDOR 3 +#define NOUVEAU_GETPARAM_PCI_DEVICE 4 +#define NOUVEAU_GETPARAM_BUS_TYPE 5 +#define NOUVEAU_GETPARAM_FB_PHYSICAL 6 +#define NOUVEAU_GETPARAM_AGP_PHYSICAL 7 +#define NOUVEAU_GETPARAM_FB_SIZE 8 +#define NOUVEAU_GETPARAM_AGP_SIZE 9 +#define NOUVEAU_GETPARAM_PCI_PHYSICAL 10 +#define NOUVEAU_GETPARAM_CHIPSET_ID 11 +#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12 +#define NOUVEAU_GETPARAM_GRAPH_UNITS 13 +#define NOUVEAU_GETPARAM_PTIMER_TIME 14 +#define NOUVEAU_GETPARAM_HAS_BO_USAGE 15 +#define NOUVEAU_GETPARAM_HAS_PAGEFLIP 16 +struct drm_nouveau_getparam { + uint64_t param; + uint64_t value; +}; + +struct drm_nouveau_setparam { + uint64_t param; + uint64_t value; +}; + +#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0) +#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1) +#define NOUVEAU_GEM_DOMAIN_GART (1 << 2) +#define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3) +#define NOUVEAU_GEM_DOMAIN_COHERENT (1 << 4) + +#define NOUVEAU_GEM_TILE_COMP 0x00030000 /* nv50-only */ +#define NOUVEAU_GEM_TILE_LAYOUT_MASK 0x0000ff00 +#define NOUVEAU_GEM_TILE_16BPP 0x00000001 +#define NOUVEAU_GEM_TILE_32BPP 0x00000002 +#define NOUVEAU_GEM_TILE_ZETA 0x00000004 +#define NOUVEAU_GEM_TILE_NONCONTIG 0x00000008 + +struct drm_nouveau_gem_info { + __u32 handle; + __u32 domain; + __u64 size; + __u64 offset; + __u64 map_handle; + __u32 tile_mode; + __u32 tile_flags; +}; + +struct drm_nouveau_gem_new { + struct drm_nouveau_gem_info info; + __u32 channel_hint; + __u32 align; +}; + +#define NOUVEAU_GEM_MAX_BUFFERS 1024 +struct drm_nouveau_gem_pushbuf_bo_presumed { + __u32 valid; + __u32 domain; + __u64 offset; +}; + +struct drm_nouveau_gem_pushbuf_bo { + __u64 user_priv; + __u32 handle; + __u32 read_domains; + __u32 write_domains; + __u32 valid_domains; + struct drm_nouveau_gem_pushbuf_bo_presumed presumed; +}; + +#define NOUVEAU_GEM_RELOC_LOW (1 << 0) +#define NOUVEAU_GEM_RELOC_HIGH (1 << 1) +#define NOUVEAU_GEM_RELOC_OR (1 << 2) +#define NOUVEAU_GEM_MAX_RELOCS 1024 +struct drm_nouveau_gem_pushbuf_reloc { + __u32 reloc_bo_index; + __u32 reloc_bo_offset; + __u32 bo_index; + __u32 flags; + __u32 data; + __u32 vor; + __u32 tor; +}; + +#define NOUVEAU_GEM_MAX_PUSH 512 +struct drm_nouveau_gem_pushbuf_push { + __u32 bo_index; + __u32 pad; + __u64 offset; + __u64 length; +}; + +struct drm_nouveau_gem_pushbuf { + __u32 channel; + __u32 nr_buffers; + __u64 buffers; + __u32 nr_relocs; + __u32 nr_push; + __u64 relocs; + __u64 push; + __u32 suffix0; + __u32 suffix1; + __u64 vram_available; + __u64 gart_available; +}; + +#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001 +#define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002 +#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004 +struct drm_nouveau_gem_cpu_prep { + __u32 handle; + __u32 flags; +}; + +struct drm_nouveau_gem_cpu_fini { + __u32 handle; +}; + +enum nouveau_bus_type { + NV_AGP = 0, + NV_PCI = 1, + NV_PCIE = 2, +}; + +struct drm_nouveau_sarea { +}; + +#define DRM_NOUVEAU_GETPARAM 0x00 +#define DRM_NOUVEAU_SETPARAM 0x01 +#define DRM_NOUVEAU_CHANNEL_ALLOC 0x02 +#define DRM_NOUVEAU_CHANNEL_FREE 0x03 +#define DRM_NOUVEAU_GROBJ_ALLOC 0x04 +#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x05 +#define DRM_NOUVEAU_GPUOBJ_FREE 0x06 +#define DRM_NOUVEAU_NVIF 0x07 +#define DRM_NOUVEAU_GEM_NEW 0x40 +#define DRM_NOUVEAU_GEM_PUSHBUF 0x41 +#define DRM_NOUVEAU_GEM_CPU_PREP 0x42 +#define DRM_NOUVEAU_GEM_CPU_FINI 0x43 +#define DRM_NOUVEAU_GEM_INFO 0x44 + +#if defined(__cplusplus) +} +#endif + +#endif /* __NOUVEAU_DRM_H__ */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/qxl_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/qxl_drm.h new file mode 100644 index 0000000..880999d --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/qxl_drm.h @@ -0,0 +1,158 @@ +/* + * Copyright 2013 Red Hat + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef QXL_DRM_H +#define QXL_DRM_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + * + * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel + * compatibility Keep fields aligned to their size + */ + +#define QXL_GEM_DOMAIN_CPU 0 +#define QXL_GEM_DOMAIN_VRAM 1 +#define QXL_GEM_DOMAIN_SURFACE 2 + +#define DRM_QXL_ALLOC 0x00 +#define DRM_QXL_MAP 0x01 +#define DRM_QXL_EXECBUFFER 0x02 +#define DRM_QXL_UPDATE_AREA 0x03 +#define DRM_QXL_GETPARAM 0x04 +#define DRM_QXL_CLIENTCAP 0x05 + +#define DRM_QXL_ALLOC_SURF 0x06 + +struct drm_qxl_alloc { + __u32 size; + __u32 handle; /* 0 is an invalid handle */ +}; + +struct drm_qxl_map { + __u64 offset; /* use for mmap system call */ + __u32 handle; + __u32 pad; +}; + +/* + * dest is the bo we are writing the relocation into + * src is bo we are relocating. + * *(dest_handle.base_addr + dest_offset) = physical_address(src_handle.addr + + * src_offset) + */ +#define QXL_RELOC_TYPE_BO 1 +#define QXL_RELOC_TYPE_SURF 2 + +struct drm_qxl_reloc { + __u64 src_offset; /* offset into src_handle or src buffer */ + __u64 dst_offset; /* offset in dest handle */ + __u32 src_handle; /* dest handle to compute address from */ + __u32 dst_handle; /* 0 if to command buffer */ + __u32 reloc_type; + __u32 pad; +}; + +struct drm_qxl_command { + __u64 command; /* void* */ + __u64 relocs; /* struct drm_qxl_reloc* */ + __u32 type; + __u32 command_size; + __u32 relocs_num; + __u32 pad; +}; + +struct drm_qxl_execbuffer { + __u32 flags; /* for future use */ + __u32 commands_num; + __u64 commands; /* struct drm_qxl_command* */ +}; + +struct drm_qxl_update_area { + __u32 handle; + __u32 top; + __u32 left; + __u32 bottom; + __u32 right; + __u32 pad; +}; + +#define QXL_PARAM_NUM_SURFACES 1 /* rom->n_surfaces */ +#define QXL_PARAM_MAX_RELOCS 2 +struct drm_qxl_getparam { + __u64 param; + __u64 value; +}; + +/* these are one bit values */ +struct drm_qxl_clientcap { + __u32 index; + __u32 pad; +}; + +struct drm_qxl_alloc_surf { + __u32 format; + __u32 width; + __u32 height; + __s32 stride; + __u32 handle; + __u32 pad; +}; + +#define DRM_IOCTL_QXL_ALLOC \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC, struct drm_qxl_alloc) + +#define DRM_IOCTL_QXL_MAP \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_MAP, struct drm_qxl_map) + +#define DRM_IOCTL_QXL_EXECBUFFER \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_EXECBUFFER,\ + struct drm_qxl_execbuffer) + +#define DRM_IOCTL_QXL_UPDATE_AREA \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_UPDATE_AREA,\ + struct drm_qxl_update_area) + +#define DRM_IOCTL_QXL_GETPARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_GETPARAM,\ + struct drm_qxl_getparam) + +#define DRM_IOCTL_QXL_CLIENTCAP \ + DRM_IOW(DRM_COMMAND_BASE + DRM_QXL_CLIENTCAP,\ + struct drm_qxl_clientcap) + +#define DRM_IOCTL_QXL_ALLOC_SURF \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_QXL_ALLOC_SURF,\ + struct drm_qxl_alloc_surf) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/r128_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/r128_drm.h new file mode 100644 index 0000000..bf431a0 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/r128_drm.h @@ -0,0 +1,336 @@ +/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + */ +/* + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Gareth Hughes + * Kevin E. Martin + */ + +#ifndef __R128_DRM_H__ +#define __R128_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (r128_sarea.h) + */ +#ifndef __R128_SAREA_DEFINES__ +#define __R128_SAREA_DEFINES__ + +/* What needs to be changed for the current vertex buffer? + */ +#define R128_UPLOAD_CONTEXT 0x001 +#define R128_UPLOAD_SETUP 0x002 +#define R128_UPLOAD_TEX0 0x004 +#define R128_UPLOAD_TEX1 0x008 +#define R128_UPLOAD_TEX0IMAGES 0x010 +#define R128_UPLOAD_TEX1IMAGES 0x020 +#define R128_UPLOAD_CORE 0x040 +#define R128_UPLOAD_MASKS 0x080 +#define R128_UPLOAD_WINDOW 0x100 +#define R128_UPLOAD_CLIPRECTS 0x200 /* handled client-side */ +#define R128_REQUIRE_QUIESCENCE 0x400 +#define R128_UPLOAD_ALL 0x7ff + +#define R128_FRONT 0x1 +#define R128_BACK 0x2 +#define R128_DEPTH 0x4 + +/* Primitive types + */ +#define R128_POINTS 0x1 +#define R128_LINES 0x2 +#define R128_LINE_STRIP 0x3 +#define R128_TRIANGLES 0x4 +#define R128_TRIANGLE_FAN 0x5 +#define R128_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define R128_BUFFER_SIZE 16384 + +/* Byte offsets for indirect buffer data + */ +#define R128_INDEX_PRIM_OFFSET 20 +#define R128_HOSTDATA_BLIT_OFFSET 32 + +/* Keep these small for testing. + */ +#define R128_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/AGP). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define R128_LOCAL_TEX_HEAP 0 +#define R128_AGP_TEX_HEAP 1 +#define R128_NR_TEX_HEAPS 2 +#define R128_NR_TEX_REGIONS 64 +#define R128_LOG_TEX_GRANULARITY 16 + +#define R128_NR_CONTEXT_REGS 12 + +#define R128_MAX_TEXTURE_LEVELS 11 +#define R128_MAX_TEXTURE_UNITS 2 + +#endif /* __R128_SAREA_DEFINES__ */ + +typedef struct { + /* Context state - can be written in one large chunk */ + unsigned int dst_pitch_offset_c; + unsigned int dp_gui_master_cntl_c; + unsigned int sc_top_left_c; + unsigned int sc_bottom_right_c; + unsigned int z_offset_c; + unsigned int z_pitch_c; + unsigned int z_sten_cntl_c; + unsigned int tex_cntl_c; + unsigned int misc_3d_state_cntl_reg; + unsigned int texture_clr_cmp_clr_c; + unsigned int texture_clr_cmp_msk_c; + unsigned int fog_color_c; + + /* Texture state */ + unsigned int tex_size_pitch_c; + unsigned int constant_color_c; + + /* Setup state */ + unsigned int pm4_vc_fpu_setup; + unsigned int setup_cntl; + + /* Mask state */ + unsigned int dp_write_mask; + unsigned int sten_ref_mask_c; + unsigned int plane_3d_mask_c; + + /* Window state */ + unsigned int window_xy_offset; + + /* Core state */ + unsigned int scale_3d_cntl; +} drm_r128_context_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int tex_cntl; + unsigned int tex_combine_cntl; + unsigned int tex_size_pitch; + unsigned int tex_offset[R128_MAX_TEXTURE_LEVELS]; + unsigned int tex_border_color; +} drm_r128_texture_regs_t; + +typedef struct drm_r128_sarea { + /* The channel for communication of state information to the kernel + * on firing a vertex buffer. + */ + drm_r128_context_regs_t context_state; + drm_r128_texture_regs_t tex_state[R128_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[R128_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + + struct drm_tex_region tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS + 1]; + unsigned int tex_age[R128_NR_TEX_HEAPS]; + int ctx_owner; + int pfAllowPageFlip; /* number of 3d windows (0,1,2 or more) */ + int pfCurrentPage; /* which buffer is being displayed? */ +} drm_r128_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmR128.h) + */ + +/* Rage 128 specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_R128_INIT 0x00 +#define DRM_R128_CCE_START 0x01 +#define DRM_R128_CCE_STOP 0x02 +#define DRM_R128_CCE_RESET 0x03 +#define DRM_R128_CCE_IDLE 0x04 +/* 0x05 not used */ +#define DRM_R128_RESET 0x06 +#define DRM_R128_SWAP 0x07 +#define DRM_R128_CLEAR 0x08 +#define DRM_R128_VERTEX 0x09 +#define DRM_R128_INDICES 0x0a +#define DRM_R128_BLIT 0x0b +#define DRM_R128_DEPTH 0x0c +#define DRM_R128_STIPPLE 0x0d +/* 0x0e not used */ +#define DRM_R128_INDIRECT 0x0f +#define DRM_R128_FULLSCREEN 0x10 +#define DRM_R128_CLEAR2 0x11 +#define DRM_R128_GETPARAM 0x12 +#define DRM_R128_FLIP 0x13 + +#define DRM_IOCTL_R128_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_R128_INIT, drm_r128_init_t) +#define DRM_IOCTL_R128_CCE_START DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_START) +#define DRM_IOCTL_R128_CCE_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CCE_STOP, drm_r128_cce_stop_t) +#define DRM_IOCTL_R128_CCE_RESET DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_RESET) +#define DRM_IOCTL_R128_CCE_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_R128_CCE_IDLE) +/* 0x05 not used */ +#define DRM_IOCTL_R128_RESET DRM_IO( DRM_COMMAND_BASE + DRM_R128_RESET) +#define DRM_IOCTL_R128_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_R128_SWAP) +#define DRM_IOCTL_R128_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CLEAR, drm_r128_clear_t) +#define DRM_IOCTL_R128_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_R128_VERTEX, drm_r128_vertex_t) +#define DRM_IOCTL_R128_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_R128_INDICES, drm_r128_indices_t) +#define DRM_IOCTL_R128_BLIT DRM_IOW( DRM_COMMAND_BASE + DRM_R128_BLIT, drm_r128_blit_t) +#define DRM_IOCTL_R128_DEPTH DRM_IOW( DRM_COMMAND_BASE + DRM_R128_DEPTH, drm_r128_depth_t) +#define DRM_IOCTL_R128_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_R128_STIPPLE, drm_r128_stipple_t) +/* 0x0e not used */ +#define DRM_IOCTL_R128_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_R128_INDIRECT, drm_r128_indirect_t) +#define DRM_IOCTL_R128_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_R128_FULLSCREEN, drm_r128_fullscreen_t) +#define DRM_IOCTL_R128_CLEAR2 DRM_IOW( DRM_COMMAND_BASE + DRM_R128_CLEAR2, drm_r128_clear2_t) +#define DRM_IOCTL_R128_GETPARAM DRM_IOWR( DRM_COMMAND_BASE + DRM_R128_GETPARAM, drm_r128_getparam_t) +#define DRM_IOCTL_R128_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_R128_FLIP) + +typedef struct drm_r128_init { + enum { + R128_INIT_CCE = 0x01, + R128_CLEANUP_CCE = 0x02 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cce_mode; + int cce_secure; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + unsigned int span_offset; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; +} drm_r128_init_t; + +typedef struct drm_r128_cce_stop { + int flush; + int idle; +} drm_r128_cce_stop_t; + +typedef struct drm_r128_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; +} drm_r128_clear_t; + +typedef struct drm_r128_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_r128_vertex_t; + +typedef struct drm_r128_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_r128_indices_t; + +typedef struct drm_r128_blit { + int idx; + int pitch; + int offset; + int format; + unsigned short x, y; + unsigned short width, height; +} drm_r128_blit_t; + +typedef struct drm_r128_depth { + enum { + R128_WRITE_SPAN = 0x01, + R128_WRITE_PIXELS = 0x02, + R128_READ_SPAN = 0x03, + R128_READ_PIXELS = 0x04 + } func; + int n; + int *x; + int *y; + unsigned int *buffer; + unsigned char *mask; +} drm_r128_depth_t; + +typedef struct drm_r128_stipple { + unsigned int *mask; +} drm_r128_stipple_t; + +typedef struct drm_r128_indirect { + int idx; + int start; + int end; + int discard; +} drm_r128_indirect_t; + +typedef struct drm_r128_fullscreen { + enum { + R128_INIT_FULLSCREEN = 0x01, + R128_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_r128_fullscreen_t; + +/* 2.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define R128_PARAM_IRQ_NR 1 + +typedef struct drm_r128_getparam { + int param; + void *value; +} drm_r128_getparam_t; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/radeon_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/radeon_drm.h new file mode 100644 index 0000000..a1e385d --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/radeon_drm.h @@ -0,0 +1,1079 @@ +/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*- + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Fremont, California. + * Copyright 2002 Tungsten Graphics, Inc., Cedar Park, Texas. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Kevin E. Martin + * Gareth Hughes + * Keith Whitwell + */ + +#ifndef __RADEON_DRM_H__ +#define __RADEON_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the X server file (radeon_sarea.h) + */ +#ifndef __RADEON_SAREA_DEFINES__ +#define __RADEON_SAREA_DEFINES__ + +/* Old style state flags, required for sarea interface (1.1 and 1.2 + * clears) and 1.2 drm_vertex2 ioctl. + */ +#define RADEON_UPLOAD_CONTEXT 0x00000001 +#define RADEON_UPLOAD_VERTFMT 0x00000002 +#define RADEON_UPLOAD_LINE 0x00000004 +#define RADEON_UPLOAD_BUMPMAP 0x00000008 +#define RADEON_UPLOAD_MASKS 0x00000010 +#define RADEON_UPLOAD_VIEWPORT 0x00000020 +#define RADEON_UPLOAD_SETUP 0x00000040 +#define RADEON_UPLOAD_TCL 0x00000080 +#define RADEON_UPLOAD_MISC 0x00000100 +#define RADEON_UPLOAD_TEX0 0x00000200 +#define RADEON_UPLOAD_TEX1 0x00000400 +#define RADEON_UPLOAD_TEX2 0x00000800 +#define RADEON_UPLOAD_TEX0IMAGES 0x00001000 +#define RADEON_UPLOAD_TEX1IMAGES 0x00002000 +#define RADEON_UPLOAD_TEX2IMAGES 0x00004000 +#define RADEON_UPLOAD_CLIPRECTS 0x00008000 /* handled client-side */ +#define RADEON_REQUIRE_QUIESCENCE 0x00010000 +#define RADEON_UPLOAD_ZBIAS 0x00020000 /* version 1.2 and newer */ +#define RADEON_UPLOAD_ALL 0x003effff +#define RADEON_UPLOAD_CONTEXT_ALL 0x003e01ff + +/* New style per-packet identifiers for use in cmd_buffer ioctl with + * the RADEON_EMIT_PACKET command. Comments relate new packets to old + * state bits and the packet size: + */ +#define RADEON_EMIT_PP_MISC 0 /* context/7 */ +#define RADEON_EMIT_PP_CNTL 1 /* context/3 */ +#define RADEON_EMIT_RB3D_COLORPITCH 2 /* context/1 */ +#define RADEON_EMIT_RE_LINE_PATTERN 3 /* line/2 */ +#define RADEON_EMIT_SE_LINE_WIDTH 4 /* line/1 */ +#define RADEON_EMIT_PP_LUM_MATRIX 5 /* bumpmap/1 */ +#define RADEON_EMIT_PP_ROT_MATRIX_0 6 /* bumpmap/2 */ +#define RADEON_EMIT_RB3D_STENCILREFMASK 7 /* masks/3 */ +#define RADEON_EMIT_SE_VPORT_XSCALE 8 /* viewport/6 */ +#define RADEON_EMIT_SE_CNTL 9 /* setup/2 */ +#define RADEON_EMIT_SE_CNTL_STATUS 10 /* setup/1 */ +#define RADEON_EMIT_RE_MISC 11 /* misc/1 */ +#define RADEON_EMIT_PP_TXFILTER_0 12 /* tex0/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_0 13 /* tex0/1 */ +#define RADEON_EMIT_PP_TXFILTER_1 14 /* tex1/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_1 15 /* tex1/1 */ +#define RADEON_EMIT_PP_TXFILTER_2 16 /* tex2/6 */ +#define RADEON_EMIT_PP_BORDER_COLOR_2 17 /* tex2/1 */ +#define RADEON_EMIT_SE_ZBIAS_FACTOR 18 /* zbias/2 */ +#define RADEON_EMIT_SE_TCL_OUTPUT_VTX_FMT 19 /* tcl/11 */ +#define RADEON_EMIT_SE_TCL_MATERIAL_EMMISSIVE_RED 20 /* material/17 */ +#define R200_EMIT_PP_TXCBLEND_0 21 /* tex0/4 */ +#define R200_EMIT_PP_TXCBLEND_1 22 /* tex1/4 */ +#define R200_EMIT_PP_TXCBLEND_2 23 /* tex2/4 */ +#define R200_EMIT_PP_TXCBLEND_3 24 /* tex3/4 */ +#define R200_EMIT_PP_TXCBLEND_4 25 /* tex4/4 */ +#define R200_EMIT_PP_TXCBLEND_5 26 /* tex5/4 */ +#define R200_EMIT_PP_TXCBLEND_6 27 /* /4 */ +#define R200_EMIT_PP_TXCBLEND_7 28 /* /4 */ +#define R200_EMIT_TCL_LIGHT_MODEL_CTL_0 29 /* tcl/7 */ +#define R200_EMIT_TFACTOR_0 30 /* tf/7 */ +#define R200_EMIT_VTX_FMT_0 31 /* vtx/5 */ +#define R200_EMIT_VAP_CTL 32 /* vap/1 */ +#define R200_EMIT_MATRIX_SELECT_0 33 /* msl/5 */ +#define R200_EMIT_TEX_PROC_CTL_2 34 /* tcg/5 */ +#define R200_EMIT_TCL_UCP_VERT_BLEND_CTL 35 /* tcl/1 */ +#define R200_EMIT_PP_TXFILTER_0 36 /* tex0/6 */ +#define R200_EMIT_PP_TXFILTER_1 37 /* tex1/6 */ +#define R200_EMIT_PP_TXFILTER_2 38 /* tex2/6 */ +#define R200_EMIT_PP_TXFILTER_3 39 /* tex3/6 */ +#define R200_EMIT_PP_TXFILTER_4 40 /* tex4/6 */ +#define R200_EMIT_PP_TXFILTER_5 41 /* tex5/6 */ +#define R200_EMIT_PP_TXOFFSET_0 42 /* tex0/1 */ +#define R200_EMIT_PP_TXOFFSET_1 43 /* tex1/1 */ +#define R200_EMIT_PP_TXOFFSET_2 44 /* tex2/1 */ +#define R200_EMIT_PP_TXOFFSET_3 45 /* tex3/1 */ +#define R200_EMIT_PP_TXOFFSET_4 46 /* tex4/1 */ +#define R200_EMIT_PP_TXOFFSET_5 47 /* tex5/1 */ +#define R200_EMIT_VTE_CNTL 48 /* vte/1 */ +#define R200_EMIT_OUTPUT_VTX_COMP_SEL 49 /* vtx/1 */ +#define R200_EMIT_PP_TAM_DEBUG3 50 /* tam/1 */ +#define R200_EMIT_PP_CNTL_X 51 /* cst/1 */ +#define R200_EMIT_RB3D_DEPTHXY_OFFSET 52 /* cst/1 */ +#define R200_EMIT_RE_AUX_SCISSOR_CNTL 53 /* cst/1 */ +#define R200_EMIT_RE_SCISSOR_TL_0 54 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_1 55 /* cst/2 */ +#define R200_EMIT_RE_SCISSOR_TL_2 56 /* cst/2 */ +#define R200_EMIT_SE_VAP_CNTL_STATUS 57 /* cst/1 */ +#define R200_EMIT_SE_VTX_STATE_CNTL 58 /* cst/1 */ +#define R200_EMIT_RE_POINTSIZE 59 /* cst/1 */ +#define R200_EMIT_TCL_INPUT_VTX_VECTOR_ADDR_0 60 /* cst/4 */ +#define R200_EMIT_PP_CUBIC_FACES_0 61 +#define R200_EMIT_PP_CUBIC_OFFSETS_0 62 +#define R200_EMIT_PP_CUBIC_FACES_1 63 +#define R200_EMIT_PP_CUBIC_OFFSETS_1 64 +#define R200_EMIT_PP_CUBIC_FACES_2 65 +#define R200_EMIT_PP_CUBIC_OFFSETS_2 66 +#define R200_EMIT_PP_CUBIC_FACES_3 67 +#define R200_EMIT_PP_CUBIC_OFFSETS_3 68 +#define R200_EMIT_PP_CUBIC_FACES_4 69 +#define R200_EMIT_PP_CUBIC_OFFSETS_4 70 +#define R200_EMIT_PP_CUBIC_FACES_5 71 +#define R200_EMIT_PP_CUBIC_OFFSETS_5 72 +#define RADEON_EMIT_PP_TEX_SIZE_0 73 +#define RADEON_EMIT_PP_TEX_SIZE_1 74 +#define RADEON_EMIT_PP_TEX_SIZE_2 75 +#define R200_EMIT_RB3D_BLENDCOLOR 76 +#define R200_EMIT_TCL_POINT_SPRITE_CNTL 77 +#define RADEON_EMIT_PP_CUBIC_FACES_0 78 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T0 79 +#define RADEON_EMIT_PP_CUBIC_FACES_1 80 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T1 81 +#define RADEON_EMIT_PP_CUBIC_FACES_2 82 +#define RADEON_EMIT_PP_CUBIC_OFFSETS_T2 83 +#define R200_EMIT_PP_TRI_PERF_CNTL 84 +#define R200_EMIT_PP_AFS_0 85 +#define R200_EMIT_PP_AFS_1 86 +#define R200_EMIT_ATF_TFACTOR 87 +#define R200_EMIT_PP_TXCTLALL_0 88 +#define R200_EMIT_PP_TXCTLALL_1 89 +#define R200_EMIT_PP_TXCTLALL_2 90 +#define R200_EMIT_PP_TXCTLALL_3 91 +#define R200_EMIT_PP_TXCTLALL_4 92 +#define R200_EMIT_PP_TXCTLALL_5 93 +#define R200_EMIT_VAP_PVS_CNTL 94 +#define RADEON_MAX_STATE_PACKETS 95 + +/* Commands understood by cmd_buffer ioctl. More can be added but + * obviously these can't be removed or changed: + */ +#define RADEON_CMD_PACKET 1 /* emit one of the register packets above */ +#define RADEON_CMD_SCALARS 2 /* emit scalar data */ +#define RADEON_CMD_VECTORS 3 /* emit vector data */ +#define RADEON_CMD_DMA_DISCARD 4 /* discard current dma buf */ +#define RADEON_CMD_PACKET3 5 /* emit hw packet */ +#define RADEON_CMD_PACKET3_CLIP 6 /* emit hw packet wrapped in cliprects */ +#define RADEON_CMD_SCALARS2 7 /* r200 stopgap */ +#define RADEON_CMD_WAIT 8 /* emit hw wait commands -- note: + * doesn't make the cpu wait, just + * the graphics hardware */ +#define RADEON_CMD_VECLINEAR 9 /* another r200 stopgap */ + +typedef union { + int i; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, packet_id, pad0, pad1; + } packet; + struct { + unsigned char cmd_type, offset, stride, count; + } scalars; + struct { + unsigned char cmd_type, offset, stride, count; + } vectors; + struct { + unsigned char cmd_type, addr_lo, addr_hi, count; + } veclinear; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; +} drm_radeon_cmd_header_t; + +#define RADEON_WAIT_2D 0x1 +#define RADEON_WAIT_3D 0x2 + +/* Allowed parameters for R300_CMD_PACKET3 + */ +#define R300_CMD_PACKET3_CLEAR 0 +#define R300_CMD_PACKET3_RAW 1 + +/* Commands understood by cmd_buffer ioctl for R300. + * The interface has not been stabilized, so some of these may be removed + * and eventually reordered before stabilization. + */ +#define R300_CMD_PACKET0 1 +#define R300_CMD_VPU 2 /* emit vertex program upload */ +#define R300_CMD_PACKET3 3 /* emit a packet3 */ +#define R300_CMD_END3D 4 /* emit sequence ending 3d rendering */ +#define R300_CMD_CP_DELAY 5 +#define R300_CMD_DMA_DISCARD 6 +#define R300_CMD_WAIT 7 +# define R300_WAIT_2D 0x1 +# define R300_WAIT_3D 0x2 +/* these two defines are DOING IT WRONG - however + * we have userspace which relies on using these. + * The wait interface is backwards compat new + * code should use the NEW_WAIT defines below + * THESE ARE NOT BIT FIELDS + */ +# define R300_WAIT_2D_CLEAN 0x3 +# define R300_WAIT_3D_CLEAN 0x4 + +# define R300_NEW_WAIT_2D_3D 0x3 +# define R300_NEW_WAIT_2D_2D_CLEAN 0x4 +# define R300_NEW_WAIT_3D_3D_CLEAN 0x6 +# define R300_NEW_WAIT_2D_2D_CLEAN_3D_3D_CLEAN 0x8 + +#define R300_CMD_SCRATCH 8 +#define R300_CMD_R500FP 9 + +typedef union { + unsigned int u; + struct { + unsigned char cmd_type, pad0, pad1, pad2; + } header; + struct { + unsigned char cmd_type, count, reglo, reghi; + } packet0; + struct { + unsigned char cmd_type, count, adrlo, adrhi; + } vpu; + struct { + unsigned char cmd_type, packet, pad0, pad1; + } packet3; + struct { + unsigned char cmd_type, packet; + unsigned short count; /* amount of packet2 to emit */ + } delay; + struct { + unsigned char cmd_type, buf_idx, pad0, pad1; + } dma; + struct { + unsigned char cmd_type, flags, pad0, pad1; + } wait; + struct { + unsigned char cmd_type, reg, n_bufs, flags; + } scratch; + struct { + unsigned char cmd_type, count, adrlo, adrhi_flags; + } r500fp; +} drm_r300_cmd_header_t; + +#define RADEON_FRONT 0x1 +#define RADEON_BACK 0x2 +#define RADEON_DEPTH 0x4 +#define RADEON_STENCIL 0x8 +#define RADEON_CLEAR_FASTZ 0x80000000 +#define RADEON_USE_HIERZ 0x40000000 +#define RADEON_USE_COMP_ZBUF 0x20000000 + +#define R500FP_CONSTANT_TYPE (1 << 1) +#define R500FP_CONSTANT_CLAMP (1 << 2) + +/* Primitive types + */ +#define RADEON_POINTS 0x1 +#define RADEON_LINES 0x2 +#define RADEON_LINE_STRIP 0x3 +#define RADEON_TRIANGLES 0x4 +#define RADEON_TRIANGLE_FAN 0x5 +#define RADEON_TRIANGLE_STRIP 0x6 + +/* Vertex/indirect buffer size + */ +#define RADEON_BUFFER_SIZE 65536 + +/* Byte offsets for indirect buffer data + */ +#define RADEON_INDEX_PRIM_OFFSET 20 + +#define RADEON_SCRATCH_REG_OFFSET 32 + +#define R600_SCRATCH_REG_OFFSET 256 + +#define RADEON_NR_SAREA_CLIPRECTS 12 + +/* There are 2 heaps (local/GART). Each region within a heap is a + * minimum of 64k, and there are at most 64 of them per heap. + */ +#define RADEON_LOCAL_TEX_HEAP 0 +#define RADEON_GART_TEX_HEAP 1 +#define RADEON_NR_TEX_HEAPS 2 +#define RADEON_NR_TEX_REGIONS 64 +#define RADEON_LOG_TEX_GRANULARITY 16 + +#define RADEON_MAX_TEXTURE_LEVELS 12 +#define RADEON_MAX_TEXTURE_UNITS 3 + +#define RADEON_MAX_SURFACES 8 + +/* Blits have strict offset rules. All blit offset must be aligned on + * a 1K-byte boundary. + */ +#define RADEON_OFFSET_SHIFT 10 +#define RADEON_OFFSET_ALIGN (1 << RADEON_OFFSET_SHIFT) +#define RADEON_OFFSET_MASK (RADEON_OFFSET_ALIGN - 1) + +#endif /* __RADEON_SAREA_DEFINES__ */ + +typedef struct { + unsigned int red; + unsigned int green; + unsigned int blue; + unsigned int alpha; +} radeon_color_regs_t; + +typedef struct { + /* Context state */ + unsigned int pp_misc; /* 0x1c14 */ + unsigned int pp_fog_color; + unsigned int re_solid_color; + unsigned int rb3d_blendcntl; + unsigned int rb3d_depthoffset; + unsigned int rb3d_depthpitch; + unsigned int rb3d_zstencilcntl; + + unsigned int pp_cntl; /* 0x1c38 */ + unsigned int rb3d_cntl; + unsigned int rb3d_coloroffset; + unsigned int re_width_height; + unsigned int rb3d_colorpitch; + unsigned int se_cntl; + + /* Vertex format state */ + unsigned int se_coord_fmt; /* 0x1c50 */ + + /* Line state */ + unsigned int re_line_pattern; /* 0x1cd0 */ + unsigned int re_line_state; + + unsigned int se_line_width; /* 0x1db8 */ + + /* Bumpmap state */ + unsigned int pp_lum_matrix; /* 0x1d00 */ + + unsigned int pp_rot_matrix_0; /* 0x1d58 */ + unsigned int pp_rot_matrix_1; + + /* Mask state */ + unsigned int rb3d_stencilrefmask; /* 0x1d7c */ + unsigned int rb3d_ropcntl; + unsigned int rb3d_planemask; + + /* Viewport state */ + unsigned int se_vport_xscale; /* 0x1d98 */ + unsigned int se_vport_xoffset; + unsigned int se_vport_yscale; + unsigned int se_vport_yoffset; + unsigned int se_vport_zscale; + unsigned int se_vport_zoffset; + + /* Setup state */ + unsigned int se_cntl_status; /* 0x2140 */ + + /* Misc state */ + unsigned int re_top_left; /* 0x26c0 */ + unsigned int re_misc; +} drm_radeon_context_regs_t; + +typedef struct { + /* Zbias state */ + unsigned int se_zbias_factor; /* 0x1dac */ + unsigned int se_zbias_constant; +} drm_radeon_context2_regs_t; + +/* Setup registers for each texture unit + */ +typedef struct { + unsigned int pp_txfilter; + unsigned int pp_txformat; + unsigned int pp_txoffset; + unsigned int pp_txcblend; + unsigned int pp_txablend; + unsigned int pp_tfactor; + unsigned int pp_border_color; +} drm_radeon_texture_regs_t; + +typedef struct { + unsigned int start; + unsigned int finish; + unsigned int prim:8; + unsigned int stateidx:8; + unsigned int numverts:16; /* overloaded as offset/64 for elt prims */ + unsigned int vc_format; /* vertex format */ +} drm_radeon_prim_t; + +typedef struct { + drm_radeon_context_regs_t context; + drm_radeon_texture_regs_t tex[RADEON_MAX_TEXTURE_UNITS]; + drm_radeon_context2_regs_t context2; + unsigned int dirty; +} drm_radeon_state_t; + +typedef struct { + /* The channel for communication of state information to the + * kernel on firing a vertex buffer with either of the + * obsoleted vertex/index ioctls. + */ + drm_radeon_context_regs_t context_state; + drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS]; + unsigned int dirty; + unsigned int vertsize; + unsigned int vc_format; + + /* The current cliprects, or a subset thereof. + */ + struct drm_clip_rect boxes[RADEON_NR_SAREA_CLIPRECTS]; + unsigned int nbox; + + /* Counters for client-side throttling of rendering clients. + */ + unsigned int last_frame; + unsigned int last_dispatch; + unsigned int last_clear; + + struct drm_tex_region tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS + + 1]; + unsigned int tex_age[RADEON_NR_TEX_HEAPS]; + int ctx_owner; + int pfState; /* number of 3d windows (0,1,2ormore) */ + int pfCurrentPage; /* which buffer is being displayed? */ + int crtc2_base; /* CRTC2 frame offset */ + int tiling_enabled; /* set by drm, read by 2d + 3d clients */ +} drm_radeon_sarea_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmRadeon.h) + * + * KW: actually it's illegal to change any of this (backwards compatibility). + */ + +/* Radeon specific ioctls + * The device specific ioctl range is 0x40 to 0x79. + */ +#define DRM_RADEON_CP_INIT 0x00 +#define DRM_RADEON_CP_START 0x01 +#define DRM_RADEON_CP_STOP 0x02 +#define DRM_RADEON_CP_RESET 0x03 +#define DRM_RADEON_CP_IDLE 0x04 +#define DRM_RADEON_RESET 0x05 +#define DRM_RADEON_FULLSCREEN 0x06 +#define DRM_RADEON_SWAP 0x07 +#define DRM_RADEON_CLEAR 0x08 +#define DRM_RADEON_VERTEX 0x09 +#define DRM_RADEON_INDICES 0x0A +#define DRM_RADEON_NOT_USED +#define DRM_RADEON_STIPPLE 0x0C +#define DRM_RADEON_INDIRECT 0x0D +#define DRM_RADEON_TEXTURE 0x0E +#define DRM_RADEON_VERTEX2 0x0F +#define DRM_RADEON_CMDBUF 0x10 +#define DRM_RADEON_GETPARAM 0x11 +#define DRM_RADEON_FLIP 0x12 +#define DRM_RADEON_ALLOC 0x13 +#define DRM_RADEON_FREE 0x14 +#define DRM_RADEON_INIT_HEAP 0x15 +#define DRM_RADEON_IRQ_EMIT 0x16 +#define DRM_RADEON_IRQ_WAIT 0x17 +#define DRM_RADEON_CP_RESUME 0x18 +#define DRM_RADEON_SETPARAM 0x19 +#define DRM_RADEON_SURF_ALLOC 0x1a +#define DRM_RADEON_SURF_FREE 0x1b +/* KMS ioctl */ +#define DRM_RADEON_GEM_INFO 0x1c +#define DRM_RADEON_GEM_CREATE 0x1d +#define DRM_RADEON_GEM_MMAP 0x1e +#define DRM_RADEON_GEM_PREAD 0x21 +#define DRM_RADEON_GEM_PWRITE 0x22 +#define DRM_RADEON_GEM_SET_DOMAIN 0x23 +#define DRM_RADEON_GEM_WAIT_IDLE 0x24 +#define DRM_RADEON_CS 0x26 +#define DRM_RADEON_INFO 0x27 +#define DRM_RADEON_GEM_SET_TILING 0x28 +#define DRM_RADEON_GEM_GET_TILING 0x29 +#define DRM_RADEON_GEM_BUSY 0x2a +#define DRM_RADEON_GEM_VA 0x2b +#define DRM_RADEON_GEM_OP 0x2c +#define DRM_RADEON_GEM_USERPTR 0x2d + +#define DRM_IOCTL_RADEON_CP_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_INIT, drm_radeon_init_t) +#define DRM_IOCTL_RADEON_CP_START DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_START) +#define DRM_IOCTL_RADEON_CP_STOP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CP_STOP, drm_radeon_cp_stop_t) +#define DRM_IOCTL_RADEON_CP_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESET) +#define DRM_IOCTL_RADEON_CP_IDLE DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_IDLE) +#define DRM_IOCTL_RADEON_RESET DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_RESET) +#define DRM_IOCTL_RADEON_FULLSCREEN DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FULLSCREEN, drm_radeon_fullscreen_t) +#define DRM_IOCTL_RADEON_SWAP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_SWAP) +#define DRM_IOCTL_RADEON_CLEAR DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CLEAR, drm_radeon_clear_t) +#define DRM_IOCTL_RADEON_VERTEX DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX, drm_radeon_vertex_t) +#define DRM_IOCTL_RADEON_INDICES DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INDICES, drm_radeon_indices_t) +#define DRM_IOCTL_RADEON_STIPPLE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_STIPPLE, drm_radeon_stipple_t) +#define DRM_IOCTL_RADEON_INDIRECT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INDIRECT, drm_radeon_indirect_t) +#define DRM_IOCTL_RADEON_TEXTURE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_TEXTURE, drm_radeon_texture_t) +#define DRM_IOCTL_RADEON_VERTEX2 DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_VERTEX2, drm_radeon_vertex2_t) +#define DRM_IOCTL_RADEON_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_CMDBUF, drm_radeon_cmd_buffer_t) +#define DRM_IOCTL_RADEON_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GETPARAM, drm_radeon_getparam_t) +#define DRM_IOCTL_RADEON_FLIP DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_FLIP) +#define DRM_IOCTL_RADEON_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_ALLOC, drm_radeon_mem_alloc_t) +#define DRM_IOCTL_RADEON_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_FREE, drm_radeon_mem_free_t) +#define DRM_IOCTL_RADEON_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_INIT_HEAP, drm_radeon_mem_init_heap_t) +#define DRM_IOCTL_RADEON_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_IRQ_EMIT, drm_radeon_irq_emit_t) +#define DRM_IOCTL_RADEON_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_IRQ_WAIT, drm_radeon_irq_wait_t) +#define DRM_IOCTL_RADEON_CP_RESUME DRM_IO( DRM_COMMAND_BASE + DRM_RADEON_CP_RESUME) +#define DRM_IOCTL_RADEON_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SETPARAM, drm_radeon_setparam_t) +#define DRM_IOCTL_RADEON_SURF_ALLOC DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_ALLOC, drm_radeon_surface_alloc_t) +#define DRM_IOCTL_RADEON_SURF_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_RADEON_SURF_FREE, drm_radeon_surface_free_t) +/* KMS */ +#define DRM_IOCTL_RADEON_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_INFO, struct drm_radeon_gem_info) +#define DRM_IOCTL_RADEON_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_CREATE, struct drm_radeon_gem_create) +#define DRM_IOCTL_RADEON_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_MMAP, struct drm_radeon_gem_mmap) +#define DRM_IOCTL_RADEON_GEM_PREAD DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PREAD, struct drm_radeon_gem_pread) +#define DRM_IOCTL_RADEON_GEM_PWRITE DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_PWRITE, struct drm_radeon_gem_pwrite) +#define DRM_IOCTL_RADEON_GEM_SET_DOMAIN DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_DOMAIN, struct drm_radeon_gem_set_domain) +#define DRM_IOCTL_RADEON_GEM_WAIT_IDLE DRM_IOW(DRM_COMMAND_BASE + DRM_RADEON_GEM_WAIT_IDLE, struct drm_radeon_gem_wait_idle) +#define DRM_IOCTL_RADEON_CS DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_CS, struct drm_radeon_cs) +#define DRM_IOCTL_RADEON_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_INFO, struct drm_radeon_info) +#define DRM_IOCTL_RADEON_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_SET_TILING, struct drm_radeon_gem_set_tiling) +#define DRM_IOCTL_RADEON_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_GET_TILING, struct drm_radeon_gem_get_tiling) +#define DRM_IOCTL_RADEON_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_BUSY, struct drm_radeon_gem_busy) +#define DRM_IOCTL_RADEON_GEM_VA DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_VA, struct drm_radeon_gem_va) +#define DRM_IOCTL_RADEON_GEM_OP DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_OP, struct drm_radeon_gem_op) +#define DRM_IOCTL_RADEON_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_RADEON_GEM_USERPTR, struct drm_radeon_gem_userptr) + +typedef struct drm_radeon_init { + enum { + RADEON_INIT_CP = 0x01, + RADEON_CLEANUP_CP = 0x02, + RADEON_INIT_R200_CP = 0x03, + RADEON_INIT_R300_CP = 0x04, + RADEON_INIT_R600_CP = 0x05 + } func; + unsigned long sarea_priv_offset; + int is_pci; + int cp_mode; + int gart_size; + int ring_size; + int usec_timeout; + + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long gart_textures_offset; +} drm_radeon_init_t; + +typedef struct drm_radeon_cp_stop { + int flush; + int idle; +} drm_radeon_cp_stop_t; + +typedef struct drm_radeon_fullscreen { + enum { + RADEON_INIT_FULLSCREEN = 0x01, + RADEON_CLEANUP_FULLSCREEN = 0x02 + } func; +} drm_radeon_fullscreen_t; + +#define CLEAR_X1 0 +#define CLEAR_Y1 1 +#define CLEAR_X2 2 +#define CLEAR_Y2 3 +#define CLEAR_DEPTH 4 + +typedef union drm_radeon_clear_rect { + float f[5]; + unsigned int ui[5]; +} drm_radeon_clear_rect_t; + +typedef struct drm_radeon_clear { + unsigned int flags; + unsigned int clear_color; + unsigned int clear_depth; + unsigned int color_mask; + unsigned int depth_mask; /* misnamed field: should be stencil */ + drm_radeon_clear_rect_t *depth_boxes; +} drm_radeon_clear_t; + +typedef struct drm_radeon_vertex { + int prim; + int idx; /* Index of vertex buffer */ + int count; /* Number of vertices in buffer */ + int discard; /* Client finished with buffer? */ +} drm_radeon_vertex_t; + +typedef struct drm_radeon_indices { + int prim; + int idx; + int start; + int end; + int discard; /* Client finished with buffer? */ +} drm_radeon_indices_t; + +/* v1.2 - obsoletes drm_radeon_vertex and drm_radeon_indices + * - allows multiple primitives and state changes in a single ioctl + * - supports driver change to emit native primitives + */ +typedef struct drm_radeon_vertex2 { + int idx; /* Index of vertex buffer */ + int discard; /* Client finished with buffer? */ + int nr_states; + drm_radeon_state_t *state; + int nr_prims; + drm_radeon_prim_t *prim; +} drm_radeon_vertex2_t; + +/* v1.3 - obsoletes drm_radeon_vertex2 + * - allows arbitrarily large cliprect list + * - allows updating of tcl packet, vector and scalar state + * - allows memory-efficient description of state updates + * - allows state to be emitted without a primitive + * (for clears, ctx switches) + * - allows more than one dma buffer to be referenced per ioctl + * - supports tcl driver + * - may be extended in future versions with new cmd types, packets + */ +typedef struct drm_radeon_cmd_buffer { + int bufsz; + char *buf; + int nbox; + struct drm_clip_rect *boxes; +} drm_radeon_cmd_buffer_t; + +typedef struct drm_radeon_tex_image { + unsigned int x, y; /* Blit coordinates */ + unsigned int width, height; + const void *data; +} drm_radeon_tex_image_t; + +typedef struct drm_radeon_texture { + unsigned int offset; + int pitch; + int format; + int width; /* Texture image coordinates */ + int height; + drm_radeon_tex_image_t *image; +} drm_radeon_texture_t; + +typedef struct drm_radeon_stipple { + unsigned int *mask; +} drm_radeon_stipple_t; + +typedef struct drm_radeon_indirect { + int idx; + int start; + int end; + int discard; +} drm_radeon_indirect_t; + +/* enum for card type parameters */ +#define RADEON_CARD_PCI 0 +#define RADEON_CARD_AGP 1 +#define RADEON_CARD_PCIE 2 + +/* 1.3: An ioctl to get parameters that aren't available to the 3d + * client any other way. + */ +#define RADEON_PARAM_GART_BUFFER_OFFSET 1 /* card offset of 1st GART buffer */ +#define RADEON_PARAM_LAST_FRAME 2 +#define RADEON_PARAM_LAST_DISPATCH 3 +#define RADEON_PARAM_LAST_CLEAR 4 +/* Added with DRM version 1.6. */ +#define RADEON_PARAM_IRQ_NR 5 +#define RADEON_PARAM_GART_BASE 6 /* card offset of GART base */ +/* Added with DRM version 1.8. */ +#define RADEON_PARAM_REGISTER_HANDLE 7 /* for drmMap() */ +#define RADEON_PARAM_STATUS_HANDLE 8 +#define RADEON_PARAM_SAREA_HANDLE 9 +#define RADEON_PARAM_GART_TEX_HANDLE 10 +#define RADEON_PARAM_SCRATCH_OFFSET 11 +#define RADEON_PARAM_CARD_TYPE 12 +#define RADEON_PARAM_VBLANK_CRTC 13 /* VBLANK CRTC */ +#define RADEON_PARAM_FB_LOCATION 14 /* FB location */ +#define RADEON_PARAM_NUM_GB_PIPES 15 /* num GB pipes */ +#define RADEON_PARAM_DEVICE_ID 16 +#define RADEON_PARAM_NUM_Z_PIPES 17 /* num Z pipes */ + +typedef struct drm_radeon_getparam { + int param; + void *value; +} drm_radeon_getparam_t; + +/* 1.6: Set up a memory manager for regions of shared memory: + */ +#define RADEON_MEM_REGION_GART 1 +#define RADEON_MEM_REGION_FB 2 + +typedef struct drm_radeon_mem_alloc { + int region; + int alignment; + int size; + int *region_offset; /* offset from start of fb or GART */ +} drm_radeon_mem_alloc_t; + +typedef struct drm_radeon_mem_free { + int region; + int region_offset; +} drm_radeon_mem_free_t; + +typedef struct drm_radeon_mem_init_heap { + int region; + int size; + int start; +} drm_radeon_mem_init_heap_t; + +/* 1.6: Userspace can request & wait on irq's: + */ +typedef struct drm_radeon_irq_emit { + int *irq_seq; +} drm_radeon_irq_emit_t; + +typedef struct drm_radeon_irq_wait { + int irq_seq; +} drm_radeon_irq_wait_t; + +/* 1.10: Clients tell the DRM where they think the framebuffer is located in + * the card's address space, via a new generic ioctl to set parameters + */ + +typedef struct drm_radeon_setparam { + unsigned int param; + __s64 value; +} drm_radeon_setparam_t; + +#define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ +#define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ +#define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ +#define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ +#define RADEON_SETPARAM_PCIGART_TABLE_SIZE 5 /* PCI GART Table Size */ +#define RADEON_SETPARAM_VBLANK_CRTC 6 /* VBLANK CRTC */ +/* 1.14: Clients can allocate/free a surface + */ +typedef struct drm_radeon_surface_alloc { + unsigned int address; + unsigned int size; + unsigned int flags; +} drm_radeon_surface_alloc_t; + +typedef struct drm_radeon_surface_free { + unsigned int address; +} drm_radeon_surface_free_t; + +#define DRM_RADEON_VBLANK_CRTC1 1 +#define DRM_RADEON_VBLANK_CRTC2 2 + +/* + * Kernel modesetting world below. + */ +#define RADEON_GEM_DOMAIN_CPU 0x1 +#define RADEON_GEM_DOMAIN_GTT 0x2 +#define RADEON_GEM_DOMAIN_VRAM 0x4 + +struct drm_radeon_gem_info { + __u64 gart_size; + __u64 vram_size; + __u64 vram_visible; +}; + +#define RADEON_GEM_NO_BACKING_STORE (1 << 0) +#define RADEON_GEM_GTT_UC (1 << 1) +#define RADEON_GEM_GTT_WC (1 << 2) +/* BO is expected to be accessed by the CPU */ +#define RADEON_GEM_CPU_ACCESS (1 << 3) +/* CPU access is not expected to work for this BO */ +#define RADEON_GEM_NO_CPU_ACCESS (1 << 4) + +struct drm_radeon_gem_create { + __u64 size; + __u64 alignment; + __u32 handle; + __u32 initial_domain; + __u32 flags; +}; + +/* + * This is not a reliable API and you should expect it to fail for any + * number of reasons and have fallback path that do not use userptr to + * perform any operation. + */ +#define RADEON_GEM_USERPTR_READONLY (1 << 0) +#define RADEON_GEM_USERPTR_ANONONLY (1 << 1) +#define RADEON_GEM_USERPTR_VALIDATE (1 << 2) +#define RADEON_GEM_USERPTR_REGISTER (1 << 3) + +struct drm_radeon_gem_userptr { + __u64 addr; + __u64 size; + __u32 flags; + __u32 handle; +}; + +#define RADEON_TILING_MACRO 0x1 +#define RADEON_TILING_MICRO 0x2 +#define RADEON_TILING_SWAP_16BIT 0x4 +#define RADEON_TILING_R600_NO_SCANOUT RADEON_TILING_SWAP_16BIT +#define RADEON_TILING_SWAP_32BIT 0x8 +/* this object requires a surface when mapped - i.e. front buffer */ +#define RADEON_TILING_SURFACE 0x10 +#define RADEON_TILING_MICRO_SQUARE 0x20 +#define RADEON_TILING_EG_BANKW_SHIFT 8 +#define RADEON_TILING_EG_BANKW_MASK 0xf +#define RADEON_TILING_EG_BANKH_SHIFT 12 +#define RADEON_TILING_EG_BANKH_MASK 0xf +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT 16 +#define RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK 0xf +#define RADEON_TILING_EG_TILE_SPLIT_SHIFT 24 +#define RADEON_TILING_EG_TILE_SPLIT_MASK 0xf +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT 28 +#define RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK 0xf + +struct drm_radeon_gem_set_tiling { + __u32 handle; + __u32 tiling_flags; + __u32 pitch; +}; + +struct drm_radeon_gem_get_tiling { + __u32 handle; + __u32 tiling_flags; + __u32 pitch; +}; + +struct drm_radeon_gem_mmap { + __u32 handle; + __u32 pad; + __u64 offset; + __u64 size; + __u64 addr_ptr; +}; + +struct drm_radeon_gem_set_domain { + __u32 handle; + __u32 read_domains; + __u32 write_domain; +}; + +struct drm_radeon_gem_wait_idle { + __u32 handle; + __u32 pad; +}; + +struct drm_radeon_gem_busy { + __u32 handle; + __u32 domain; +}; + +struct drm_radeon_gem_pread { + /** Handle for the object being read. */ + __u32 handle; + __u32 pad; + /** Offset into the object to read from */ + __u64 offset; + /** Length of data to read */ + __u64 size; + /** Pointer to write the data into. */ + /* void *, but pointers are not 32/64 compatible */ + __u64 data_ptr; +}; + +struct drm_radeon_gem_pwrite { + /** Handle for the object being written to. */ + __u32 handle; + __u32 pad; + /** Offset into the object to write to */ + __u64 offset; + /** Length of data to write */ + __u64 size; + /** Pointer to read the data from. */ + /* void *, but pointers are not 32/64 compatible */ + __u64 data_ptr; +}; + +/* Sets or returns a value associated with a buffer. */ +struct drm_radeon_gem_op { + __u32 handle; /* buffer */ + __u32 op; /* RADEON_GEM_OP_* */ + __u64 value; /* input or return value */ +}; + +#define RADEON_GEM_OP_GET_INITIAL_DOMAIN 0 +#define RADEON_GEM_OP_SET_INITIAL_DOMAIN 1 + +#define RADEON_VA_MAP 1 +#define RADEON_VA_UNMAP 2 + +#define RADEON_VA_RESULT_OK 0 +#define RADEON_VA_RESULT_ERROR 1 +#define RADEON_VA_RESULT_VA_EXIST 2 + +#define RADEON_VM_PAGE_VALID (1 << 0) +#define RADEON_VM_PAGE_READABLE (1 << 1) +#define RADEON_VM_PAGE_WRITEABLE (1 << 2) +#define RADEON_VM_PAGE_SYSTEM (1 << 3) +#define RADEON_VM_PAGE_SNOOPED (1 << 4) + +struct drm_radeon_gem_va { + __u32 handle; + __u32 operation; + __u32 vm_id; + __u32 flags; + __u64 offset; +}; + +#define RADEON_CHUNK_ID_RELOCS 0x01 +#define RADEON_CHUNK_ID_IB 0x02 +#define RADEON_CHUNK_ID_FLAGS 0x03 +#define RADEON_CHUNK_ID_CONST_IB 0x04 + +/* The first dword of RADEON_CHUNK_ID_FLAGS is a uint32 of these flags: */ +#define RADEON_CS_KEEP_TILING_FLAGS 0x01 +#define RADEON_CS_USE_VM 0x02 +#define RADEON_CS_END_OF_FRAME 0x04 /* a hint from userspace which CS is the last one */ +/* The second dword of RADEON_CHUNK_ID_FLAGS is a uint32 that sets the ring type */ +#define RADEON_CS_RING_GFX 0 +#define RADEON_CS_RING_COMPUTE 1 +#define RADEON_CS_RING_DMA 2 +#define RADEON_CS_RING_UVD 3 +#define RADEON_CS_RING_VCE 4 +/* The third dword of RADEON_CHUNK_ID_FLAGS is a sint32 that sets the priority */ +/* 0 = normal, + = higher priority, - = lower priority */ + +struct drm_radeon_cs_chunk { + __u32 chunk_id; + __u32 length_dw; + __u64 chunk_data; +}; + +/* drm_radeon_cs_reloc.flags */ +#define RADEON_RELOC_PRIO_MASK (0xf << 0) + +struct drm_radeon_cs_reloc { + __u32 handle; + __u32 read_domains; + __u32 write_domain; + __u32 flags; +}; + +struct drm_radeon_cs { + __u32 num_chunks; + __u32 cs_id; + /* this points to __u64 * which point to cs chunks */ + __u64 chunks; + /* updates to the limits after this CS ioctl */ + __u64 gart_limit; + __u64 vram_limit; +}; + +#define RADEON_INFO_DEVICE_ID 0x00 +#define RADEON_INFO_NUM_GB_PIPES 0x01 +#define RADEON_INFO_NUM_Z_PIPES 0x02 +#define RADEON_INFO_ACCEL_WORKING 0x03 +#define RADEON_INFO_CRTC_FROM_ID 0x04 +#define RADEON_INFO_ACCEL_WORKING2 0x05 +#define RADEON_INFO_TILING_CONFIG 0x06 +#define RADEON_INFO_WANT_HYPERZ 0x07 +#define RADEON_INFO_WANT_CMASK 0x08 /* get access to CMASK on r300 */ +#define RADEON_INFO_CLOCK_CRYSTAL_FREQ 0x09 /* clock crystal frequency */ +#define RADEON_INFO_NUM_BACKENDS 0x0a /* DB/backends for r600+ - need for OQ */ +#define RADEON_INFO_NUM_TILE_PIPES 0x0b /* tile pipes for r600+ */ +#define RADEON_INFO_FUSION_GART_WORKING 0x0c /* fusion writes to GTT were broken before this */ +#define RADEON_INFO_BACKEND_MAP 0x0d /* pipe to backend map, needed by mesa */ +/* virtual address start, va < start are reserved by the kernel */ +#define RADEON_INFO_VA_START 0x0e +/* maximum size of ib using the virtual memory cs */ +#define RADEON_INFO_IB_VM_MAX_SIZE 0x0f +/* max pipes - needed for compute shaders */ +#define RADEON_INFO_MAX_PIPES 0x10 +/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */ +#define RADEON_INFO_TIMESTAMP 0x11 +/* max shader engines (SE) - needed for geometry shaders, etc. */ +#define RADEON_INFO_MAX_SE 0x12 +/* max SH per SE */ +#define RADEON_INFO_MAX_SH_PER_SE 0x13 +/* fast fb access is enabled */ +#define RADEON_INFO_FASTFB_WORKING 0x14 +/* query if a RADEON_CS_RING_* submission is supported */ +#define RADEON_INFO_RING_WORKING 0x15 +/* SI tile mode array */ +#define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16 +/* query if CP DMA is supported on the compute ring */ +#define RADEON_INFO_SI_CP_DMA_COMPUTE 0x17 +/* CIK macrotile mode array */ +#define RADEON_INFO_CIK_MACROTILE_MODE_ARRAY 0x18 +/* query the number of render backends */ +#define RADEON_INFO_SI_BACKEND_ENABLED_MASK 0x19 +/* max engine clock - needed for OpenCL */ +#define RADEON_INFO_MAX_SCLK 0x1a +/* version of VCE firmware */ +#define RADEON_INFO_VCE_FW_VERSION 0x1b +/* version of VCE feedback */ +#define RADEON_INFO_VCE_FB_VERSION 0x1c +#define RADEON_INFO_NUM_BYTES_MOVED 0x1d +#define RADEON_INFO_VRAM_USAGE 0x1e +#define RADEON_INFO_GTT_USAGE 0x1f +#define RADEON_INFO_ACTIVE_CU_COUNT 0x20 +#define RADEON_INFO_CURRENT_GPU_TEMP 0x21 +#define RADEON_INFO_CURRENT_GPU_SCLK 0x22 +#define RADEON_INFO_CURRENT_GPU_MCLK 0x23 +#define RADEON_INFO_READ_REG 0x24 +#define RADEON_INFO_VA_UNMAP_WORKING 0x25 +#define RADEON_INFO_GPU_RESET_COUNTER 0x26 + +struct drm_radeon_info { + __u32 request; + __u32 pad; + __u64 value; +}; + +/* Those correspond to the tile index to use, this is to explicitly state + * the API that is implicitly defined by the tile mode array. + */ +#define SI_TILE_MODE_COLOR_LINEAR_ALIGNED 8 +#define SI_TILE_MODE_COLOR_1D 13 +#define SI_TILE_MODE_COLOR_1D_SCANOUT 9 +#define SI_TILE_MODE_COLOR_2D_8BPP 14 +#define SI_TILE_MODE_COLOR_2D_16BPP 15 +#define SI_TILE_MODE_COLOR_2D_32BPP 16 +#define SI_TILE_MODE_COLOR_2D_64BPP 17 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_16BPP 11 +#define SI_TILE_MODE_COLOR_2D_SCANOUT_32BPP 12 +#define SI_TILE_MODE_DEPTH_STENCIL_1D 4 +#define SI_TILE_MODE_DEPTH_STENCIL_2D 0 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_2AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_4AA 3 +#define SI_TILE_MODE_DEPTH_STENCIL_2D_8AA 2 + +#define CIK_TILE_MODE_DEPTH_STENCIL_1D 5 + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/savage_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/savage_drm.h new file mode 100644 index 0000000..1a91234 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/savage_drm.h @@ -0,0 +1,220 @@ +/* savage_drm.h -- Public header for the savage driver + * + * Copyright 2004 Felix Kuehling + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SAVAGE_DRM_H__ +#define __SAVAGE_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef __SAVAGE_SAREA_DEFINES__ +#define __SAVAGE_SAREA_DEFINES__ + +/* 2 heaps (1 for card, 1 for agp), each divided into up to 128 + * regions, subject to a minimum region size of (1<<16) == 64k. + * + * Clients may subdivide regions internally, but when sharing between + * clients, the region size is the minimum granularity. + */ + +#define SAVAGE_CARD_HEAP 0 +#define SAVAGE_AGP_HEAP 1 +#define SAVAGE_NR_TEX_HEAPS 2 +#define SAVAGE_NR_TEX_REGIONS 16 +#define SAVAGE_LOG_MIN_TEX_REGION_SIZE 16 + +#endif /* __SAVAGE_SAREA_DEFINES__ */ + +typedef struct _drm_savage_sarea { + /* LRU lists for texture memory in agp space and on the card. + */ + struct drm_tex_region texList[SAVAGE_NR_TEX_HEAPS][SAVAGE_NR_TEX_REGIONS + + 1]; + unsigned int texAge[SAVAGE_NR_TEX_HEAPS]; + + /* Mechanism to validate card state. + */ + int ctxOwner; +} drm_savage_sarea_t, *drm_savage_sarea_ptr; + +/* Savage-specific ioctls + */ +#define DRM_SAVAGE_BCI_INIT 0x00 +#define DRM_SAVAGE_BCI_CMDBUF 0x01 +#define DRM_SAVAGE_BCI_EVENT_EMIT 0x02 +#define DRM_SAVAGE_BCI_EVENT_WAIT 0x03 + +#define DRM_IOCTL_SAVAGE_BCI_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_INIT, drm_savage_init_t) +#define DRM_IOCTL_SAVAGE_BCI_CMDBUF DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_CMDBUF, drm_savage_cmdbuf_t) +#define DRM_IOCTL_SAVAGE_BCI_EVENT_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_EMIT, drm_savage_event_emit_t) +#define DRM_IOCTL_SAVAGE_BCI_EVENT_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_WAIT, drm_savage_event_wait_t) + +#define SAVAGE_DMA_PCI 1 +#define SAVAGE_DMA_AGP 3 +typedef struct drm_savage_init { + enum { + SAVAGE_INIT_BCI = 1, + SAVAGE_CLEANUP_BCI = 2 + } func; + unsigned int sarea_priv_offset; + + /* some parameters */ + unsigned int cob_size; + unsigned int bci_threshold_lo, bci_threshold_hi; + unsigned int dma_type; + + /* frame buffer layout */ + unsigned int fb_bpp; + unsigned int front_offset, front_pitch; + unsigned int back_offset, back_pitch; + unsigned int depth_bpp; + unsigned int depth_offset, depth_pitch; + + /* local textures */ + unsigned int texture_offset; + unsigned int texture_size; + + /* physical locations of non-permanent maps */ + unsigned long status_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; + unsigned long cmd_dma_offset; +} drm_savage_init_t; + +typedef union drm_savage_cmd_header drm_savage_cmd_header_t; +typedef struct drm_savage_cmdbuf { + /* command buffer in client's address space */ + drm_savage_cmd_header_t *cmd_addr; + unsigned int size; /* size of the command buffer in 64bit units */ + + unsigned int dma_idx; /* DMA buffer index to use */ + int discard; /* discard DMA buffer when done */ + /* vertex buffer in client's address space */ + unsigned int *vb_addr; + unsigned int vb_size; /* size of client vertex buffer in bytes */ + unsigned int vb_stride; /* stride of vertices in 32bit words */ + /* boxes in client's address space */ + struct drm_clip_rect *box_addr; + unsigned int nbox; /* number of clipping boxes */ +} drm_savage_cmdbuf_t; + +#define SAVAGE_WAIT_2D 0x1 /* wait for 2D idle before updating event tag */ +#define SAVAGE_WAIT_3D 0x2 /* wait for 3D idle before updating event tag */ +#define SAVAGE_WAIT_IRQ 0x4 /* emit or wait for IRQ, not implemented yet */ +typedef struct drm_savage_event { + unsigned int count; + unsigned int flags; +} drm_savage_event_emit_t, drm_savage_event_wait_t; + +/* Commands for the cmdbuf ioctl + */ +#define SAVAGE_CMD_STATE 0 /* a range of state registers */ +#define SAVAGE_CMD_DMA_PRIM 1 /* vertices from DMA buffer */ +#define SAVAGE_CMD_VB_PRIM 2 /* vertices from client vertex buffer */ +#define SAVAGE_CMD_DMA_IDX 3 /* indexed vertices from DMA buffer */ +#define SAVAGE_CMD_VB_IDX 4 /* indexed vertices client vertex buffer */ +#define SAVAGE_CMD_CLEAR 5 /* clear buffers */ +#define SAVAGE_CMD_SWAP 6 /* swap buffers */ + +/* Primitive types +*/ +#define SAVAGE_PRIM_TRILIST 0 /* triangle list */ +#define SAVAGE_PRIM_TRISTRIP 1 /* triangle strip */ +#define SAVAGE_PRIM_TRIFAN 2 /* triangle fan */ +#define SAVAGE_PRIM_TRILIST_201 3 /* reorder verts for correct flat + * shading on s3d */ + +/* Skip flags (vertex format) + */ +#define SAVAGE_SKIP_Z 0x01 +#define SAVAGE_SKIP_W 0x02 +#define SAVAGE_SKIP_C0 0x04 +#define SAVAGE_SKIP_C1 0x08 +#define SAVAGE_SKIP_S0 0x10 +#define SAVAGE_SKIP_T0 0x20 +#define SAVAGE_SKIP_ST0 0x30 +#define SAVAGE_SKIP_S1 0x40 +#define SAVAGE_SKIP_T1 0x80 +#define SAVAGE_SKIP_ST1 0xc0 +#define SAVAGE_SKIP_ALL_S3D 0x3f +#define SAVAGE_SKIP_ALL_S4 0xff + +/* Buffer names for clear command + */ +#define SAVAGE_FRONT 0x1 +#define SAVAGE_BACK 0x2 +#define SAVAGE_DEPTH 0x4 + +/* 64-bit command header + */ +union drm_savage_cmd_header { + struct { + unsigned char cmd; /* command */ + unsigned char pad0; + unsigned short pad1; + unsigned short pad2; + unsigned short pad3; + } cmd; /* generic */ + struct { + unsigned char cmd; + unsigned char global; /* need idle engine? */ + unsigned short count; /* number of consecutive registers */ + unsigned short start; /* first register */ + unsigned short pad3; + } state; /* SAVAGE_CMD_STATE */ + struct { + unsigned char cmd; + unsigned char prim; /* primitive type */ + unsigned short skip; /* vertex format (skip flags) */ + unsigned short count; /* number of vertices */ + unsigned short start; /* first vertex in DMA/vertex buffer */ + } prim; /* SAVAGE_CMD_DMA_PRIM, SAVAGE_CMD_VB_PRIM */ + struct { + unsigned char cmd; + unsigned char prim; + unsigned short skip; + unsigned short count; /* number of indices that follow */ + unsigned short pad3; + } idx; /* SAVAGE_CMD_DMA_IDX, SAVAGE_CMD_VB_IDX */ + struct { + unsigned char cmd; + unsigned char pad0; + unsigned short pad1; + unsigned int flags; + } clear0; /* SAVAGE_CMD_CLEAR */ + struct { + unsigned int mask; + unsigned int value; + } clear1; /* SAVAGE_CMD_CLEAR data */ +}; + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/sis_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/sis_drm.h new file mode 100644 index 0000000..8e51bb9 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/sis_drm.h @@ -0,0 +1,77 @@ +/* sis_drv.h -- Private header for sis driver -*- linux-c -*- */ +/* + * Copyright 2005 Eric Anholt + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __SIS_DRM_H__ +#define __SIS_DRM_H__ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* SiS specific ioctls */ +#define NOT_USED_0_3 +#define DRM_SIS_FB_ALLOC 0x04 +#define DRM_SIS_FB_FREE 0x05 +#define NOT_USED_6_12 +#define DRM_SIS_AGP_INIT 0x13 +#define DRM_SIS_AGP_ALLOC 0x14 +#define DRM_SIS_AGP_FREE 0x15 +#define DRM_SIS_FB_INIT 0x16 + +#define DRM_IOCTL_SIS_FB_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_FB_ALLOC, drm_sis_mem_t) +#define DRM_IOCTL_SIS_FB_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_FB_FREE, drm_sis_mem_t) +#define DRM_IOCTL_SIS_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_AGP_INIT, drm_sis_agp_t) +#define DRM_IOCTL_SIS_AGP_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_SIS_AGP_ALLOC, drm_sis_mem_t) +#define DRM_IOCTL_SIS_AGP_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_AGP_FREE, drm_sis_mem_t) +#define DRM_IOCTL_SIS_FB_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_SIS_FB_INIT, drm_sis_fb_t) +/* +#define DRM_IOCTL_SIS_FLIP DRM_IOW( 0x48, drm_sis_flip_t) +#define DRM_IOCTL_SIS_FLIP_INIT DRM_IO( 0x49) +#define DRM_IOCTL_SIS_FLIP_FINAL DRM_IO( 0x50) +*/ + +typedef struct { + int context; + unsigned int offset; + unsigned int size; + unsigned long free; +} drm_sis_mem_t; + +typedef struct { + unsigned int offset, size; +} drm_sis_agp_t; + +typedef struct { + unsigned int offset, size; +} drm_sis_fb_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* __SIS_DRM_H__ */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/tegra_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/tegra_drm.h new file mode 100644 index 0000000..6c07919 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/tegra_drm.h @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _TEGRA_DRM_H_ +#define _TEGRA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0) +#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1) + +/** + * struct drm_tegra_gem_create - parameters for the GEM object creation IOCTL + */ +struct drm_tegra_gem_create { + /** + * @size: + * + * The size, in bytes, of the buffer object to be created. + */ + __u64 size; + + /** + * @flags: + * + * A bitmask of flags that influence the creation of GEM objects: + * + * DRM_TEGRA_GEM_CREATE_TILED + * Use the 16x16 tiling format for this buffer. + * + * DRM_TEGRA_GEM_CREATE_BOTTOM_UP + * The buffer has a bottom-up layout. + */ + __u32 flags; + + /** + * @handle: + * + * The handle of the created GEM object. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u32 handle; +}; + +/** + * struct drm_tegra_gem_mmap - parameters for the GEM mmap IOCTL + */ +struct drm_tegra_gem_mmap { + /** + * @handle: + * + * Handle of the GEM object to obtain an mmap offset for. + */ + __u32 handle; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; + + /** + * @offset: + * + * The mmap offset for the given GEM object. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u64 offset; +}; + +/** + * struct drm_tegra_syncpt_read - parameters for the read syncpoint IOCTL + */ +struct drm_tegra_syncpt_read { + /** + * @id: + * + * ID of the syncpoint to read the current value from. + */ + __u32 id; + + /** + * @value: + * + * The current syncpoint value. Set by the kernel upon successful + * completion of the IOCTL. + */ + __u32 value; +}; + +/** + * struct drm_tegra_syncpt_incr - parameters for the increment syncpoint IOCTL + */ +struct drm_tegra_syncpt_incr { + /** + * @id: + * + * ID of the syncpoint to increment. + */ + __u32 id; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_syncpt_wait - parameters for the wait syncpoint IOCTL + */ +struct drm_tegra_syncpt_wait { + /** + * @id: + * + * ID of the syncpoint to wait on. + */ + __u32 id; + + /** + * @thresh: + * + * Threshold value for which to wait. + */ + __u32 thresh; + + /** + * @timeout: + * + * Timeout, in milliseconds, to wait. + */ + __u32 timeout; + + /** + * @value: + * + * The new syncpoint value after the wait. Set by the kernel upon + * successful completion of the IOCTL. + */ + __u32 value; +}; + +#define DRM_TEGRA_NO_TIMEOUT (0xffffffff) + +/** + * struct drm_tegra_open_channel - parameters for the open channel IOCTL + */ +struct drm_tegra_open_channel { + /** + * @client: + * + * The client ID for this channel. + */ + __u32 client; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; + + /** + * @context: + * + * The application context of this channel. Set by the kernel upon + * successful completion of the IOCTL. This context needs to be passed + * to the DRM_TEGRA_CHANNEL_CLOSE or the DRM_TEGRA_SUBMIT IOCTLs. + */ + __u64 context; +}; + +/** + * struct drm_tegra_close_channel - parameters for the close channel IOCTL + */ +struct drm_tegra_close_channel { + /** + * @context: + * + * The application context of this channel. This is obtained from the + * DRM_TEGRA_OPEN_CHANNEL IOCTL. + */ + __u64 context; +}; + +/** + * struct drm_tegra_get_syncpt - parameters for the get syncpoint IOCTL + */ +struct drm_tegra_get_syncpt { + /** + * @context: + * + * The application context identifying the channel for which to obtain + * the syncpoint ID. + */ + __u64 context; + + /** + * @index: + * + * Index of the client syncpoint for which to obtain the ID. + */ + __u32 index; + + /** + * @id: + * + * The ID of the given syncpoint. Set by the kernel upon successful + * completion of the IOCTL. + */ + __u32 id; +}; + +/** + * struct drm_tegra_get_syncpt_base - parameters for the get wait base IOCTL + */ +struct drm_tegra_get_syncpt_base { + /** + * @context: + * + * The application context identifying for which channel to obtain the + * wait base. + */ + __u64 context; + + /** + * @syncpt: + * + * ID of the syncpoint for which to obtain the wait base. + */ + __u32 syncpt; + + /** + * @id: + * + * The ID of the wait base corresponding to the client syncpoint. Set + * by the kernel upon successful completion of the IOCTL. + */ + __u32 id; +}; + +/** + * struct drm_tegra_syncpt - syncpoint increment operation + */ +struct drm_tegra_syncpt { + /** + * @id: + * + * ID of the syncpoint to operate on. + */ + __u32 id; + + /** + * @incrs: + * + * Number of increments to perform for the syncpoint. + */ + __u32 incrs; +}; + +/** + * struct drm_tegra_cmdbuf - structure describing a command buffer + */ +struct drm_tegra_cmdbuf { + /** + * @handle: + * + * Handle to a GEM object containing the command buffer. + */ + __u32 handle; + + /** + * @offset: + * + * Offset, in bytes, into the GEM object identified by @handle at + * which the command buffer starts. + */ + __u32 offset; + + /** + * @words: + * + * Number of 32-bit words in this command buffer. + */ + __u32 words; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_reloc - GEM object relocation structure + */ +struct drm_tegra_reloc { + struct { + /** + * @cmdbuf.handle: + * + * Handle to the GEM object containing the command buffer for + * which to perform this GEM object relocation. + */ + __u32 handle; + + /** + * @cmdbuf.offset: + * + * Offset, in bytes, into the command buffer at which to + * insert the relocated address. + */ + __u32 offset; + } cmdbuf; + struct { + /** + * @target.handle: + * + * Handle to the GEM object to be relocated. + */ + __u32 handle; + + /** + * @target.offset: + * + * Offset, in bytes, into the target GEM object at which the + * relocated data starts. + */ + __u32 offset; + } target; + + /** + * @shift: + * + * The number of bits by which to shift relocated addresses. + */ + __u32 shift; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_waitchk - wait check structure + */ +struct drm_tegra_waitchk { + /** + * @handle: + * + * Handle to the GEM object containing a command stream on which to + * perform the wait check. + */ + __u32 handle; + + /** + * @offset: + * + * Offset, in bytes, of the location in the command stream to perform + * the wait check on. + */ + __u32 offset; + + /** + * @syncpt: + * + * ID of the syncpoint to wait check. + */ + __u32 syncpt; + + /** + * @thresh: + * + * Threshold value for which to check. + */ + __u32 thresh; +}; + +/** + * struct drm_tegra_submit - job submission structure + */ +struct drm_tegra_submit { + /** + * @context: + * + * The application context identifying the channel to use for the + * execution of this job. + */ + __u64 context; + + /** + * @num_syncpts: + * + * The number of syncpoints operated on by this job. This defines the + * length of the array pointed to by @syncpts. + */ + __u32 num_syncpts; + + /** + * @num_cmdbufs: + * + * The number of command buffers to execute as part of this job. This + * defines the length of the array pointed to by @cmdbufs. + */ + __u32 num_cmdbufs; + + /** + * @num_relocs: + * + * The number of relocations to perform before executing this job. + * This defines the length of the array pointed to by @relocs. + */ + __u32 num_relocs; + + /** + * @num_waitchks: + * + * The number of wait checks to perform as part of this job. This + * defines the length of the array pointed to by @waitchks. + */ + __u32 num_waitchks; + + /** + * @waitchk_mask: + * + * Bitmask of valid wait checks. + */ + __u32 waitchk_mask; + + /** + * @timeout: + * + * Timeout, in milliseconds, before this job is cancelled. + */ + __u32 timeout; + + /** + * @syncpts: + * + * A pointer to an array of &struct drm_tegra_syncpt structures that + * specify the syncpoint operations performed as part of this job. + * The number of elements in the array must be equal to the value + * given by @num_syncpts. + */ + __u64 syncpts; + + /** + * @cmdbufs: + * + * A pointer to an array of &struct drm_tegra_cmdbuf structures that + * define the command buffers to execute as part of this job. The + * number of elements in the array must be equal to the value given + * by @num_syncpts. + */ + __u64 cmdbufs; + + /** + * @relocs: + * + * A pointer to an array of &struct drm_tegra_reloc structures that + * specify the relocations that need to be performed before executing + * this job. The number of elements in the array must be equal to the + * value given by @num_relocs. + */ + __u64 relocs; + + /** + * @waitchks: + * + * A pointer to an array of &struct drm_tegra_waitchk structures that + * specify the wait checks to be performed while executing this job. + * The number of elements in the array must be equal to the value + * given by @num_waitchks. + */ + __u64 waitchks; + + /** + * @fence: + * + * The threshold of the syncpoint associated with this job after it + * has been completed. Set by the kernel upon successful completion of + * the IOCTL. This can be used with the DRM_TEGRA_SYNCPT_WAIT IOCTL to + * wait for this job to be finished. + */ + __u32 fence; + + /** + * @reserved: + * + * This field is reserved for future use. Must be 0. + */ + __u32 reserved[5]; +}; + +#define DRM_TEGRA_GEM_TILING_MODE_PITCH 0 +#define DRM_TEGRA_GEM_TILING_MODE_TILED 1 +#define DRM_TEGRA_GEM_TILING_MODE_BLOCK 2 + +/** + * struct drm_tegra_gem_set_tiling - parameters for the set tiling IOCTL + */ +struct drm_tegra_gem_set_tiling { + /** + * @handle: + * + * Handle to the GEM object for which to set the tiling parameters. + */ + __u32 handle; + + /** + * @mode: + * + * The tiling mode to set. Must be one of: + * + * DRM_TEGRA_GEM_TILING_MODE_PITCH + * pitch linear format + * + * DRM_TEGRA_GEM_TILING_MODE_TILED + * 16x16 tiling format + * + * DRM_TEGRA_GEM_TILING_MODE_BLOCK + * 16Bx2 tiling format + */ + __u32 mode; + + /** + * @value: + * + * The value to set for the tiling mode parameter. + */ + __u32 value; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +/** + * struct drm_tegra_gem_get_tiling - parameters for the get tiling IOCTL + */ +struct drm_tegra_gem_get_tiling { + /** + * @handle: + * + * Handle to the GEM object for which to query the tiling parameters. + */ + __u32 handle; + + /** + * @mode: + * + * The tiling mode currently associated with the GEM object. Set by + * the kernel upon successful completion of the IOCTL. + */ + __u32 mode; + + /** + * @value: + * + * The tiling mode parameter currently associated with the GEM object. + * Set by the kernel upon successful completion of the IOCTL. + */ + __u32 value; + + /** + * @pad: + * + * Structure padding that may be used in the future. Must be 0. + */ + __u32 pad; +}; + +#define DRM_TEGRA_GEM_BOTTOM_UP (1 << 0) +#define DRM_TEGRA_GEM_FLAGS (DRM_TEGRA_GEM_BOTTOM_UP) + +/** + * struct drm_tegra_gem_set_flags - parameters for the set flags IOCTL + */ +struct drm_tegra_gem_set_flags { + /** + * @handle: + * + * Handle to the GEM object for which to set the flags. + */ + __u32 handle; + + /** + * @flags: + * + * The flags to set for the GEM object. + */ + __u32 flags; +}; + +/** + * struct drm_tegra_gem_get_flags - parameters for the get flags IOCTL + */ +struct drm_tegra_gem_get_flags { + /** + * @handle: + * + * Handle to the GEM object for which to query the flags. + */ + __u32 handle; + + /** + * @flags: + * + * The flags currently associated with the GEM object. Set by the + * kernel upon successful completion of the IOCTL. + */ + __u32 flags; +}; + +#define DRM_TEGRA_GEM_CREATE 0x00 +#define DRM_TEGRA_GEM_MMAP 0x01 +#define DRM_TEGRA_SYNCPT_READ 0x02 +#define DRM_TEGRA_SYNCPT_INCR 0x03 +#define DRM_TEGRA_SYNCPT_WAIT 0x04 +#define DRM_TEGRA_OPEN_CHANNEL 0x05 +#define DRM_TEGRA_CLOSE_CHANNEL 0x06 +#define DRM_TEGRA_GET_SYNCPT 0x07 +#define DRM_TEGRA_SUBMIT 0x08 +#define DRM_TEGRA_GET_SYNCPT_BASE 0x09 +#define DRM_TEGRA_GEM_SET_TILING 0x0a +#define DRM_TEGRA_GEM_GET_TILING 0x0b +#define DRM_TEGRA_GEM_SET_FLAGS 0x0c +#define DRM_TEGRA_GEM_GET_FLAGS 0x0d + +#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create) +#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap) +#define DRM_IOCTL_TEGRA_SYNCPT_READ DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_READ, struct drm_tegra_syncpt_read) +#define DRM_IOCTL_TEGRA_SYNCPT_INCR DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_INCR, struct drm_tegra_syncpt_incr) +#define DRM_IOCTL_TEGRA_SYNCPT_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SYNCPT_WAIT, struct drm_tegra_syncpt_wait) +#define DRM_IOCTL_TEGRA_OPEN_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_OPEN_CHANNEL, struct drm_tegra_open_channel) +#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_close_channel) +#define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt) +#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit) +#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base) +#define DRM_IOCTL_TEGRA_GEM_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_TILING, struct drm_tegra_gem_set_tiling) +#define DRM_IOCTL_TEGRA_GEM_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_TILING, struct drm_tegra_gem_get_tiling) +#define DRM_IOCTL_TEGRA_GEM_SET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_SET_FLAGS, struct drm_tegra_gem_set_flags) +#define DRM_IOCTL_TEGRA_GEM_GET_FLAGS DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_GET_FLAGS, struct drm_tegra_gem_get_flags) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/vc4_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/vc4_drm.h new file mode 100644 index 0000000..31f50de --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/vc4_drm.h @@ -0,0 +1,442 @@ +/* + * Copyright © 2014-2015 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _VC4_DRM_H_ +#define _VC4_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define DRM_VC4_SUBMIT_CL 0x00 +#define DRM_VC4_WAIT_SEQNO 0x01 +#define DRM_VC4_WAIT_BO 0x02 +#define DRM_VC4_CREATE_BO 0x03 +#define DRM_VC4_MMAP_BO 0x04 +#define DRM_VC4_CREATE_SHADER_BO 0x05 +#define DRM_VC4_GET_HANG_STATE 0x06 +#define DRM_VC4_GET_PARAM 0x07 +#define DRM_VC4_SET_TILING 0x08 +#define DRM_VC4_GET_TILING 0x09 +#define DRM_VC4_LABEL_BO 0x0a +#define DRM_VC4_GEM_MADVISE 0x0b +#define DRM_VC4_PERFMON_CREATE 0x0c +#define DRM_VC4_PERFMON_DESTROY 0x0d +#define DRM_VC4_PERFMON_GET_VALUES 0x0e + +#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) +#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) +#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) +#define DRM_IOCTL_VC4_CREATE_SHADER_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_CREATE_SHADER_BO, struct drm_vc4_create_shader_bo) +#define DRM_IOCTL_VC4_GET_HANG_STATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_HANG_STATE, struct drm_vc4_get_hang_state) +#define DRM_IOCTL_VC4_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_PARAM, struct drm_vc4_get_param) +#define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) +#define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) +#define DRM_IOCTL_VC4_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_LABEL_BO, struct drm_vc4_label_bo) +#define DRM_IOCTL_VC4_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GEM_MADVISE, struct drm_vc4_gem_madvise) +#define DRM_IOCTL_VC4_PERFMON_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_CREATE, struct drm_vc4_perfmon_create) +#define DRM_IOCTL_VC4_PERFMON_DESTROY DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_DESTROY, struct drm_vc4_perfmon_destroy) +#define DRM_IOCTL_VC4_PERFMON_GET_VALUES DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_PERFMON_GET_VALUES, struct drm_vc4_perfmon_get_values) + +struct drm_vc4_submit_rcl_surface { + __u32 hindex; /* Handle index, or ~0 if not present. */ + __u32 offset; /* Offset to start of buffer. */ + /* + * Bits for either render config (color_write) or load/store packet. + * Bits should all be 0 for MSAA load/stores. + */ + __u16 bits; + +#define VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES (1 << 0) + __u16 flags; +}; + +/** + * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D + * engine. + * + * Drivers typically use GPU BOs to store batchbuffers / command lists and + * their associated state. However, because the VC4 lacks an MMU, we have to + * do validation of memory accesses by the GPU commands. If we were to store + * our commands in BOs, we'd need to do uncached readback from them to do the + * validation process, which is too expensive. Instead, userspace accumulates + * commands and associated state in plain memory, then the kernel copies the + * data to its own address space, and then validates and stores it in a GPU + * BO. + */ +struct drm_vc4_submit_cl { + /* Pointer to the binner command list. + * + * This is the first set of commands executed, which runs the + * coordinate shader to determine where primitives land on the screen, + * then writes out the state updates and draw calls necessary per tile + * to the tile allocation BO. + */ + __u64 bin_cl; + + /* Pointer to the shader records. + * + * Shader records are the structures read by the hardware that contain + * pointers to uniforms, shaders, and vertex attributes. The + * reference to the shader record has enough information to determine + * how many pointers are necessary (fixed number for shaders/uniforms, + * and an attribute count), so those BO indices into bo_handles are + * just stored as __u32s before each shader record passed in. + */ + __u64 shader_rec; + + /* Pointer to uniform data and texture handles for the textures + * referenced by the shader. + * + * For each shader state record, there is a set of uniform data in the + * order referenced by the record (FS, VS, then CS). Each set of + * uniform data has a __u32 index into bo_handles per texture + * sample operation, in the order the QPU_W_TMUn_S writes appear in + * the program. Following the texture BO handle indices is the actual + * uniform data. + * + * The individual uniform state blocks don't have sizes passed in, + * because the kernel has to determine the sizes anyway during shader + * code validation. + */ + __u64 uniforms; + __u64 bo_handles; + + /* Size in bytes of the binner command list. */ + __u32 bin_cl_size; + /* Size in bytes of the set of shader records. */ + __u32 shader_rec_size; + /* Number of shader records. + * + * This could just be computed from the contents of shader_records and + * the address bits of references to them from the bin CL, but it + * keeps the kernel from having to resize some allocations it makes. + */ + __u32 shader_rec_count; + /* Size in bytes of the uniform state. */ + __u32 uniforms_size; + + /* Number of BO handles passed in (size is that times 4). */ + __u32 bo_handle_count; + + /* RCL setup: */ + __u16 width; + __u16 height; + __u8 min_x_tile; + __u8 min_y_tile; + __u8 max_x_tile; + __u8 max_y_tile; + struct drm_vc4_submit_rcl_surface color_read; + struct drm_vc4_submit_rcl_surface color_write; + struct drm_vc4_submit_rcl_surface zs_read; + struct drm_vc4_submit_rcl_surface zs_write; + struct drm_vc4_submit_rcl_surface msaa_color_write; + struct drm_vc4_submit_rcl_surface msaa_zs_write; + __u32 clear_color[2]; + __u32 clear_z; + __u8 clear_s; + + __u32 pad:24; + +#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) +/* By default, the kernel gets to choose the order that the tiles are + * rendered in. If this is set, then the tiles will be rendered in a + * raster order, with the right-to-left vs left-to-right and + * top-to-bottom vs bottom-to-top dictated by + * VC4_SUBMIT_CL_RCL_ORDER_INCREASING_*. This allows overlapping + * blits to be implemented using the 3D engine. + */ +#define VC4_SUBMIT_CL_FIXED_RCL_ORDER (1 << 1) +#define VC4_SUBMIT_CL_RCL_ORDER_INCREASING_X (1 << 2) +#define VC4_SUBMIT_CL_RCL_ORDER_INCREASING_Y (1 << 3) + __u32 flags; + + /* Returned value of the seqno of this render job (for the + * wait ioctl). + */ + __u64 seqno; + + /* ID of the perfmon to attach to this job. 0 means no perfmon. */ + __u32 perfmonid; + + /* Syncobj handle to wait on. If set, processing of this render job + * will not start until the syncobj is signaled. 0 means ignore. + */ + __u32 in_sync; + + /* Syncobj handle to export fence to. If set, the fence in the syncobj + * will be replaced with a fence that signals upon completion of this + * render job. 0 means ignore. + */ + __u32 out_sync; + + __u32 pad2; +}; + +/** + * struct drm_vc4_wait_seqno - ioctl argument for waiting for + * DRM_VC4_SUBMIT_CL completion using its returned seqno. + * + * timeout_ns is the timeout in nanoseconds, where "0" means "don't + * block, just return the status." + */ +struct drm_vc4_wait_seqno { + __u64 seqno; + __u64 timeout_ns; +}; + +/** + * struct drm_vc4_wait_bo - ioctl argument for waiting for + * completion of the last DRM_VC4_SUBMIT_CL on a BO. + * + * This is useful for cases where multiple processes might be + * rendering to a BO and you want to wait for all rendering to be + * completed. + */ +struct drm_vc4_wait_bo { + __u32 handle; + __u32 pad; + __u64 timeout_ns; +}; + +/** + * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_create_bo { + __u32 size; + __u32 flags; + /** Returned GEM handle for the BO. */ + __u32 handle; + __u32 pad; +}; + +/** + * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. + * + * This doesn't actually perform an mmap. Instead, it returns the + * offset you need to use in an mmap on the DRM device node. This + * means that tools like valgrind end up knowing about the mapped + * memory. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_mmap_bo { + /** Handle for the object being mapped. */ + __u32 handle; + __u32 flags; + /** offset into the drm node to use for subsequent mmap call. */ + __u64 offset; +}; + +/** + * struct drm_vc4_create_shader_bo - ioctl argument for creating VC4 + * shader BOs. + * + * Since allowing a shader to be overwritten while it's also being + * executed from would allow privlege escalation, shaders must be + * created using this ioctl, and they can't be mmapped later. + */ +struct drm_vc4_create_shader_bo { + /* Size of the data argument. */ + __u32 size; + /* Flags, currently must be 0. */ + __u32 flags; + + /* Pointer to the data. */ + __u64 data; + + /** Returned GEM handle for the BO. */ + __u32 handle; + /* Pad, must be 0. */ + __u32 pad; +}; + +struct drm_vc4_get_hang_state_bo { + __u32 handle; + __u32 paddr; + __u32 size; + __u32 pad; +}; + +/** + * struct drm_vc4_hang_state - ioctl argument for collecting state + * from a GPU hang for analysis. +*/ +struct drm_vc4_get_hang_state { + /** Pointer to array of struct drm_vc4_get_hang_state_bo. */ + __u64 bo; + /** + * On input, the size of the bo array. Output is the number + * of bos to be returned. + */ + __u32 bo_count; + + __u32 start_bin, start_render; + + __u32 ct0ca, ct0ea; + __u32 ct1ca, ct1ea; + __u32 ct0cs, ct1cs; + __u32 ct0ra0, ct1ra0; + + __u32 bpca, bpcs; + __u32 bpoa, bpos; + + __u32 vpmbase; + + __u32 dbge; + __u32 fdbgo; + __u32 fdbgb; + __u32 fdbgr; + __u32 fdbgs; + __u32 errstat; + + /* Pad that we may save more registers into in the future. */ + __u32 pad[16]; +}; + +#define DRM_VC4_PARAM_V3D_IDENT0 0 +#define DRM_VC4_PARAM_V3D_IDENT1 1 +#define DRM_VC4_PARAM_V3D_IDENT2 2 +#define DRM_VC4_PARAM_SUPPORTS_BRANCHES 3 +#define DRM_VC4_PARAM_SUPPORTS_ETC1 4 +#define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 +#define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 +#define DRM_VC4_PARAM_SUPPORTS_MADVISE 7 +#define DRM_VC4_PARAM_SUPPORTS_PERFMON 8 + +struct drm_vc4_get_param { + __u32 param; + __u32 pad; + __u64 value; +}; + +struct drm_vc4_get_tiling { + __u32 handle; + __u32 flags; + __u64 modifier; +}; + +struct drm_vc4_set_tiling { + __u32 handle; + __u32 flags; + __u64 modifier; +}; + +/** + * struct drm_vc4_label_bo - Attach a name to a BO for debug purposes. + */ +struct drm_vc4_label_bo { + __u32 handle; + __u32 len; + __u64 name; +}; + +/* + * States prefixed with '__' are internal states and cannot be passed to the + * DRM_IOCTL_VC4_GEM_MADVISE ioctl. + */ +#define VC4_MADV_WILLNEED 0 +#define VC4_MADV_DONTNEED 1 +#define __VC4_MADV_PURGED 2 +#define __VC4_MADV_NOTSUPP 3 + +struct drm_vc4_gem_madvise { + __u32 handle; + __u32 madv; + __u32 retained; + __u32 pad; +}; + +enum { + VC4_PERFCNT_FEP_VALID_PRIMS_NO_RENDER, + VC4_PERFCNT_FEP_VALID_PRIMS_RENDER, + VC4_PERFCNT_FEP_CLIPPED_QUADS, + VC4_PERFCNT_FEP_VALID_QUADS, + VC4_PERFCNT_TLB_QUADS_NOT_PASSING_STENCIL, + VC4_PERFCNT_TLB_QUADS_NOT_PASSING_Z_AND_STENCIL, + VC4_PERFCNT_TLB_QUADS_PASSING_Z_AND_STENCIL, + VC4_PERFCNT_TLB_QUADS_ZERO_COVERAGE, + VC4_PERFCNT_TLB_QUADS_NON_ZERO_COVERAGE, + VC4_PERFCNT_TLB_QUADS_WRITTEN_TO_COLOR_BUF, + VC4_PERFCNT_PLB_PRIMS_OUTSIDE_VIEWPORT, + VC4_PERFCNT_PLB_PRIMS_NEED_CLIPPING, + VC4_PERFCNT_PSE_PRIMS_REVERSED, + VC4_PERFCNT_QPU_TOTAL_IDLE_CYCLES, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_VERTEX_COORD_SHADING, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_FRAGMENT_SHADING, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_EXEC_VALID_INST, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_TMUS, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_SCOREBOARD, + VC4_PERFCNT_QPU_TOTAL_CLK_CYCLES_WAITING_VARYINGS, + VC4_PERFCNT_QPU_TOTAL_INST_CACHE_HIT, + VC4_PERFCNT_QPU_TOTAL_INST_CACHE_MISS, + VC4_PERFCNT_QPU_TOTAL_UNIFORM_CACHE_HIT, + VC4_PERFCNT_QPU_TOTAL_UNIFORM_CACHE_MISS, + VC4_PERFCNT_TMU_TOTAL_TEXT_QUADS_PROCESSED, + VC4_PERFCNT_TMU_TOTAL_TEXT_CACHE_MISS, + VC4_PERFCNT_VPM_TOTAL_CLK_CYCLES_VDW_STALLED, + VC4_PERFCNT_VPM_TOTAL_CLK_CYCLES_VCD_STALLED, + VC4_PERFCNT_L2C_TOTAL_L2_CACHE_HIT, + VC4_PERFCNT_L2C_TOTAL_L2_CACHE_MISS, + VC4_PERFCNT_NUM_EVENTS, +}; + +#define DRM_VC4_MAX_PERF_COUNTERS 16 + +struct drm_vc4_perfmon_create { + __u32 id; + __u32 ncounters; + __u8 events[DRM_VC4_MAX_PERF_COUNTERS]; +}; + +struct drm_vc4_perfmon_destroy { + __u32 id; +}; + +/* + * Returns the values of the performance counters tracked by this + * perfmon (as an array of ncounters u64 values). + * + * No implicit synchronization is performed, so the user has to + * guarantee that any jobs using this perfmon have already been + * completed (probably by blocking on the seqno returned by the + * last exec that used the perfmon). + */ +struct drm_vc4_perfmon_get_values { + __u32 id; + __u64 values_ptr; +}; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VC4_DRM_H_ */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/via_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/via_drm.h new file mode 100644 index 0000000..8b69e81 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/via_drm.h @@ -0,0 +1,283 @@ +/* + * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef _VIA_DRM_H_ +#define _VIA_DRM_H_ + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _VIA_DEFINES_ +#define _VIA_DEFINES_ + +#include "via_drmclient.h" + +#define VIA_NR_SAREA_CLIPRECTS 8 +#define VIA_NR_XVMC_PORTS 10 +#define VIA_NR_XVMC_LOCKS 5 +#define VIA_MAX_CACHELINE_SIZE 64 +#define XVMCLOCKPTR(saPriv,lockNo) \ + ((__volatile__ struct drm_hw_lock *)(((((unsigned long) (saPriv)->XvMCLockArea) + \ + (VIA_MAX_CACHELINE_SIZE - 1)) & \ + ~(VIA_MAX_CACHELINE_SIZE - 1)) + \ + VIA_MAX_CACHELINE_SIZE*(lockNo))) + +/* Each region is a minimum of 64k, and there are at most 64 of them. + */ +#define VIA_NR_TEX_REGIONS 64 +#define VIA_LOG_MIN_TEX_REGION_SIZE 16 +#endif + +#define VIA_UPLOAD_TEX0IMAGE 0x1 /* handled clientside */ +#define VIA_UPLOAD_TEX1IMAGE 0x2 /* handled clientside */ +#define VIA_UPLOAD_CTX 0x4 +#define VIA_UPLOAD_BUFFERS 0x8 +#define VIA_UPLOAD_TEX0 0x10 +#define VIA_UPLOAD_TEX1 0x20 +#define VIA_UPLOAD_CLIPRECTS 0x40 +#define VIA_UPLOAD_ALL 0xff + +/* VIA specific ioctls */ +#define DRM_VIA_ALLOCMEM 0x00 +#define DRM_VIA_FREEMEM 0x01 +#define DRM_VIA_AGP_INIT 0x02 +#define DRM_VIA_FB_INIT 0x03 +#define DRM_VIA_MAP_INIT 0x04 +#define DRM_VIA_DEC_FUTEX 0x05 +#define NOT_USED +#define DRM_VIA_DMA_INIT 0x07 +#define DRM_VIA_CMDBUFFER 0x08 +#define DRM_VIA_FLUSH 0x09 +#define DRM_VIA_PCICMD 0x0a +#define DRM_VIA_CMDBUF_SIZE 0x0b +#define NOT_USED +#define DRM_VIA_WAIT_IRQ 0x0d +#define DRM_VIA_DMA_BLIT 0x0e +#define DRM_VIA_BLIT_SYNC 0x0f + +#define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) +#define DRM_IOCTL_VIA_AGP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_AGP_INIT, drm_via_agp_t) +#define DRM_IOCTL_VIA_FB_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_FB_INIT, drm_via_fb_t) +#define DRM_IOCTL_VIA_MAP_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_MAP_INIT, drm_via_init_t) +#define DRM_IOCTL_VIA_DEC_FUTEX DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_DEC_FUTEX, drm_via_futex_t) +#define DRM_IOCTL_VIA_DMA_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_DMA_INIT, drm_via_dma_init_t) +#define DRM_IOCTL_VIA_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_CMDBUFFER, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_FLUSH DRM_IO( DRM_COMMAND_BASE + DRM_VIA_FLUSH) +#define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) +#define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ + drm_via_cmdbuf_size_t) +#define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) +#define DRM_IOCTL_VIA_DMA_BLIT DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_DMA_BLIT, drm_via_dmablit_t) +#define DRM_IOCTL_VIA_BLIT_SYNC DRM_IOW(DRM_COMMAND_BASE + DRM_VIA_BLIT_SYNC, drm_via_blitsync_t) + +/* Indices into buf.Setup where various bits of state are mirrored per + * context and per buffer. These can be fired at the card as a unit, + * or in a piecewise fashion as required. + */ + +#define VIA_TEX_SETUP_SIZE 8 + +/* Flags for clear ioctl + */ +#define VIA_FRONT 0x1 +#define VIA_BACK 0x2 +#define VIA_DEPTH 0x4 +#define VIA_STENCIL 0x8 +#define VIA_MEM_VIDEO 0 /* matches drm constant */ +#define VIA_MEM_AGP 1 /* matches drm constant */ +#define VIA_MEM_SYSTEM 2 +#define VIA_MEM_MIXED 3 +#define VIA_MEM_UNKNOWN 4 + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_agp_t; + +typedef struct { + __u32 offset; + __u32 size; +} drm_via_fb_t; + +typedef struct { + __u32 context; + __u32 type; + __u32 size; + unsigned long index; + unsigned long offset; +} drm_via_mem_t; + +typedef struct _drm_via_init { + enum { + VIA_INIT_MAP = 0x01, + VIA_CLEANUP_MAP = 0x02 + } func; + + unsigned long sarea_priv_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long agpAddr; +} drm_via_init_t; + +typedef struct _drm_via_futex { + enum { + VIA_FUTEX_WAIT = 0x00, + VIA_FUTEX_WAKE = 0X01 + } func; + __u32 ms; + __u32 lock; + __u32 val; +} drm_via_futex_t; + +typedef struct _drm_via_dma_init { + enum { + VIA_INIT_DMA = 0x01, + VIA_CLEANUP_DMA = 0x02, + VIA_DMA_INITIALIZED = 0x03 + } func; + + unsigned long offset; + unsigned long size; + unsigned long reg_pause_addr; +} drm_via_dma_init_t; + +typedef struct _drm_via_cmdbuffer { + char *buf; + unsigned long size; +} drm_via_cmdbuffer_t; + +/* Warning: If you change the SAREA structure you must change the Xserver + * structure as well */ + +typedef struct _drm_via_tex_region { + unsigned char next, prev; /* indices to form a circular LRU */ + unsigned char inUse; /* owned by a client, or free? */ + int age; /* tracked by clients to update local LRU's */ +} drm_via_tex_region_t; + +typedef struct _drm_via_sarea { + unsigned int dirty; + unsigned int nbox; + struct drm_clip_rect boxes[VIA_NR_SAREA_CLIPRECTS]; + drm_via_tex_region_t texList[VIA_NR_TEX_REGIONS + 1]; + int texAge; /* last time texture was uploaded */ + int ctxOwner; /* last context to upload state */ + int vertexPrim; + + /* + * Below is for XvMC. + * We want the lock integers alone on, and aligned to, a cache line. + * Therefore this somewhat strange construct. + */ + + char XvMCLockArea[VIA_MAX_CACHELINE_SIZE * (VIA_NR_XVMC_LOCKS + 1)]; + + unsigned int XvMCDisplaying[VIA_NR_XVMC_PORTS]; + unsigned int XvMCSubPicOn[VIA_NR_XVMC_PORTS]; + unsigned int XvMCCtxNoGrabbed; /* Last context to hold decoder */ + + /* Used by the 3d driver only at this point, for pageflipping: + */ + unsigned int pfCurrentOffset; +} drm_via_sarea_t; + +typedef struct _drm_via_cmdbuf_size { + enum { + VIA_CMDBUF_SPACE = 0x01, + VIA_CMDBUF_LAG = 0x02 + } func; + int wait; + __u32 size; +} drm_via_cmdbuf_size_t; + +typedef enum { + VIA_IRQ_ABSOLUTE = 0x0, + VIA_IRQ_RELATIVE = 0x1, + VIA_IRQ_SIGNAL = 0x10000000, + VIA_IRQ_FORCE_SEQUENCE = 0x20000000 +} via_irq_seq_type_t; + +#define VIA_IRQ_FLAGS_MASK 0xF0000000 + +enum drm_via_irqs { + drm_via_irq_hqv0 = 0, + drm_via_irq_hqv1, + drm_via_irq_dma0_dd, + drm_via_irq_dma0_td, + drm_via_irq_dma1_dd, + drm_via_irq_dma1_td, + drm_via_irq_num +}; + +struct drm_via_wait_irq_request { + unsigned irq; + via_irq_seq_type_t type; + __u32 sequence; + __u32 signal; +}; + +typedef union drm_via_irqwait { + struct drm_via_wait_irq_request request; + struct drm_wait_vblank_reply reply; +} drm_via_irqwait_t; + +typedef struct drm_via_blitsync { + __u32 sync_handle; + unsigned engine; +} drm_via_blitsync_t; + +/* - * Below,"flags" is currently unused but will be used for possible future + * extensions like kernel space bounce buffers for bad alignments and + * blit engine busy-wait polling for better latency in the absence of + * interrupts. + */ + +typedef struct drm_via_dmablit { + __u32 num_lines; + __u32 line_length; + + __u32 fb_addr; + __u32 fb_stride; + + unsigned char *mem_addr; + __u32 mem_stride; + + __u32 flags; + int to_fb; + + drm_via_blitsync_t sync; +} drm_via_dmablit_t; + +#if defined(__cplusplus) +} +#endif + +#endif /* _VIA_DRM_H_ */ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/virtgpu_drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/virtgpu_drm.h new file mode 100644 index 0000000..f06a789 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/libdrm/virtgpu_drm.h @@ -0,0 +1,182 @@ +/* + * Copyright 2013 Red Hat + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VIRTGPU_DRM_H +#define VIRTGPU_DRM_H + +#include "drm.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints. + * + * Do not use pointers, use __u64 instead for 32 bit / 64 bit user/kernel + * compatibility Keep fields aligned to their size + */ + +#define DRM_VIRTGPU_MAP 0x01 +#define DRM_VIRTGPU_EXECBUFFER 0x02 +#define DRM_VIRTGPU_GETPARAM 0x03 +#define DRM_VIRTGPU_RESOURCE_CREATE 0x04 +#define DRM_VIRTGPU_RESOURCE_INFO 0x05 +#define DRM_VIRTGPU_TRANSFER_FROM_HOST 0x06 +#define DRM_VIRTGPU_TRANSFER_TO_HOST 0x07 +#define DRM_VIRTGPU_WAIT 0x08 +#define DRM_VIRTGPU_GET_CAPS 0x09 + +#define VIRTGPU_EXECBUF_FENCE_FD_IN 0x01 +#define VIRTGPU_EXECBUF_FENCE_FD_OUT 0x02 +#define VIRTGPU_EXECBUF_FLAGS (\ + VIRTGPU_EXECBUF_FENCE_FD_IN |\ + VIRTGPU_EXECBUF_FENCE_FD_OUT |\ + 0) + +struct drm_virtgpu_map { + __u64 offset; /* use for mmap system call */ + __u32 handle; + __u32 pad; +}; + +struct drm_virtgpu_execbuffer { + __u32 flags; + __u32 size; + __u64 command; /* void* */ + __u64 bo_handles; + __u32 num_bo_handles; + __s32 fence_fd; /* in/out fence fd (see VIRTGPU_EXECBUF_FENCE_FD_IN/OUT) */ +}; + +#define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */ +#define VIRTGPU_PARAM_CAPSET_QUERY_FIX 2 /* do we have the capset fix */ + +struct drm_virtgpu_getparam { + __u64 param; + __u64 value; +}; + +/* NO_BO flags? NO resource flag? */ +/* resource flag for y_0_top */ +struct drm_virtgpu_resource_create { + __u32 target; + __u32 format; + __u32 bind; + __u32 width; + __u32 height; + __u32 depth; + __u32 array_size; + __u32 last_level; + __u32 nr_samples; + __u32 flags; + __u32 bo_handle; /* if this is set - recreate a new resource attached to this bo ? */ + __u32 res_handle; /* returned by kernel */ + __u32 size; /* validate transfer in the host */ + __u32 stride; /* validate transfer in the host */ +}; + +struct drm_virtgpu_resource_info { + __u32 bo_handle; + __u32 res_handle; + __u32 size; + __u32 stride; +}; + +struct drm_virtgpu_3d_box { + __u32 x; + __u32 y; + __u32 z; + __u32 w; + __u32 h; + __u32 d; +}; + +struct drm_virtgpu_3d_transfer_to_host { + __u32 bo_handle; + struct drm_virtgpu_3d_box box; + __u32 level; + __u32 offset; +}; + +struct drm_virtgpu_3d_transfer_from_host { + __u32 bo_handle; + struct drm_virtgpu_3d_box box; + __u32 level; + __u32 offset; +}; + +#define VIRTGPU_WAIT_NOWAIT 1 /* like it */ +struct drm_virtgpu_3d_wait { + __u32 handle; /* 0 is an invalid handle */ + __u32 flags; +}; + +struct drm_virtgpu_get_caps { + __u32 cap_set_id; + __u32 cap_set_ver; + __u64 addr; + __u32 size; + __u32 pad; +}; + +#define DRM_IOCTL_VIRTGPU_MAP \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_MAP, struct drm_virtgpu_map) + +#define DRM_IOCTL_VIRTGPU_EXECBUFFER \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_EXECBUFFER,\ + struct drm_virtgpu_execbuffer) + +#define DRM_IOCTL_VIRTGPU_GETPARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GETPARAM,\ + struct drm_virtgpu_getparam) + +#define DRM_IOCTL_VIRTGPU_RESOURCE_CREATE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_CREATE, \ + struct drm_virtgpu_resource_create) + +#define DRM_IOCTL_VIRTGPU_RESOURCE_INFO \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_RESOURCE_INFO, \ + struct drm_virtgpu_resource_info) + +#define DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_FROM_HOST, \ + struct drm_virtgpu_3d_transfer_from_host) + +#define DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_TRANSFER_TO_HOST, \ + struct drm_virtgpu_3d_transfer_to_host) + +#define DRM_IOCTL_VIRTGPU_WAIT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_WAIT, \ + struct drm_virtgpu_3d_wait) + +#define DRM_IOCTL_VIRTGPU_GET_CAPS \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VIRTGPU_GET_CAPS, \ + struct drm_virtgpu_get_caps) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/tool.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/tool.h new file mode 100644 index 0000000..c7b334d --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/tool.h @@ -0,0 +1,145 @@ +/* + + */ + +#ifndef TOOL_H_ +#define TOOL_H_ +//ڴ˴ͷļ +#include "stdio.h" +#include "string.h" +#include "stdlib.h" + +#ifdef __cplusplus +extern "C" { +#endif +#define YUV420 0 +#define YUV422 YUV420 + 1 +#define YUV444 YUV422 + 1 +/************************************************************* +Function: ReadBmpFile +Description: ȡbmpͼڴ +Input: pFilePathbmp· + pDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgrrgbֵΪ8bit + widthͼ + heightͼ +Output: +*************************************************************/ +extern void ReadBmpFile(char *pFilePath, unsigned char *pData, int & width, int & height); +/************************************************************* +Function: SaveBmpFile +Description: rgbͼݱΪbmp +Input: pFilePathbmp· + pDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgrrgbֵΪ8bit + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveBmpFile(char *pFilePath, unsigned char *pData, int width, int height); +/************************************************************* +Function: SaveRaw +Description: rawͼ +Input: pSavePathraw· + pDataraw + widthrawͼ + heightrawͼ +Output: +*************************************************************/ +extern void SaveRaw(char *pSavePath, short *pRawData, int width, int height); + +extern void SaveRaw32bit(char *pSavePath, long *pRawData, int width, int height); + +/************************************************************* +Function: SaveBmpFile2 +Description: λ8bitbmpͼ +Input: pFilePathbmp· + widthͼ + heightͼ + bitValueͼλ + pRGBDatargbͼڴָ룬rgbͼ˳Ϊbgrbgr...bgr +Output: +*************************************************************/ +extern void SaveBmpFile2(char *pFilePath, int width, int height, int bitValue, short *pRGBData); + +/************************************************************* +Function: SaveYUVData +Description: 8bit YUVͼ +Input: pSavePath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData(char *pSavePath, unsigned char *pData, int width, int height); + + + +/************************************************************* +Function: SaveYUVData2 +Description: λ8bitYUVͼ +Input: pSavePath· + pDatayuvݣλ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData2(char *pSavePath, short *pData, int width, int height, int bitValue); +/************************************************************* +Function: SaveYUVData1 +Description: 8bit YUV420ͼ +Input: pSavePath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void SaveYUVData1(char *pSavePath, unsigned char *pData, int width, int height, int fmt); +/************************************************************* +Function: ReadYUVData1 +Description: ȡ8bit YUV420ͼ +Input: pReadPath· + pDatayuvݣ8bit˳yyy...yyyuuu...uuuvvv...vvv + widthͼ + heightͼ +Output: +*************************************************************/ +extern void ReadYUVData1(char *pReadPath, unsigned char *pData, int width, int height, int fmt); +/************************************************************* +Function: Yuvfmtconv +Description: yuv fmt conversion.444 420 422 to 444 420 422 +Input: pDatain 뻺 + pDataout + width + height + fmt_in ʽ + fmt_out ʽ +Output: +*************************************************************/ +extern void Yuvfmtconv(void *pDatain, void *pDataout, int width, int height, int fmt_in, int fmt_out, int size); +/************************************************************* +Function: Yuvbitstochar +Description: save yuv to 8 bitdepth +Input: pDatain 뻺 + pDataout + size yuv + height λ +Output: +*************************************************************/ +extern void Yuvbitstochar(short *pDatain, unsigned char *pDataout, int size, int bitdepth); + +/************************************************************* +Function: SaveCfaBmp +Description: rawcfaͼ +Input: pRawDatarawͼ + widthrawͼ + heightrawͼߣ + bayerPatternbayer patternʽȡֵΧ[03] + bitValuerawλ +Output: +*************************************************************/ +extern void SaveCfaBmp(char *pFilePath, short *pRawData, int width, int height, int bayerPattern, int bitValue); + +#ifdef __cplusplus +} +#endif + +#endif // TOOL_H_ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/xf86drm.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/xf86drm.h new file mode 100644 index 0000000..d86428a --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/xf86drm.h @@ -0,0 +1,930 @@ +/** + * \file xf86drm.h + * OS-independent header for DRM user-level library interface. + * + * \author Rickard E. (Rik) Faith + */ + +/* + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _XF86DRM_H_ +#define _XF86DRM_H_ + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef DRM_MAX_MINOR +#define DRM_MAX_MINOR 16 +#endif + +#if defined(__linux__) + +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IOC_VOID _IOC_NONE +#define DRM_IOC_READ _IOC_READ +#define DRM_IOC_WRITE _IOC_WRITE +#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#else /* One of the *BSDs */ + +#include +#define DRM_IOCTL_NR(n) ((n) & 0xff) +#define DRM_IOC_VOID IOC_VOID +#define DRM_IOC_READ IOC_OUT +#define DRM_IOC_WRITE IOC_IN +#define DRM_IOC_READWRITE IOC_INOUT +#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size) + +#endif + + /* Defaults, if nothing set in xf86config */ +#define DRM_DEV_UID 0 +#define DRM_DEV_GID 0 +/* Default /dev/dri directory permissions 0755 */ +#define DRM_DEV_DIRMODE \ + (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) +#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) + +#ifdef __OpenBSD__ +#define DRM_DIR_NAME "/dev" +#define DRM_PRIMARY_MINOR_NAME "drm" +#define DRM_CONTROL_MINOR_NAME "drmC" +#define DRM_RENDER_MINOR_NAME "drmR" +#else +#define DRM_DIR_NAME "/dev/dri" +#define DRM_PRIMARY_MINOR_NAME "card" +#define DRM_CONTROL_MINOR_NAME "controlD" +#define DRM_RENDER_MINOR_NAME "renderD" +#define DRM_PROC_NAME "/proc/dri/" /* For backward Linux compatibility */ +#endif + +#define DRM_DEV_NAME "%s/" DRM_PRIMARY_MINOR_NAME "%d" +#define DRM_CONTROL_DEV_NAME "%s/" DRM_CONTROL_MINOR_NAME "%d" +#define DRM_RENDER_DEV_NAME "%s/" DRM_RENDER_MINOR_NAME "%d" + +#define DRM_NODE_NAME_MAX \ + (sizeof(DRM_DIR_NAME) + 1 /* slash */ \ + + MAX3(sizeof(DRM_PRIMARY_MINOR_NAME), \ + sizeof(DRM_CONTROL_MINOR_NAME), \ + sizeof(DRM_RENDER_MINOR_NAME)) \ + + sizeof("144") /* highest possible node number */ \ + + 1) /* NULL-terminator */ + +#define DRM_ERR_NO_DEVICE (-1001) +#define DRM_ERR_NO_ACCESS (-1002) +#define DRM_ERR_NOT_ROOT (-1003) +#define DRM_ERR_INVALID (-1004) +#define DRM_ERR_NO_FD (-1005) + +#define DRM_AGP_NO_HANDLE 0 + +typedef unsigned int drmSize, *drmSizePtr; /**< For mapped regions */ +typedef void *drmAddress, **drmAddressPtr; /**< For mapped regions */ + +#if (__GNUC__ >= 3) +#define DRM_PRINTFLIKE(f, a) __attribute__ ((format(__printf__, f, a))) +#else +#define DRM_PRINTFLIKE(f, a) +#endif + +typedef struct _drmServerInfo { + int (*debug_print)(const char *format, va_list ap) DRM_PRINTFLIKE(1,0); + int (*load_module)(const char *name); + void (*get_perms)(gid_t *, mode_t *); +} drmServerInfo, *drmServerInfoPtr; + +typedef struct drmHashEntry { + int fd; + void (*f)(int, void *, void *); + void *tagTable; +} drmHashEntry; + +extern int drmIoctl(int fd, unsigned long request, void *arg); +extern void *drmGetHashTable(void); +extern drmHashEntry *drmGetEntry(int fd); + +/** + * Driver version information. + * + * \sa drmGetVersion() and drmSetVersion(). + */ +typedef struct _drmVersion { + int version_major; /**< Major version */ + int version_minor; /**< Minor version */ + int version_patchlevel; /**< Patch level */ + int name_len; /**< Length of name buffer */ + char *name; /**< Name of driver */ + int date_len; /**< Length of date buffer */ + char *date; /**< User-space buffer to hold date */ + int desc_len; /**< Length of desc buffer */ + char *desc; /**< User-space buffer to hold desc */ +} drmVersion, *drmVersionPtr; + +typedef struct _drmStats { + unsigned long count; /**< Number of data */ + struct { + unsigned long value; /**< Value from kernel */ + const char *long_format; /**< Suggested format for long_name */ + const char *long_name; /**< Long name for value */ + const char *rate_format; /**< Suggested format for rate_name */ + const char *rate_name; /**< Short name for value per second */ + int isvalue; /**< True if value (vs. counter) */ + const char *mult_names; /**< Multiplier names (e.g., "KGM") */ + int mult; /**< Multiplier value (e.g., 1024) */ + int verbose; /**< Suggest only in verbose output */ + } data[15]; +} drmStatsT; + + + /* All of these enums *MUST* match with the + kernel implementation -- so do *NOT* + change them! (The drmlib implementation + will just copy the flags instead of + translating them.) */ +typedef enum { + DRM_FRAME_BUFFER = 0, /**< WC, no caching, no core dump */ + DRM_REGISTERS = 1, /**< no caching, no core dump */ + DRM_SHM = 2, /**< shared, cached */ + DRM_AGP = 3, /**< AGP/GART */ + DRM_SCATTER_GATHER = 4, /**< PCI scatter/gather */ + DRM_CONSISTENT = 5 /**< PCI consistent */ +} drmMapType; + +typedef enum { + DRM_RESTRICTED = 0x0001, /**< Cannot be mapped to client-virtual */ + DRM_READ_ONLY = 0x0002, /**< Read-only in client-virtual */ + DRM_LOCKED = 0x0004, /**< Physical pages locked */ + DRM_KERNEL = 0x0008, /**< Kernel requires access */ + DRM_WRITE_COMBINING = 0x0010, /**< Use write-combining, if available */ + DRM_CONTAINS_LOCK = 0x0020, /**< SHM page that contains lock */ + DRM_REMOVABLE = 0x0040 /**< Removable mapping */ +} drmMapFlags; + +/** + * \warning These values *MUST* match drm.h + */ +typedef enum { + /** \name Flags for DMA buffer dispatch */ + /*@{*/ + DRM_DMA_BLOCK = 0x01, /**< + * Block until buffer dispatched. + * + * \note the buffer may not yet have been + * processed by the hardware -- getting a + * hardware lock with the hardware quiescent + * will ensure that the buffer has been + * processed. + */ + DRM_DMA_WHILE_LOCKED = 0x02, /**< Dispatch while lock held */ + DRM_DMA_PRIORITY = 0x04, /**< High priority dispatch */ + /*@}*/ + + /** \name Flags for DMA buffer request */ + /*@{*/ + DRM_DMA_WAIT = 0x10, /**< Wait for free buffers */ + DRM_DMA_SMALLER_OK = 0x20, /**< Smaller-than-requested buffers OK */ + DRM_DMA_LARGER_OK = 0x40 /**< Larger-than-requested buffers OK */ + /*@}*/ +} drmDMAFlags; + +typedef enum { + DRM_PAGE_ALIGN = 0x01, + DRM_AGP_BUFFER = 0x02, + DRM_SG_BUFFER = 0x04, + DRM_FB_BUFFER = 0x08, + DRM_PCI_BUFFER_RO = 0x10 +} drmBufDescFlags; + +typedef enum { + DRM_LOCK_READY = 0x01, /**< Wait until hardware is ready for DMA */ + DRM_LOCK_QUIESCENT = 0x02, /**< Wait until hardware quiescent */ + DRM_LOCK_FLUSH = 0x04, /**< Flush this context's DMA queue first */ + DRM_LOCK_FLUSH_ALL = 0x08, /**< Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + DRM_HALT_ALL_QUEUES = 0x10, /**< Halt all current and future queues */ + DRM_HALT_CUR_QUEUES = 0x20 /**< Halt all current queues */ +} drmLockFlags; + +typedef enum { + DRM_CONTEXT_PRESERVED = 0x01, /**< This context is preserved and + never swapped. */ + DRM_CONTEXT_2DONLY = 0x02 /**< This context is for 2D rendering only. */ +} drm_context_tFlags, *drm_context_tFlagsPtr; + +typedef struct _drmBufDesc { + int count; /**< Number of buffers of this size */ + int size; /**< Size in bytes */ + int low_mark; /**< Low water mark */ + int high_mark; /**< High water mark */ +} drmBufDesc, *drmBufDescPtr; + +typedef struct _drmBufInfo { + int count; /**< Number of buffers described in list */ + drmBufDescPtr list; /**< List of buffer descriptions */ +} drmBufInfo, *drmBufInfoPtr; + +typedef struct _drmBuf { + int idx; /**< Index into the master buffer list */ + int total; /**< Buffer size */ + int used; /**< Amount of buffer in use (for DMA) */ + drmAddress address; /**< Address */ +} drmBuf, *drmBufPtr; + +/** + * Buffer mapping information. + * + * Used by drmMapBufs() and drmUnmapBufs() to store information about the + * mapped buffers. + */ +typedef struct _drmBufMap { + int count; /**< Number of buffers mapped */ + drmBufPtr list; /**< Buffers */ +} drmBufMap, *drmBufMapPtr; + +typedef struct _drmLock { + volatile unsigned int lock; + char padding[60]; + /* This is big enough for most current (and future?) architectures: + DEC Alpha: 32 bytes + Intel Merced: ? + Intel P5/PPro/PII/PIII: 32 bytes + Intel StrongARM: 32 bytes + Intel i386/i486: 16 bytes + MIPS: 32 bytes (?) + Motorola 68k: 16 bytes + Motorola PowerPC: 32 bytes + Sun SPARC: 32 bytes + */ +} drmLock, *drmLockPtr; + +/** + * Indices here refer to the offset into + * list in drmBufInfo + */ +typedef struct _drmDMAReq { + drm_context_t context; /**< Context handle */ + int send_count; /**< Number of buffers to send */ + int *send_list; /**< List of handles to buffers */ + int *send_sizes; /**< Lengths of data to send, in bytes */ + drmDMAFlags flags; /**< Flags */ + int request_count; /**< Number of buffers requested */ + int request_size; /**< Desired size of buffers requested */ + int *request_list; /**< Buffer information */ + int *request_sizes; /**< Minimum acceptable sizes */ + int granted_count; /**< Number of buffers granted at this size */ +} drmDMAReq, *drmDMAReqPtr; + +typedef struct _drmRegion { + drm_handle_t handle; + unsigned int offset; + drmSize size; + drmAddress map; +} drmRegion, *drmRegionPtr; + +typedef struct _drmTextureRegion { + unsigned char next; + unsigned char prev; + unsigned char in_use; + unsigned char padding; /**< Explicitly pad this out */ + unsigned int age; +} drmTextureRegion, *drmTextureRegionPtr; + + +typedef enum { + DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ + DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ + /* bits 1-6 are reserved for high crtcs */ + DRM_VBLANK_HIGH_CRTC_MASK = 0x0000003e, + DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */ + DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */ + DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ + DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ + DRM_VBLANK_SIGNAL = 0x40000000 /* Send signal instead of blocking */ +} drmVBlankSeqType; +#define DRM_VBLANK_HIGH_CRTC_SHIFT 1 + +typedef struct _drmVBlankReq { + drmVBlankSeqType type; + unsigned int sequence; + unsigned long signal; +} drmVBlankReq, *drmVBlankReqPtr; + +typedef struct _drmVBlankReply { + drmVBlankSeqType type; + unsigned int sequence; + long tval_sec; + long tval_usec; +} drmVBlankReply, *drmVBlankReplyPtr; + +typedef union _drmVBlank { + drmVBlankReq request; + drmVBlankReply reply; +} drmVBlank, *drmVBlankPtr; + +typedef struct _drmSetVersion { + int drm_di_major; + int drm_di_minor; + int drm_dd_major; + int drm_dd_minor; +} drmSetVersion, *drmSetVersionPtr; + +#define __drm_dummy_lock(lock) (*(__volatile__ unsigned int *)lock) + +#define DRM_LOCK_HELD 0x80000000U /**< Hardware lock is held */ +#define DRM_LOCK_CONT 0x40000000U /**< Hardware lock is contended */ + +#if defined(__GNUC__) && (__GNUC__ >= 2) +# if defined(__i386) || defined(__AMD64__) || defined(__x86_64__) || defined(__amd64__) + /* Reflect changes here to drmP.h */ +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + int __dummy; /* Can't mark eax as clobbered */ \ + __asm__ __volatile__( \ + "lock ; cmpxchg %4,%1\n\t" \ + "setnz %0" \ + : "=d" (__ret), \ + "=m" (__drm_dummy_lock(lock)), \ + "=a" (__dummy) \ + : "2" (old), \ + "r" (new)); \ + } while (0) + +#elif defined(__alpha__) + +#define DRM_CAS(lock, old, new, ret) \ + do { \ + int tmp, old32; \ + __asm__ __volatile__( \ + " addl $31, %5, %3\n" \ + "1: ldl_l %0, %2\n" \ + " cmpeq %0, %3, %1\n" \ + " beq %1, 2f\n" \ + " mov %4, %0\n" \ + " stl_c %0, %2\n" \ + " beq %0, 3f\n" \ + " mb\n" \ + "2: cmpeq %1, 0, %1\n" \ + ".subsection 2\n" \ + "3: br 1b\n" \ + ".previous" \ + : "=&r"(tmp), "=&r"(ret), \ + "=m"(__drm_dummy_lock(lock)), \ + "=&r"(old32) \ + : "r"(new), "r"(old) \ + : "memory"); \ + } while (0) + +#elif defined(__sparc__) + +#define DRM_CAS(lock,old,new,__ret) \ +do { register unsigned int __old __asm("o0"); \ + register unsigned int __new __asm("o1"); \ + register volatile unsigned int *__lock __asm("o2"); \ + __old = old; \ + __new = new; \ + __lock = (volatile unsigned int *)lock; \ + __asm__ __volatile__( \ + /*"cas [%2], %3, %0"*/ \ + ".word 0xd3e29008\n\t" \ + /*"membar #StoreStore | #StoreLoad"*/ \ + ".word 0x8143e00a" \ + : "=&r" (__new) \ + : "0" (__new), \ + "r" (__lock), \ + "r" (__old) \ + : "memory"); \ + __ret = (__new != __old); \ +} while(0) + +#elif defined(__ia64__) + +#ifdef __INTEL_COMPILER +/* this currently generates bad code (missing stop bits)... */ +#include + +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + unsigned long __result, __old = (old) & 0xffffffff; \ + __mf(); \ + __result = _InterlockedCompareExchange_acq(&__drm_dummy_lock(lock), (new), __old);\ + __ret = (__result) != (__old); \ +/* __ret = (__sync_val_compare_and_swap(&__drm_dummy_lock(lock), \ + (old), (new)) \ + != (old)); */\ + } while (0) + +#else +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + unsigned int __result, __old = (old); \ + __asm__ __volatile__( \ + "mf\n" \ + "mov ar.ccv=%2\n" \ + ";;\n" \ + "cmpxchg4.acq %0=%1,%3,ar.ccv" \ + : "=r" (__result), "=m" (__drm_dummy_lock(lock)) \ + : "r" ((unsigned long)__old), "r" (new) \ + : "memory"); \ + __ret = (__result) != (__old); \ + } while (0) + +#endif + +#elif defined(__powerpc__) + +#define DRM_CAS(lock,old,new,__ret) \ + do { \ + __asm__ __volatile__( \ + "sync;" \ + "0: lwarx %0,0,%1;" \ + " xor. %0,%3,%0;" \ + " bne 1f;" \ + " stwcx. %2,0,%1;" \ + " bne- 0b;" \ + "1: " \ + "sync;" \ + : "=&r"(__ret) \ + : "r"(lock), "r"(new), "r"(old) \ + : "cr0", "memory"); \ + } while (0) + +# elif defined (__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined (__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) \ + || defined (__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) \ + || defined (__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7EM__) + #undef DRM_DEV_MODE + #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) + + #define DRM_CAS(lock,old,new,__ret) \ + do { \ + __asm__ __volatile__ ( \ + "1: ldrex %0, [%1]\n" \ + " teq %0, %2\n" \ + " ite eq\n" \ + " strexeq %0, %3, [%1]\n" \ + " movne %0, #1\n" \ + : "=&r" (__ret) \ + : "r" (lock), "r" (old), "r" (new) \ + : "cc","memory"); \ + } while (0) + +#endif /* architecture */ +#endif /* __GNUC__ >= 2 */ + +#ifndef DRM_CAS +#define DRM_CAS(lock,old,new,ret) do { ret=1; } while (0) /* FAST LOCK FAILS */ +#endif + +#if defined(__alpha__) +#define DRM_CAS_RESULT(_result) long _result +#elif defined(__powerpc__) +#define DRM_CAS_RESULT(_result) int _result +#else +#define DRM_CAS_RESULT(_result) char _result +#endif + +#define DRM_LIGHT_LOCK(fd,lock,context) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ + if (__ret) drmGetLock(fd,context,0); \ + } while(0) + + /* This one counts fast locks -- for + benchmarking only. */ +#define DRM_LIGHT_LOCK_COUNT(fd,lock,context,count) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,context,DRM_LOCK_HELD|context,__ret); \ + if (__ret) drmGetLock(fd,context,0); \ + else ++count; \ + } while(0) + +#define DRM_LOCK(fd,lock,context,flags) \ + do { \ + if (flags) drmGetLock(fd,context,flags); \ + else DRM_LIGHT_LOCK(fd,lock,context); \ + } while(0) + +#define DRM_UNLOCK(fd,lock,context) \ + do { \ + DRM_CAS_RESULT(__ret); \ + DRM_CAS(lock,DRM_LOCK_HELD|context,context,__ret); \ + if (__ret) drmUnlock(fd,context); \ + } while(0) + + /* Simple spin locks */ +#define DRM_SPINLOCK(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + do { \ + DRM_CAS(spin,0,val,__ret); \ + if (__ret) while ((spin)->lock); \ + } while (__ret); \ + } while(0) + +#define DRM_SPINLOCK_TAKE(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + int cur; \ + do { \ + cur = (*spin).lock; \ + DRM_CAS(spin,cur,val,__ret); \ + } while (__ret); \ + } while(0) + +#define DRM_SPINLOCK_COUNT(spin,val,count,__ret) \ + do { \ + int __i; \ + __ret = 1; \ + for (__i = 0; __ret && __i < count; __i++) { \ + DRM_CAS(spin,0,val,__ret); \ + if (__ret) for (;__i < count && (spin)->lock; __i++); \ + } \ + } while(0) + +#define DRM_SPINUNLOCK(spin,val) \ + do { \ + DRM_CAS_RESULT(__ret); \ + if ((*spin).lock == val) { /* else server stole lock */ \ + do { \ + DRM_CAS(spin,val,0,__ret); \ + } while (__ret); \ + } \ + } while(0) + + + +/* General user-level programmer's API: unprivileged */ +extern int drmAvailable(void); +extern int drmOpen(const char *name, const char *busid); + +#define DRM_NODE_PRIMARY 0 +#define DRM_NODE_CONTROL 1 +#define DRM_NODE_RENDER 2 +#define DRM_NODE_MAX 3 + +extern int drmOpenWithType(const char *name, const char *busid, + int type); + +extern int drmOpenControl(int minor); +extern int drmOpenRender(int minor); +extern int drmClose(int fd); +extern drmVersionPtr drmGetVersion(int fd); +extern drmVersionPtr drmGetLibVersion(int fd); +extern int drmGetCap(int fd, uint64_t capability, uint64_t *value); +extern void drmFreeVersion(drmVersionPtr); +extern int drmGetMagic(int fd, drm_magic_t * magic); +extern char *drmGetBusid(int fd); +extern int drmGetInterruptFromBusID(int fd, int busnum, int devnum, + int funcnum); +extern int drmGetMap(int fd, int idx, drm_handle_t *offset, + drmSize *size, drmMapType *type, + drmMapFlags *flags, drm_handle_t *handle, + int *mtrr); +extern int drmGetClient(int fd, int idx, int *auth, int *pid, + int *uid, unsigned long *magic, + unsigned long *iocs); +extern int drmGetStats(int fd, drmStatsT *stats); +extern int drmSetInterfaceVersion(int fd, drmSetVersion *version); +extern int drmCommandNone(int fd, unsigned long drmCommandIndex); +extern int drmCommandRead(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); +extern int drmCommandWrite(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); +extern int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, + void *data, unsigned long size); + +/* General user-level programmer's API: X server (root) only */ +extern void drmFreeBusid(const char *busid); +extern int drmSetBusid(int fd, const char *busid); +extern int drmAuthMagic(int fd, drm_magic_t magic); +extern int drmAddMap(int fd, + drm_handle_t offset, + drmSize size, + drmMapType type, + drmMapFlags flags, + drm_handle_t * handle); +extern int drmRmMap(int fd, drm_handle_t handle); +extern int drmAddContextPrivateMapping(int fd, drm_context_t ctx_id, + drm_handle_t handle); + +extern int drmAddBufs(int fd, int count, int size, + drmBufDescFlags flags, + int agp_offset); +extern int drmMarkBufs(int fd, double low, double high); +extern int drmCreateContext(int fd, drm_context_t * handle); +extern int drmSetContextFlags(int fd, drm_context_t context, + drm_context_tFlags flags); +extern int drmGetContextFlags(int fd, drm_context_t context, + drm_context_tFlagsPtr flags); +extern int drmAddContextTag(int fd, drm_context_t context, void *tag); +extern int drmDelContextTag(int fd, drm_context_t context); +extern void *drmGetContextTag(int fd, drm_context_t context); +extern drm_context_t * drmGetReservedContextList(int fd, int *count); +extern void drmFreeReservedContextList(drm_context_t *); +extern int drmSwitchToContext(int fd, drm_context_t context); +extern int drmDestroyContext(int fd, drm_context_t handle); +extern int drmCreateDrawable(int fd, drm_drawable_t * handle); +extern int drmDestroyDrawable(int fd, drm_drawable_t handle); +extern int drmUpdateDrawableInfo(int fd, drm_drawable_t handle, + drm_drawable_info_type_t type, + unsigned int num, void *data); +extern int drmCtlInstHandler(int fd, int irq); +extern int drmCtlUninstHandler(int fd); +extern int drmSetClientCap(int fd, uint64_t capability, + uint64_t value); + +extern int drmCrtcGetSequence(int fd, uint32_t crtcId, + uint64_t *sequence, uint64_t *ns); +extern int drmCrtcQueueSequence(int fd, uint32_t crtcId, + uint32_t flags, uint64_t sequence, + uint64_t *sequence_queued, + uint64_t user_data); +/* General user-level programmer's API: authenticated client and/or X */ +extern int drmMap(int fd, + drm_handle_t handle, + drmSize size, + drmAddressPtr address); +extern int drmUnmap(drmAddress address, drmSize size); +extern drmBufInfoPtr drmGetBufInfo(int fd); +extern drmBufMapPtr drmMapBufs(int fd); +extern int drmUnmapBufs(drmBufMapPtr bufs); +extern int drmDMA(int fd, drmDMAReqPtr request); +extern int drmFreeBufs(int fd, int count, int *list); +extern int drmGetLock(int fd, + drm_context_t context, + drmLockFlags flags); +extern int drmUnlock(int fd, drm_context_t context); +extern int drmFinish(int fd, int context, drmLockFlags flags); +extern int drmGetContextPrivateMapping(int fd, drm_context_t ctx_id, + drm_handle_t * handle); + +/* AGP/GART support: X server (root) only */ +extern int drmAgpAcquire(int fd); +extern int drmAgpRelease(int fd); +extern int drmAgpEnable(int fd, unsigned long mode); +extern int drmAgpAlloc(int fd, unsigned long size, + unsigned long type, unsigned long *address, + drm_handle_t *handle); +extern int drmAgpFree(int fd, drm_handle_t handle); +extern int drmAgpBind(int fd, drm_handle_t handle, + unsigned long offset); +extern int drmAgpUnbind(int fd, drm_handle_t handle); + +/* AGP/GART info: authenticated client and/or X */ +extern int drmAgpVersionMajor(int fd); +extern int drmAgpVersionMinor(int fd); +extern unsigned long drmAgpGetMode(int fd); +extern unsigned long drmAgpBase(int fd); /* Physical location */ +extern unsigned long drmAgpSize(int fd); /* Bytes */ +extern unsigned long drmAgpMemoryUsed(int fd); +extern unsigned long drmAgpMemoryAvail(int fd); +extern unsigned int drmAgpVendorId(int fd); +extern unsigned int drmAgpDeviceId(int fd); + +/* PCI scatter/gather support: X server (root) only */ +extern int drmScatterGatherAlloc(int fd, unsigned long size, + drm_handle_t *handle); +extern int drmScatterGatherFree(int fd, drm_handle_t handle); + +extern int drmWaitVBlank(int fd, drmVBlankPtr vbl); + +/* Support routines */ +extern void drmSetServerInfo(drmServerInfoPtr info); +extern int drmError(int err, const char *label); +extern void *drmMalloc(int size); +extern void drmFree(void *pt); + +/* Hash table routines */ +extern void *drmHashCreate(void); +extern int drmHashDestroy(void *t); +extern int drmHashLookup(void *t, unsigned long key, void **value); +extern int drmHashInsert(void *t, unsigned long key, void *value); +extern int drmHashDelete(void *t, unsigned long key); +extern int drmHashFirst(void *t, unsigned long *key, void **value); +extern int drmHashNext(void *t, unsigned long *key, void **value); + +/* PRNG routines */ +extern void *drmRandomCreate(unsigned long seed); +extern int drmRandomDestroy(void *state); +extern unsigned long drmRandom(void *state); +extern double drmRandomDouble(void *state); + +/* Skip list routines */ + +extern void *drmSLCreate(void); +extern int drmSLDestroy(void *l); +extern int drmSLLookup(void *l, unsigned long key, void **value); +extern int drmSLInsert(void *l, unsigned long key, void *value); +extern int drmSLDelete(void *l, unsigned long key); +extern int drmSLNext(void *l, unsigned long *key, void **value); +extern int drmSLFirst(void *l, unsigned long *key, void **value); +extern void drmSLDump(void *l); +extern int drmSLLookupNeighbors(void *l, unsigned long key, + unsigned long *prev_key, void **prev_value, + unsigned long *next_key, void **next_value); + +extern int drmOpenOnce(void *unused, const char *BusID, int *newlyopened); +extern int drmOpenOnceWithType(const char *BusID, int *newlyopened, int type); +extern void drmCloseOnce(int fd); +extern void drmMsg(const char *format, ...) DRM_PRINTFLIKE(1, 2); + +extern int drmSetMaster(int fd); +extern int drmDropMaster(int fd); +extern int drmIsMaster(int fd); + +#define DRM_EVENT_CONTEXT_VERSION 4 + +typedef struct _drmEventContext { + + /* This struct is versioned so we can add more pointers if we + * add more events. */ + int version; + + void (*vblank_handler)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + + void (*page_flip_handler)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + void *user_data); + + void (*page_flip_handler2)(int fd, + unsigned int sequence, + unsigned int tv_sec, + unsigned int tv_usec, + unsigned int crtc_id, + void *user_data); + + void (*sequence_handler)(int fd, + uint64_t sequence, + uint64_t ns, + uint64_t user_data); +} drmEventContext, *drmEventContextPtr; + +extern int drmHandleEvent(int fd, drmEventContextPtr evctx); + +extern char *drmGetDeviceNameFromFd(int fd); + +/* Improved version of drmGetDeviceNameFromFd which attributes for any type of + * device/node - card, control or renderD. + */ +extern char *drmGetDeviceNameFromFd2(int fd); +extern int drmGetNodeTypeFromFd(int fd); + +extern int drmPrimeHandleToFD(int fd, uint32_t handle, uint32_t flags, int *prime_fd); +extern int drmPrimeFDToHandle(int fd, int prime_fd, uint32_t *handle); + +extern char *drmGetPrimaryDeviceNameFromFd(int fd); +extern char *drmGetRenderDeviceNameFromFd(int fd); + +#define DRM_BUS_PCI 0 +#define DRM_BUS_USB 1 +#define DRM_BUS_PLATFORM 2 +#define DRM_BUS_HOST1X 3 + +typedef struct _drmPciBusInfo { + uint16_t domain; + uint8_t bus; + uint8_t dev; + uint8_t func; +} drmPciBusInfo, *drmPciBusInfoPtr; + +typedef struct _drmPciDeviceInfo { + uint16_t vendor_id; + uint16_t device_id; + uint16_t subvendor_id; + uint16_t subdevice_id; + uint8_t revision_id; +} drmPciDeviceInfo, *drmPciDeviceInfoPtr; + +typedef struct _drmUsbBusInfo { + uint8_t bus; + uint8_t dev; +} drmUsbBusInfo, *drmUsbBusInfoPtr; + +typedef struct _drmUsbDeviceInfo { + uint16_t vendor; + uint16_t product; +} drmUsbDeviceInfo, *drmUsbDeviceInfoPtr; + +#define DRM_PLATFORM_DEVICE_NAME_LEN 512 + +typedef struct _drmPlatformBusInfo { + char fullname[DRM_PLATFORM_DEVICE_NAME_LEN]; +} drmPlatformBusInfo, *drmPlatformBusInfoPtr; + +typedef struct _drmPlatformDeviceInfo { + char **compatible; /* NULL terminated list of compatible strings */ +} drmPlatformDeviceInfo, *drmPlatformDeviceInfoPtr; + +#define DRM_HOST1X_DEVICE_NAME_LEN 512 + +typedef struct _drmHost1xBusInfo { + char fullname[DRM_HOST1X_DEVICE_NAME_LEN]; +} drmHost1xBusInfo, *drmHost1xBusInfoPtr; + +typedef struct _drmHost1xDeviceInfo { + char **compatible; /* NULL terminated list of compatible strings */ +} drmHost1xDeviceInfo, *drmHost1xDeviceInfoPtr; + +typedef struct _drmDevice { + char **nodes; /* DRM_NODE_MAX sized array */ + int available_nodes; /* DRM_NODE_* bitmask */ + int bustype; + union { + drmPciBusInfoPtr pci; + drmUsbBusInfoPtr usb; + drmPlatformBusInfoPtr platform; + drmHost1xBusInfoPtr host1x; + } businfo; + union { + drmPciDeviceInfoPtr pci; + drmUsbDeviceInfoPtr usb; + drmPlatformDeviceInfoPtr platform; + drmHost1xDeviceInfoPtr host1x; + } deviceinfo; +} drmDevice, *drmDevicePtr; + +extern int drmGetDevice(int fd, drmDevicePtr *device); +extern void drmFreeDevice(drmDevicePtr *device); + +extern int drmGetDevices(drmDevicePtr devices[], int max_devices); +extern void drmFreeDevices(drmDevicePtr devices[], int count); + +#define DRM_DEVICE_GET_PCI_REVISION (1 << 0) +extern int drmGetDevice2(int fd, uint32_t flags, drmDevicePtr *device); +extern int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices); + +extern int drmDevicesEqual(drmDevicePtr a, drmDevicePtr b); + +extern int drmSyncobjCreate(int fd, uint32_t flags, uint32_t *handle); +extern int drmSyncobjDestroy(int fd, uint32_t handle); +extern int drmSyncobjHandleToFD(int fd, uint32_t handle, int *obj_fd); +extern int drmSyncobjFDToHandle(int fd, int obj_fd, uint32_t *handle); + +extern int drmSyncobjImportSyncFile(int fd, uint32_t handle, int sync_file_fd); +extern int drmSyncobjExportSyncFile(int fd, uint32_t handle, int *sync_file_fd); +extern int drmSyncobjWait(int fd, uint32_t *handles, unsigned num_handles, + int64_t timeout_nsec, unsigned flags, + uint32_t *first_signaled); +extern int drmSyncobjReset(int fd, const uint32_t *handles, uint32_t handle_count); +extern int drmSyncobjSignal(int fd, const uint32_t *handles, uint32_t handle_count); +extern int drmSyncobjTimelineSignal(int fd, const uint32_t *handles, + uint64_t *points, uint32_t handle_count); +extern int drmSyncobjTimelineWait(int fd, uint32_t *handles, uint64_t *points, + unsigned num_handles, + int64_t timeout_nsec, unsigned flags, + uint32_t *first_signaled); +extern int drmSyncobjQuery(int fd, uint32_t *handles, uint64_t *points, + uint32_t handle_count); +extern int drmSyncobjTransfer(int fd, + uint32_t dst_handle, uint64_t dst_point, + uint32_t src_handle, uint64_t src_point, + uint32_t flags); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/xf86drmMode.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/xf86drmMode.h new file mode 100644 index 0000000..a32902f --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/drm/include/xf86drmMode.h @@ -0,0 +1,551 @@ +/* + * \file xf86drmMode.h + * Header for DRM modesetting interface. + * + * \author Jakob Bornecrantz + * + * \par Acknowledgements: + * Feb 2007, Dave Airlie + */ + +/* + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2007-2008 Dave Airlie + * Copyright (c) 2007-2008 Jakob Bornecrantz + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef _XF86DRMMODE_H_ +#define _XF86DRMMODE_H_ + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +/* + * This is the interface for modesetting for drm. + * + * In order to use this interface you must include either or another + * header defining uint32_t, int32_t and uint16_t. + * + * It aims to provide a randr1.2 compatible interface for modesettings in the + * kernel, the interface is also meant to be used by libraries like EGL. + * + * More information can be found in randrproto.txt which can be found here: + * http://gitweb.freedesktop.org/?p=xorg/proto/randrproto.git + * + * There are some major differences to be noted. Unlike the randr1.2 proto you + * need to create the memory object of the framebuffer yourself with the ttm + * buffer object interface. This object needs to be pinned. + */ + +/* + * If we pickup an old version of drm.h which doesn't include drm_mode.h + * we should redefine defines. This is so that builds doesn't breaks with + * new libdrm on old kernels. + */ +#ifndef _DRM_MODE_H + +#define DRM_DISPLAY_INFO_LEN 32 +#define DRM_CONNECTOR_NAME_LEN 32 +#define DRM_DISPLAY_MODE_LEN 32 +#define DRM_PROP_NAME_LEN 32 + +#define DRM_MODE_TYPE_BUILTIN (1<<0) +#define DRM_MODE_TYPE_CLOCK_C ((1<<1) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_CRTC_C ((1<<2) | DRM_MODE_TYPE_BUILTIN) +#define DRM_MODE_TYPE_PREFERRED (1<<3) +#define DRM_MODE_TYPE_DEFAULT (1<<4) +#define DRM_MODE_TYPE_USERDEF (1<<5) +#define DRM_MODE_TYPE_DRIVER (1<<6) + +/* Video mode flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_FLAG_PHSYNC (1<<0) +#define DRM_MODE_FLAG_NHSYNC (1<<1) +#define DRM_MODE_FLAG_PVSYNC (1<<2) +#define DRM_MODE_FLAG_NVSYNC (1<<3) +#define DRM_MODE_FLAG_INTERLACE (1<<4) +#define DRM_MODE_FLAG_DBLSCAN (1<<5) +#define DRM_MODE_FLAG_CSYNC (1<<6) +#define DRM_MODE_FLAG_PCSYNC (1<<7) +#define DRM_MODE_FLAG_NCSYNC (1<<8) +#define DRM_MODE_FLAG_HSKEW (1<<9) /* hskew provided */ +#define DRM_MODE_FLAG_BCAST (1<<10) +#define DRM_MODE_FLAG_PIXMUX (1<<11) +#define DRM_MODE_FLAG_DBLCLK (1<<12) +#define DRM_MODE_FLAG_CLKDIV2 (1<<13) +#define DRM_MODE_FLAG_3D_MASK (0x1f<<14) +#define DRM_MODE_FLAG_3D_NONE (0<<14) +#define DRM_MODE_FLAG_3D_FRAME_PACKING (1<<14) +#define DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE (2<<14) +#define DRM_MODE_FLAG_3D_LINE_ALTERNATIVE (3<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL (4<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH (5<<14) +#define DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH (6<<14) +#define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) +#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + +/* DPMS flags */ +/* bit compatible with the xorg definitions. */ +#define DRM_MODE_DPMS_ON 0 +#define DRM_MODE_DPMS_STANDBY 1 +#define DRM_MODE_DPMS_SUSPEND 2 +#define DRM_MODE_DPMS_OFF 3 + +/* Scaling mode options */ +#define DRM_MODE_SCALE_NON_GPU 0 +#define DRM_MODE_SCALE_FULLSCREEN 1 +#define DRM_MODE_SCALE_NO_SCALE 2 +#define DRM_MODE_SCALE_ASPECT 3 + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 + +#define DRM_MODE_ENCODER_NONE 0 +#define DRM_MODE_ENCODER_DAC 1 +#define DRM_MODE_ENCODER_TMDS 2 +#define DRM_MODE_ENCODER_LVDS 3 +#define DRM_MODE_ENCODER_TVDAC 4 +#define DRM_MODE_ENCODER_VIRTUAL 5 +#define DRM_MODE_ENCODER_DSI 6 +#define DRM_MODE_ENCODER_DPMST 7 +#define DRM_MODE_ENCODER_DPI 8 + +#define DRM_MODE_SUBCONNECTOR_Automatic 0 +#define DRM_MODE_SUBCONNECTOR_Unknown 0 +#define DRM_MODE_SUBCONNECTOR_DVID 3 +#define DRM_MODE_SUBCONNECTOR_DVIA 4 +#define DRM_MODE_SUBCONNECTOR_Composite 5 +#define DRM_MODE_SUBCONNECTOR_SVIDEO 6 +#define DRM_MODE_SUBCONNECTOR_Component 8 +#define DRM_MODE_SUBCONNECTOR_SCART 9 + +#define DRM_MODE_CONNECTOR_Unknown 0 +#define DRM_MODE_CONNECTOR_VGA 1 +#define DRM_MODE_CONNECTOR_DVII 2 +#define DRM_MODE_CONNECTOR_DVID 3 +#define DRM_MODE_CONNECTOR_DVIA 4 +#define DRM_MODE_CONNECTOR_Composite 5 +#define DRM_MODE_CONNECTOR_SVIDEO 6 +#define DRM_MODE_CONNECTOR_LVDS 7 +#define DRM_MODE_CONNECTOR_Component 8 +#define DRM_MODE_CONNECTOR_9PinDIN 9 +#define DRM_MODE_CONNECTOR_DisplayPort 10 +#define DRM_MODE_CONNECTOR_HDMIA 11 +#define DRM_MODE_CONNECTOR_HDMIB 12 +#define DRM_MODE_CONNECTOR_TV 13 +#define DRM_MODE_CONNECTOR_eDP 14 +#define DRM_MODE_CONNECTOR_VIRTUAL 15 +#define DRM_MODE_CONNECTOR_DSI 16 +#define DRM_MODE_CONNECTOR_DPI 17 + +#define DRM_MODE_PROP_PENDING (1<<0) +#define DRM_MODE_PROP_RANGE (1<<1) +#define DRM_MODE_PROP_IMMUTABLE (1<<2) +#define DRM_MODE_PROP_ENUM (1<<3) /* enumerated type with text strings */ +#define DRM_MODE_PROP_BLOB (1<<4) + +#define DRM_MODE_CURSOR_BO (1<<0) +#define DRM_MODE_CURSOR_MOVE (1<<1) + +#endif /* _DRM_MODE_H */ + + +/* + * Feature defines + * + * Just because these are defined doesn't mean that the kernel + * can do that feature, its just for new code vs old libdrm. + */ +#define DRM_MODE_FEATURE_KMS 1 +#define DRM_MODE_FEATURE_DIRTYFB 1 + + +typedef struct _drmModeRes { + + int count_fbs; + uint32_t *fbs; + + int count_crtcs; + uint32_t *crtcs; + + int count_connectors; + uint32_t *connectors; + + int count_encoders; + uint32_t *encoders; + + uint32_t min_width, max_width; + uint32_t min_height, max_height; +} drmModeRes, *drmModeResPtr; + +typedef struct _drmModeModeInfo { + uint32_t clock; + uint16_t hdisplay, hsync_start, hsync_end, htotal, hskew; + uint16_t vdisplay, vsync_start, vsync_end, vtotal, vscan; + + uint32_t vrefresh; + + uint32_t flags; + uint32_t type; + char name[DRM_DISPLAY_MODE_LEN]; +} drmModeModeInfo, *drmModeModeInfoPtr; + +typedef struct _drmModeFB { + uint32_t fb_id; + uint32_t width, height; + uint32_t pitch; + uint32_t bpp; + uint32_t depth; + /* driver specific handle */ + uint32_t handle; +} drmModeFB, *drmModeFBPtr; + +typedef struct drm_clip_rect drmModeClip, *drmModeClipPtr; + +typedef struct _drmModePropertyBlob { + uint32_t id; + uint32_t length; + void *data; +} drmModePropertyBlobRes, *drmModePropertyBlobPtr; + +typedef struct _drmModeProperty { + uint32_t prop_id; + uint32_t flags; + char name[DRM_PROP_NAME_LEN]; + int count_values; + uint64_t *values; /* store the blob lengths */ + int count_enums; + struct drm_mode_property_enum *enums; + int count_blobs; + uint32_t *blob_ids; /* store the blob IDs */ +} drmModePropertyRes, *drmModePropertyPtr; + +static __inline int drm_property_type_is(drmModePropertyPtr property, + uint32_t type) +{ + /* instanceof for props.. handles extended type vs original types: */ + if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) + return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type; + return property->flags & type; +} + +typedef struct _drmModeCrtc { + uint32_t crtc_id; + uint32_t buffer_id; /**< FB id to connect to 0 = disconnect */ + + uint32_t x, y; /**< Position on the framebuffer */ + uint32_t width, height; + int mode_valid; + drmModeModeInfo mode; + + int gamma_size; /**< Number of gamma stops */ + +} drmModeCrtc, *drmModeCrtcPtr; + +typedef struct _drmModeEncoder { + uint32_t encoder_id; + uint32_t encoder_type; + uint32_t crtc_id; + uint32_t possible_crtcs; + uint32_t possible_clones; +} drmModeEncoder, *drmModeEncoderPtr; + +typedef enum { + DRM_MODE_CONNECTED = 1, + DRM_MODE_DISCONNECTED = 2, + DRM_MODE_UNKNOWNCONNECTION = 3 +} drmModeConnection; + +typedef enum { + DRM_MODE_SUBPIXEL_UNKNOWN = 1, + DRM_MODE_SUBPIXEL_HORIZONTAL_RGB = 2, + DRM_MODE_SUBPIXEL_HORIZONTAL_BGR = 3, + DRM_MODE_SUBPIXEL_VERTICAL_RGB = 4, + DRM_MODE_SUBPIXEL_VERTICAL_BGR = 5, + DRM_MODE_SUBPIXEL_NONE = 6 +} drmModeSubPixel; + +typedef struct _drmModeConnector { + uint32_t connector_id; + uint32_t encoder_id; /**< Encoder currently connected to */ + uint32_t connector_type; + uint32_t connector_type_id; + drmModeConnection connection; + uint32_t mmWidth, mmHeight; /**< HxW in millimeters */ + drmModeSubPixel subpixel; + + int count_modes; + drmModeModeInfoPtr modes; + + int count_props; + uint32_t *props; /**< List of property ids */ + uint64_t *prop_values; /**< List of property values */ + + int count_encoders; + uint32_t *encoders; /**< List of encoder ids */ +} drmModeConnector, *drmModeConnectorPtr; + +#define DRM_PLANE_TYPE_OVERLAY 0 +#define DRM_PLANE_TYPE_PRIMARY 1 +#define DRM_PLANE_TYPE_CURSOR 2 + +typedef struct _drmModeObjectProperties { + uint32_t count_props; + uint32_t *props; + uint64_t *prop_values; +} drmModeObjectProperties, *drmModeObjectPropertiesPtr; + +typedef struct _drmModePlane { + uint32_t count_formats; + uint32_t *formats; + uint32_t plane_id; + + uint32_t crtc_id; + uint32_t fb_id; + + uint32_t crtc_x, crtc_y; + uint32_t x, y; + + uint32_t possible_crtcs; + uint32_t gamma_size; +} drmModePlane, *drmModePlanePtr; + +typedef struct _drmModePlaneRes { + uint32_t count_planes; + uint32_t *planes; +} drmModePlaneRes, *drmModePlaneResPtr; + +extern void drmModeFreeModeInfo( drmModeModeInfoPtr ptr ); +extern void drmModeFreeResources( drmModeResPtr ptr ); +extern void drmModeFreeFB( drmModeFBPtr ptr ); +extern void drmModeFreeCrtc( drmModeCrtcPtr ptr ); +extern void drmModeFreeConnector( drmModeConnectorPtr ptr ); +extern void drmModeFreeEncoder( drmModeEncoderPtr ptr ); +extern void drmModeFreePlane( drmModePlanePtr ptr ); +extern void drmModeFreePlaneResources(drmModePlaneResPtr ptr); + +/** + * Retrieves all of the resources associated with a card. + */ +extern drmModeResPtr drmModeGetResources(int fd); + +/* + * FrameBuffer manipulation. + */ + +/** + * Retrieve information about framebuffer bufferId + */ +extern drmModeFBPtr drmModeGetFB(int fd, uint32_t bufferId); + +/** + * Creates a new framebuffer with an buffer object as its scanout buffer. + */ +extern int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, + uint8_t bpp, uint32_t pitch, uint32_t bo_handle, + uint32_t *buf_id); +/* ...with a specific pixel format */ +extern int drmModeAddFB2(int fd, uint32_t width, uint32_t height, + uint32_t pixel_format, const uint32_t bo_handles[4], + const uint32_t pitches[4], const uint32_t offsets[4], + uint32_t *buf_id, uint32_t flags); + +/* ...with format modifiers */ +int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height, + uint32_t pixel_format, const uint32_t bo_handles[4], + const uint32_t pitches[4], const uint32_t offsets[4], + const uint64_t modifier[4], uint32_t *buf_id, + uint32_t flags); + +/** + * Destroies the given framebuffer. + */ +extern int drmModeRmFB(int fd, uint32_t bufferId); + +/** + * Mark a region of a framebuffer as dirty. + */ +extern int drmModeDirtyFB(int fd, uint32_t bufferId, + drmModeClipPtr clips, uint32_t num_clips); + + +/* + * Crtc functions + */ + +/** + * Retrieve information about the ctrt crtcId + */ +extern drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId); + +/** + * Set the mode on a crtc crtcId with the given mode modeId. + */ +int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, + uint32_t x, uint32_t y, uint32_t *connectors, int count, + drmModeModeInfoPtr mode); + +/* + * Cursor functions + */ + +/** + * Set the cursor on crtc + */ +int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height); + +int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); +/** + * Move the cursor on crtc + */ +int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y); + +/** + * Encoder functions + */ +drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id); + +/* + * Connector manipulation + */ + +/** + * Retrieve all information about the connector connectorId. This will do a + * forced probe on the connector to retrieve remote information such as EDIDs + * from the display device. + */ +extern drmModeConnectorPtr drmModeGetConnector(int fd, + uint32_t connectorId); + +/** + * Retrieve current information, i.e the currently active mode and encoder, + * about the connector connectorId. This will not do any probing on the + * connector or remote device, and only reports what is currently known. + * For the complete set of modes and encoders associated with the connector + * use drmModeGetConnector() which will do a probe to determine any display + * link changes first. + */ +extern drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, + uint32_t connector_id); + +/** + * Attaches the given mode to an connector. + */ +extern int drmModeAttachMode(int fd, uint32_t connectorId, drmModeModeInfoPtr mode_info); + +/** + * Detaches a mode from the connector + * must be unused, by the given mode. + */ +extern int drmModeDetachMode(int fd, uint32_t connectorId, drmModeModeInfoPtr mode_info); + +extern drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId); +extern void drmModeFreeProperty(drmModePropertyPtr ptr); + +extern drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id); +extern void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr); +extern int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, + uint64_t value); +extern int drmCheckModesettingSupported(const char *busid); + +extern int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size, + uint16_t *red, uint16_t *green, uint16_t *blue); +extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, + uint32_t flags, void *user_data); +extern int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id, + uint32_t flags, void *user_data, + uint32_t target_vblank); + +extern drmModePlaneResPtr drmModeGetPlaneResources(int fd); +extern drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id); +extern int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id, + uint32_t fb_id, uint32_t flags, + int32_t crtc_x, int32_t crtc_y, + uint32_t crtc_w, uint32_t crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + +extern drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd, + uint32_t object_id, + uint32_t object_type); +extern void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr); +extern int drmModeObjectSetProperty(int fd, uint32_t object_id, + uint32_t object_type, uint32_t property_id, + uint64_t value); + + +typedef struct _drmModeAtomicReq drmModeAtomicReq, *drmModeAtomicReqPtr; + +extern drmModeAtomicReqPtr drmModeAtomicAlloc(void); +extern drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr req); +extern int drmModeAtomicMerge(drmModeAtomicReqPtr base, + drmModeAtomicReqPtr augment); +extern void drmModeAtomicFree(drmModeAtomicReqPtr req); +extern int drmModeAtomicGetCursor(drmModeAtomicReqPtr req); +extern void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor); +extern int drmModeAtomicAddProperty(drmModeAtomicReqPtr req, + uint32_t object_id, + uint32_t property_id, + uint64_t value); +extern int drmModeAtomicCommit(int fd, + drmModeAtomicReqPtr req, + uint32_t flags, + void *user_data); + +extern int drmModeCreatePropertyBlob(int fd, const void *data, size_t size, + uint32_t *id); +extern int drmModeDestroyPropertyBlob(int fd, uint32_t id); + +/* + * DRM mode lease APIs. These create and manage new drm_masters with + * access to a subset of the available DRM resources + */ + +extern int drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id); + +typedef struct drmModeLesseeList { + uint32_t count; + uint32_t lessees[0]; +} drmModeLesseeListRes, *drmModeLesseeListPtr; + +extern drmModeLesseeListPtr drmModeListLessees(int fd); + +typedef struct drmModeObjectList { + uint32_t count; + uint32_t objects[0]; +} drmModeObjectListRes, *drmModeObjectListPtr; + +extern drmModeObjectListPtr drmModeGetLease(int fd); + +extern int drmModeRevokeLease(int fd, uint32_t lessee_id); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RgaApi.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RgaApi.h new file mode 100644 index 0000000..c484f4d --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RgaApi.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "drmrga.h" + +#include "RockchipRgaMacro.h" + +#ifdef __cplusplus +extern "C"{ +#endif + + +int c_RkRgaInit(); +void c_RkRgaDeInit(); +int c_RkRgaGetAllocBuffer(bo_t *bo_info, int width, int height, int bpp); +int c_RkRgaGetMmap(bo_t *bo_info); +int c_RkRgaUnmap(bo_t *bo_info); +int c_RkRgaGetBufferFd(bo_t *bo_info, int *fd); +int c_RkRgaBlit(rga_info_t *src, rga_info_t *dst, rga_info_t *src1); +int c_RkRgaColorFill(rga_info_t *dst); +#ifdef __cplusplus +} +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipFileOps.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipFileOps.h new file mode 100644 index 0000000..6d123ec --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipFileOps.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _rk_file_ops_h_ +#define _rk_file_ops_h_ + +// ------------------------------------------------------------------------------- +int get_buf_from_file(void *buf, int f, int sw, int sh, int index); +int output_buf_data_to_file(void *buf, int f, int sw, int sh, int index); +#endif + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRga.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRga.h new file mode 100644 index 0000000..c49c467 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRga.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +//#include +#include + +////////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "stdio.h" + +////////////////////////////////////////////////////////////////////////////////// + +// ------------------------------------------------------------------------------- + +class RockchipRga +{ +/************************************public**************************************/ + +public: + + RockchipRga(); + ~RockchipRga(); + + int RkRgaInit(); + void RkRgaDeInit(); + int RkRgaAllocBuffer(int drm_fd /* input */, bo_t *bo_info, + int width, int height, int bpp); + int RkRgaFreeBuffer(int drm_fd /* input */, bo_t *bo_info); + int RkRgaGetAllocBuffer(bo_t *bo_info, int width, int height, int bpp); + int RkRgaGetMmap(bo_t *bo_info); + int RkRgaUnmap(bo_t *bo_info); + int RkRgaFree(bo_t *bo_info); + int RkRgaGetBufferFd(bo_t *bo_info, int *fd); + int RkRgaBlit(rga_info *src, rga_info *dst, rga_info *src1); + int RkRgaCollorFill(rga_info *dst); + + + void RkRgaSetLogOnceFlag(int log) {mLogOnce = log;} + void RkRgaSetAlwaysLogFlag(bool log) {mLogAlways = log;} + void RkRgaLogOutRgaReq(struct rga_req rgaReg); + int RkRgaLogOutUserPara(rga_info *rgaInfo); + inline bool RkRgaIsReady() { return mSupportRga; } + +/************************************private***********************************/ +private: + bool mSupportRga; + int mLogOnce; + int mLogAlways; + void * mContext; +}; + +// --------------------------------------------------------------------------- + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRgaMacro.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRgaMacro.h new file mode 100644 index 0000000..3c1fff0 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/RockchipRgaMacro.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __ROCKCHIP_RGA_MACRO_H__ +#define __ROCKCHIP_RGA_MACRO_H__ + +/* flip source image horizontally (around the vertical axis) */ +#define HAL_TRANSFORM_FLIP_H 0x01 +/* flip source image vertically (around the horizontal axis)*/ +#define HAL_TRANSFORM_FLIP_V 0x02 +/* rotate source image 90 degrees clockwise */ +#define HAL_TRANSFORM_ROT_90 0x04 +/* rotate source image 180 degrees */ +#define HAL_TRANSFORM_ROT_180 0x03 +/* rotate source image 270 degrees clockwise */ +#define HAL_TRANSFORM_ROT_270 0x07 + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/drmrga.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/drmrga.h new file mode 100644 index 0000000..abd8b97 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/drmrga.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016 Rockchip Electronics Co.Ltd + * Authors: + * Zhiqin Wei + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _rk_drm_rga_ +#define _rk_drm_rga_ + +#include +#include +#include "rga.h" +#include "RockchipRgaMacro.h" + +/*****************************************************************************/ + +/* for compatibility */ +#define DRM_RGA_MODULE_API_VERSION HWC_MODULE_API_VERSION_0_1 +#define DRM_RGA_DEVICE_API_VERSION HWC_DEVICE_API_VERSION_0_1 +#define DRM_RGA_API_VERSION HWC_DEVICE_API_VERSION + +#define DRM_RGA_TRANSFORM_ROT_MASK 0x0000000F +#define DRM_RGA_TRANSFORM_ROT_0 0x00000000 +#define DRM_RGA_TRANSFORM_ROT_90 HAL_TRANSFORM_ROT_90 +#define DRM_RGA_TRANSFORM_ROT_180 HAL_TRANSFORM_ROT_180 +#define DRM_RGA_TRANSFORM_ROT_270 HAL_TRANSFORM_ROT_270 + +#define DRM_RGA_TRANSFORM_FLIP_MASK 0x00000003 +#define DRM_RGA_TRANSFORM_FLIP_H HAL_TRANSFORM_FLIP_H +#define DRM_RGA_TRANSFORM_FLIP_V HAL_TRANSFORM_FLIP_V + +enum { + AWIDTH = 0, + AHEIGHT, + ASTRIDE, + AFORMAT, + ASIZE, + ATYPE, +}; +/*****************************************************************************/ + + +typedef struct bo { + int fd; + void *ptr; + size_t size; + size_t offset; + size_t pitch; + unsigned handle; + }bo_t; + +/* + @value size: user not need care about.For avoid read/write out of memory + */ +typedef struct rga_rect { + int xoffset; + int yoffset; + int width; + int height; + int wstride; + int hstride; + int format; + int size; +} rga_rect_t; + +typedef struct rga_nn { + int nn_flag; + int scale_r; + int scale_g; + int scale_b; + int offset_r; + int offset_g; + int offset_b; +} rga_nn_t; + +typedef struct rga_dither { + int enable; + int mode; + int lut0_l; + int lut0_h; + int lut1_l; + int lut1_h; +} rga_dither_t; +/* + @value fd: use fd to share memory, it can be ion shard fd,and dma fd. + @value virAddr:userspace address + @value phyAddr:use phy address + @value hnd: use buffer_handle_t + */ +typedef struct rga_info { + int fd; + void *virAddr; + void *phyAddr; + unsigned hnd; + int format; + rga_rect_t rect; + unsigned int blend; + int bufferSize; + int rotation; + int color; + int testLog; + int mmuFlag; + int scale_mode; + rga_nn_t nn; + rga_dither_t dither; + int reserve[124]; +} rga_info_t; + + +typedef struct drm_rga { + rga_rect_t src; + rga_rect_t dst; +} drm_rga_t; + +/* + @fun rga_set_rect:For use to set the rects esayly + + @param rect:The rect user want to set,like setting the src rect: + drm_rga_t rects; + rga_set_rect(rects.src,0,0,1920,1080,1920,NV12); + mean to set the src rect to the value. + */ +static inline int rga_set_rect(rga_rect_t *rect, + int x, int y, int w, int h, int sw, int sh, int f) +{ + if (!rect) + return -EINVAL; + + rect->xoffset = x; + rect->yoffset = y; + rect->width = w; + rect->height = h; + rect->wstride = sw; + rect->hstride = sh; + rect->format = f; + + return 0; +} + +static inline void rga_set_rotation(rga_info_t *info, int angle) +{ + if (angle == 90) + info->rotation = HAL_TRANSFORM_ROT_90; + else if (angle == 180) + info->rotation = HAL_TRANSFORM_ROT_180; + else if (angle == 270) + info->rotation = HAL_TRANSFORM_ROT_270; +} +/*****************************************************************************/ + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/rga.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/rga.h new file mode 100644 index 0000000..7737e7a --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/rga/include/rga.h @@ -0,0 +1,752 @@ +#ifndef _RGA_DRIVER_H_ +#define _RGA_DRIVER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +#define RGA_BLIT_SYNC 0x5017 +#define RGA_BLIT_ASYNC 0x5018 +#define RGA_FLUSH 0x5019 +#define RGA_GET_RESULT 0x501a +#define RGA_GET_VERSION 0x501b + + +#define RGA_REG_CTRL_LEN 0x8 /* 8 */ +#define RGA_REG_CMD_LEN 0x1c /* 28 */ +#define RGA_CMD_BUF_SIZE 0x700 /* 16*28*4 */ + + + +#ifndef ENABLE +#define ENABLE 1 +#endif + + +#ifndef DISABLE +#define DISABLE 0 +#endif + + + +/* RGA process mode enum */ +enum +{ + bitblt_mode = 0x0, + color_palette_mode = 0x1, + color_fill_mode = 0x2, + line_point_drawing_mode = 0x3, + blur_sharp_filter_mode = 0x4, + pre_scaling_mode = 0x5, + update_palette_table_mode = 0x6, + update_patten_buff_mode = 0x7, +}; + + +enum +{ + rop_enable_mask = 0x2, + dither_enable_mask = 0x8, + fading_enable_mask = 0x10, + PD_enbale_mask = 0x20, +}; + +enum +{ + yuv2rgb_mode0 = 0x0, /* BT.601 MPEG */ + yuv2rgb_mode1 = 0x1, /* BT.601 JPEG */ + yuv2rgb_mode2 = 0x2, /* BT.709 */ +}; + + +/* RGA rotate mode */ +enum +{ + rotate_mode0 = 0x0, /* no rotate */ + rotate_mode1 = 0x1, /* rotate */ + rotate_mode2 = 0x2, /* x_mirror */ + rotate_mode3 = 0x3, /* y_mirror */ +}; + +enum +{ + color_palette_mode0 = 0x0, /* 1K */ + color_palette_mode1 = 0x1, /* 2K */ + color_palette_mode2 = 0x2, /* 4K */ + color_palette_mode3 = 0x3, /* 8K */ +}; + +enum +{ + BB_BYPASS = 0x0, /* no rotate */ + BB_ROTATE = 0x1, /* rotate */ + BB_X_MIRROR = 0x2, /* x_mirror */ + BB_Y_MIRROR = 0x3 /* y_mirror */ +}; + +enum +{ + nearby = 0x0, /* no rotate */ + bilinear = 0x1, /* rotate */ + bicubic = 0x2, /* x_mirror */ +}; + + + + + +/* +// Alpha Red Green Blue +{ 4, 32, {{32,24, 8, 0, 16, 8, 24,16 }}, GGL_RGBA }, // RK_FORMAT_RGBA_8888 +{ 4, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // RK_FORMAT_RGBX_8888 +{ 3, 24, {{ 0, 0, 8, 0, 16, 8, 24,16 }}, GGL_RGB }, // RK_FORMAT_RGB_888 +{ 4, 32, {{32,24, 24,16, 16, 8, 8, 0 }}, GGL_BGRA }, // RK_FORMAT_BGRA_8888 +{ 2, 16, {{ 0, 0, 16,11, 11, 5, 5, 0 }}, GGL_RGB }, // RK_FORMAT_RGB_565 +{ 2, 16, {{ 1, 0, 16,11, 11, 6, 6, 1 }}, GGL_RGBA }, // RK_FORMAT_RGBA_5551 +{ 2, 16, {{ 4, 0, 16,12, 12, 8, 8, 4 }}, GGL_RGBA }, // RK_FORMAT_RGBA_4444 +{ 3, 24, {{ 0, 0, 24,16, 16, 8, 8, 0 }}, GGL_BGR }, // RK_FORMAT_BGB_888 + +*/ + +typedef enum _Rga_SURF_FORMAT +{ + RK_FORMAT_RGBA_8888 = 0x0, + RK_FORMAT_RGBX_8888 = 0x1, + RK_FORMAT_RGB_888 = 0x2, + RK_FORMAT_BGRA_8888 = 0x3, + RK_FORMAT_BGRX_8888 = 0x4, + RK_FORMAT_BGR_888 = 0x5, + RK_FORMAT_RGB_565 = 0x6, + RK_FORMAT_RGBA_5551 = 0x7, + RK_FORMAT_RGBA_4444 = 0x8, + RK_FORMAT_BGR_565 = 0x9, + RK_FORMAT_BGRA_5551 = 0xa, + RK_FORMAT_BGRA_4444 = 0xb, + + RK_FORMAT_Y4 = 0xe, + RK_FORMAT_YCbCr_400 = 0xf, + RK_FORMAT_YCbCr_422_SP = 0x10, + RK_FORMAT_YCbCr_422_P = 0x11, + RK_FORMAT_YCbCr_420_SP = 0x12, + RK_FORMAT_YCbCr_420_P = 0x13, + RK_FORMAT_YCrCb_422_SP = 0x14, + RK_FORMAT_YCrCb_422_P = 0x15, + RK_FORMAT_YCrCb_420_SP = 0x16, + RK_FORMAT_YCrCb_420_P = 0x17, + + RK_FORMAT_YVYU_422 = 0x18, + RK_FORMAT_YVYU_420 = 0x19, + RK_FORMAT_VYUY_422 = 0x1a, + RK_FORMAT_VYUY_420 = 0x1b, + RK_FORMAT_YUYV_422 = 0x1c, + RK_FORMAT_YUYV_420 = 0x1d, + RK_FORMAT_UYVY_422 = 0x1e, + RK_FORMAT_UYVY_420 = 0x1f, + + RK_FORMAT_YCbCr_422_10b_SP = 0x20, + RK_FORMAT_YCbCr_420_10b_SP = 0x21, + RK_FORMAT_YCrCb_422_10b_SP = 0x22, + RK_FORMAT_YCrCb_420_10b_SP = 0x23, + + RK_FORMAT_UNKNOWN = 0x100, +} RgaSURF_FORMAT; + +typedef struct rga_img_info_t +{ +#if defined(__arm64__) || defined(__aarch64__) + unsigned long yrgb_addr; /* yrgb mem addr */ + unsigned long uv_addr; /* cb/cr mem addr */ + unsigned long v_addr; /* cr mem addr */ +#else + unsigned int yrgb_addr; /* yrgb mem addr */ + unsigned int uv_addr; /* cb/cr mem addr */ + unsigned int v_addr; /* cr mem addr */ +#endif + unsigned int format; //definition by RK_FORMAT + unsigned short act_w; + unsigned short act_h; + unsigned short x_offset; + unsigned short y_offset; + + unsigned short vir_w; + unsigned short vir_h; + + unsigned short endian_mode; //for BPP + unsigned short alpha_swap; +} +rga_img_info_t; + + +typedef struct mdp_img_act +{ + unsigned short w; // width + unsigned short h; // height + short x_off; // x offset for the vir + short y_off; // y offset for the vir +} +mdp_img_act; + + + +typedef struct RANGE +{ + unsigned short min; + unsigned short max; +} +RANGE; + +typedef struct POINT_t +{ + unsigned short x; + unsigned short y; +} +POINT_t; + +typedef struct RECT_t +{ + unsigned short xmin; + unsigned short xmax; // width - 1 + unsigned short ymin; + unsigned short ymax; // height - 1 +} RECT_t; + +typedef struct RGT_t +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char res; +}RGB_t; + + +typedef struct MMU +{ + unsigned char mmu_en; +#if defined(__arm64__) || defined(__aarch64__) + unsigned long base_addr; +#else + unsigned int base_addr; +#endif + unsigned int mmu_flag; /* [0] mmu enable [1] src_flush [2] dst_flush [3] CMD_flush [4~5] page size*/ +} MMU; + + + + +typedef struct COLOR_FILL +{ + short gr_x_a; + short gr_y_a; + short gr_x_b; + short gr_y_b; + short gr_x_g; + short gr_y_g; + short gr_x_r; + short gr_y_r; + + //u8 cp_gr_saturation; +} +COLOR_FILL; + +typedef struct FADING +{ + unsigned char b; + unsigned char g; + unsigned char r; + unsigned char res; +} +FADING; + + +typedef struct line_draw_t +{ + POINT_t start_point; /* LineDraw_start_point */ + POINT_t end_point; /* LineDraw_end_point */ + unsigned int color; /* LineDraw_color */ + unsigned int flag; /* (enum) LineDrawing mode sel */ + unsigned int line_width; /* range 1~16 */ +} +line_draw_t; + + + + struct rga_req { + unsigned char render_mode; /* (enum) process mode sel */ + + rga_img_info_t src; /* src image info */ + rga_img_info_t dst; /* dst image info */ + rga_img_info_t pat; /* patten image info */ + +#if defined(__arm64__) || defined(__aarch64__) + unsigned long rop_mask_addr; /* rop4 mask addr */ + unsigned long LUT_addr; /* LUT addr */ +#else + unsigned int rop_mask_addr; /* rop4 mask addr */ + unsigned int LUT_addr; /* LUT addr */ +#endif + + RECT_t clip; /* dst clip window default value is dst_vir */ + /* value from [0, w-1] / [0, h-1]*/ + + int sina; /* dst angle default value 0 16.16 scan from table */ + int cosa; /* dst angle default value 0 16.16 scan from table */ + + unsigned short alpha_rop_flag; /* alpha rop process flag */ + /* ([0] = 1 alpha_rop_enable) */ + /* ([1] = 1 rop enable) */ + /* ([2] = 1 fading_enable) */ + /* ([3] = 1 PD_enable) */ + /* ([4] = 1 alpha cal_mode_sel) */ + /* ([5] = 1 dither_enable) */ + /* ([6] = 1 gradient fill mode sel) */ + /* ([7] = 1 AA_enable) */ + + unsigned char scale_mode; /* 0 nearst / 1 bilnear / 2 bicubic */ + + unsigned int color_key_max; /* color key max */ + unsigned int color_key_min; /* color key min */ + + unsigned int fg_color; /* foreground color */ + unsigned int bg_color; /* background color */ + + COLOR_FILL gr_color; /* color fill use gradient */ + + line_draw_t line_draw_info; + + FADING fading; + + unsigned char PD_mode; /* porter duff alpha mode sel */ + + unsigned char alpha_global_value; /* global alpha value */ + + unsigned short rop_code; /* rop2/3/4 code scan from rop code table*/ + + unsigned char bsfilter_flag; /* [2] 0 blur 1 sharp / [1:0] filter_type*/ + + unsigned char palette_mode; /* (enum) color palatte 0/1bpp, 1/2bpp 2/4bpp 3/8bpp*/ + + unsigned char yuv2rgb_mode; /* (enum) BT.601 MPEG / BT.601 JPEG / BT.709 */ + + unsigned char endian_mode; /* 0/big endian 1/little endian*/ + + unsigned char rotate_mode; /* (enum) rotate mode */ + /* 0x0, no rotate */ + /* 0x1, rotate */ + /* 0x2, x_mirror */ + /* 0x3, y_mirror */ + + unsigned char color_fill_mode; /* 0 solid color / 1 patten color */ + + MMU mmu_info; /* mmu information */ + + unsigned char alpha_rop_mode; /* ([0~1] alpha mode) */ + /* ([2~3] rop mode) */ + /* ([4] zero mode en) */ + /* ([5] dst alpha mode) */ + + unsigned char src_trans_mode; + + unsigned char dither_mode; + + unsigned char CMD_fin_int_enable; + + /* completion is reported through a callback */ + void (*complete)(int retval); +}; + +#if 0 +typedef struct TILE_INFO +{ + int64_t matrix[4]; + + uint16_t tile_x_num; /* x axis tile num / tile size is 8x8 pixel */ + uint16_t tile_y_num; /* y axis tile num */ + + int16_t dst_x_tmp; /* dst pos x = (xstart - xoff) default value 0 */ + int16_t dst_y_tmp; /* dst pos y = (ystart - yoff) default value 0 */ + + uint16_t tile_w; + uint16_t tile_h; + int16_t tile_start_x_coor; + int16_t tile_start_y_coor; + int32_t tile_xoff; + int32_t tile_yoff; + + int32_t tile_temp_xstart; + int32_t tile_temp_ystart; + + /* src tile incr */ + int32_t x_dx; + int32_t x_dy; + int32_t y_dx; + int32_t y_dy; + + mdp_img_act dst_ctrl; + +} +TILE_INFO; +#endif + +#if 0 + +#define RGA_BASE 0x10114000 + +//General Registers +#define RGA_SYS_CTRL 0x000 +#define RGA_CMD_CTRL 0x004 +#define RGA_CMD_ADDR 0x008 +#define RGA_STATUS 0x00c +#define RGA_INT 0x010 +#define RGA_AXI_ID 0x014 +#define RGA_MMU_STA_CTRL 0x018 +#define RGA_MMU_STA 0x01c + +//Command code start +#define RGA_MODE_CTRL 0x100 + +//Source Image Registers +#define RGA_SRC_Y_MST 0x104 +#define RGA_SRC_CB_MST 0x108 +#define RGA_MASK_READ_MST 0x108 //repeat +#define RGA_SRC_CR_MST 0x10c +#define RGA_SRC_VIR_INFO 0x110 +#define RGA_SRC_ACT_INFO 0x114 +#define RGA_SRC_X_PARA 0x118 +#define RGA_SRC_Y_PARA 0x11c +#define RGA_SRC_TILE_XINFO 0x120 +#define RGA_SRC_TILE_YINFO 0x124 +#define RGA_SRC_TILE_H_INCR 0x128 +#define RGA_SRC_TILE_V_INCR 0x12c +#define RGA_SRC_TILE_OFFSETX 0x130 +#define RGA_SRC_TILE_OFFSETY 0x134 +#define RGA_SRC_BG_COLOR 0x138 +#define RGA_SRC_FG_COLOR 0x13c +#define RGA_LINE_DRAWING_COLOR 0x13c //repeat +#define RGA_SRC_TR_COLOR0 0x140 +#define RGA_CP_GR_A 0x140 //repeat +#define RGA_SRC_TR_COLOR1 0x144 +#define RGA_CP_GR_B 0x144 //repeat + +#define RGA_LINE_DRAW 0x148 +#define RGA_PAT_START_POINT 0x148 //repeat + +//Destination Image Registers +#define RGA_DST_MST 0x14c +#define RGA_LUT_MST 0x14c //repeat +#define RGA_PAT_MST 0x14c //repeat +#define RGA_LINE_DRAWING_MST 0x14c //repeat + +#define RGA_DST_VIR_INFO 0x150 + +#define RGA_DST_CTR_INFO 0x154 +#define RGA_LINE_DRAW_XY_INFO 0x154 //repeat + +//Alpha/ROP Registers +#define RGA_ALPHA_CON 0x158 + +#define RGA_PAT_CON 0x15c +#define RGA_DST_VIR_WIDTH_PIX 0x15c //repeat + +#define RGA_ROP_CON0 0x160 +#define RGA_CP_GR_G 0x160 //repeat +#define RGA_PRESCL_CB_MST 0x160 //repeat + +#define RGA_ROP_CON1 0x164 +#define RGA_CP_GR_R 0x164 //repeat +#define RGA_PRESCL_CR_MST 0x164 //repeat + +//MMU Register +#define RGA_FADING_CON 0x168 +#define RGA_MMU_CTRL 0x168 //repeat + +#define RGA_MMU_TBL 0x16c //repeat + + +#define RGA_BLIT_COMPLETE_EVENT 1 + +#endif + +int +RGA_set_src_act_info( + struct rga_req *req, + unsigned int width, /* act width */ + unsigned int height, /* act height */ + unsigned int x_off, /* x_off */ + unsigned int y_off /* y_off */ + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_src_vir_info( + struct rga_req *req, + unsigned long yrgb_addr, /* yrgb_addr */ + unsigned long uv_addr, /* uv_addr */ + unsigned long v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + unsigned char format, /* format */ + unsigned char a_swap_en /* only for 32bit RGB888 format */ + ); +#else +int +RGA_set_src_vir_info( + struct rga_req *req, + unsigned int yrgb_addr, /* yrgb_addr */ + unsigned int uv_addr, /* uv_addr */ + unsigned int v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + unsigned char format, /* format */ + unsigned char a_swap_en /* only for 32bit RGB888 format */ + ); +#endif + +int +RGA_set_dst_act_info( + struct rga_req *req, + unsigned int width, /* act width */ + unsigned int height, /* act height */ + unsigned int x_off, /* x_off */ + unsigned int y_off /* y_off */ + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_dst_vir_info( + struct rga_req *msg, + unsigned long yrgb_addr, /* yrgb_addr */ + unsigned long uv_addr, /* uv_addr */ + unsigned long v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + RECT_t *clip, /* clip window */ + unsigned char format, /* format */ + unsigned char a_swap_en + ); +#else +int +RGA_set_dst_vir_info( + struct rga_req *msg, + unsigned int yrgb_addr, /* yrgb_addr */ + unsigned int uv_addr, /* uv_addr */ + unsigned int v_addr, /* v_addr */ + unsigned int vir_w, /* vir width */ + unsigned int vir_h, /* vir height */ + RECT_t *clip, /* clip window */ + unsigned char format, /* format */ + unsigned char a_swap_en + ); +#endif + +int +RGA_set_pat_info( + struct rga_req *msg, + unsigned int width, + unsigned int height, + unsigned int x_off, + unsigned int y_off, + unsigned int pat_format + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_rop_mask_info( + struct rga_req *msg, + unsigned long rop_mask_addr, + unsigned int rop_mask_endian_mode + ); +#else +int +RGA_set_rop_mask_info( + struct rga_req *msg, + unsigned int rop_mask_addr, + unsigned int rop_mask_endian_mode + ); +#endif + +int RGA_set_alpha_en_info( + struct rga_req *msg, + unsigned int alpha_cal_mode, /* 0:alpha' = alpha + (alpha>>7) | alpha' = alpha */ + unsigned int alpha_mode, /* 0 global alpha / 1 per pixel alpha / 2 mix mode */ + unsigned int global_a_value, + unsigned int PD_en, /* porter duff alpha mode en */ + unsigned int PD_mode, + unsigned int dst_alpha_en ); /* use dst alpha */ + +int +RGA_set_rop_en_info( + struct rga_req *msg, + unsigned int ROP_mode, + unsigned int ROP_code, + unsigned int color_mode, + unsigned int solid_color + ); + + +int +RGA_set_fading_en_info( + struct rga_req *msg, + unsigned char r, + unsigned char g, + unsigned char b + ); + +int +RGA_set_src_trans_mode_info( + struct rga_req *msg, + unsigned char trans_mode, + unsigned char a_en, + unsigned char b_en, + unsigned char g_en, + unsigned char r_en, + unsigned char color_key_min, + unsigned char color_key_max, + unsigned char zero_mode_en + ); + + +int +RGA_set_bitblt_mode( + struct rga_req *msg, + unsigned char scale_mode, // 0/near 1/bilnear 2/bicubic + unsigned char rotate_mode, // 0/copy 1/rotate_scale 2/x_mirror 3/y_mirror + unsigned int angle, // rotate angle + unsigned int dither_en, // dither en flag + unsigned int AA_en, // AA flag + unsigned int yuv2rgb_mode + ); + + +int +RGA_set_color_palette_mode( + struct rga_req *msg, + unsigned char palette_mode, /* 1bpp/2bpp/4bpp/8bpp */ + unsigned char endian_mode, /* src endian mode sel */ + unsigned int bpp1_0_color, /* BPP1 = 0 */ + unsigned int bpp1_1_color /* BPP1 = 1 */ + ); + + +int +RGA_set_color_fill_mode( + struct rga_req *msg, + COLOR_FILL *gr_color, /* gradient color part */ + unsigned char gr_satur_mode, /* saturation mode */ + unsigned char cf_mode, /* patten fill or solid fill */ + unsigned int color, /* solid color */ + unsigned short pat_width, /* pattern width */ + unsigned short pat_height, /* pattern height */ + unsigned char pat_x_off, /* pattern x offset */ + unsigned char pat_y_off, /* pattern y offset */ + unsigned char aa_en /* alpha en */ + ); + + +int +RGA_set_line_point_drawing_mode( + struct rga_req *msg, + POINT_t sp, /* start point */ + POINT_t ep, /* end point */ + unsigned int color, /* line point drawing color */ + unsigned int line_width, /* line width */ + unsigned char AA_en, /* AA en */ + unsigned char last_point_en /* last point en */ + ); + + +int +RGA_set_blur_sharp_filter_mode( + struct rga_req *msg, + unsigned char filter_mode, /* blur/sharpness */ + unsigned char filter_type, /* filter intensity */ + unsigned char dither_en /* dither_en flag */ + ); + +int +RGA_set_pre_scaling_mode( + struct rga_req *msg, + unsigned char dither_en + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_update_palette_table_mode( + struct rga_req *msg, + unsigned long LUT_addr, /* LUT table addr */ + unsigned int palette_mode /* 1bpp/2bpp/4bpp/8bpp */ + ); +#else +int +RGA_update_palette_table_mode( + struct rga_req *msg, + unsigned int LUT_addr, /* LUT table addr */ + unsigned int palette_mode /* 1bpp/2bpp/4bpp/8bpp */ + ); +#endif + +int +RGA_set_update_patten_buff_mode( + struct rga_req *msg, + unsigned int pat_addr, /* patten addr */ + unsigned int w, /* patten width */ + unsigned int h, /* patten height */ + unsigned int format /* patten format */ + ); + +#if defined(__arm64__) || defined(__aarch64__) +int +RGA_set_mmu_info( + struct rga_req *msg, + unsigned char mmu_en, + unsigned char src_flush, + unsigned char dst_flush, + unsigned char cmd_flush, + unsigned long base_addr, + unsigned char page_size + ); +#else +int +RGA_set_mmu_info( + struct rga_req *msg, + unsigned char mmu_en, + unsigned char src_flush, + unsigned char dst_flush, + unsigned char cmd_flush, + unsigned int base_addr, + unsigned char page_size + ); +#endif + +void rga_set_fds_offsets( + struct rga_req *rga_request, + unsigned short src_fd, + unsigned short dst_fd, + unsigned int src_offset, + unsigned int dst_offset); + +int +RGA_set_src_fence_flag( + struct rga_req *msg, + int acq_fence, + int src_flag +); + + +int +RGA_set_dst_fence_flag( + struct rga_req *msg, + int dst_flag +); + +int +RGA_get_dst_fence( + struct rga_req *msg +); +#ifdef __cplusplus +} +#endif + +#endif /*_RK29_IPP_DRIVER_H_*/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image.h new file mode 100644 index 0000000..196dfd5 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image.h @@ -0,0 +1,7559 @@ +/* stb_image - v2.23 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt JR Smith github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image_resize.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image_resize.h new file mode 100644 index 0000000..4f6ad35 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image_resize.h @@ -0,0 +1,2630 @@ +/* stb_image_resize - v0.96 - public domain image resizing + by Jorge L Rodriguez (@VinoBS) - 2014 + http://github.com/nothings/stb + + Written with emphasis on usability, portability, and efficiency. (No + SIMD or threads, so it be easily outperformed by libs that use those.) + Only scaling and translation is supported, no rotations or shears. + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + QUICKSTART + stbir_resize_uint8( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, num_channels) + stbir_resize_float(...) + stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0) + stbir_resize_uint8_srgb_edgemode( + input_pixels , in_w , in_h , 0, + output_pixels, out_w, out_h, 0, + num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) + // WRAP/REFLECT/ZERO + + FULL API + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + SRGB & FLOATING POINT REPRESENTATION + The sRGB functions presume IEEE floating point. If you do not have + IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use + a slower implementation. + + MEMORY ALLOCATION + The resize functions here perform a single memory allocation using + malloc. To control the memory allocation, before the #include that + triggers the implementation, do: + + #define STBIR_MALLOC(size,context) ... + #define STBIR_FREE(ptr,context) ... + + Each resize function makes exactly one call to malloc/free, so to use + temp memory, store the temp memory in the context and return that. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + OPTIMIZATION + Define STBIR_SATURATE_INT to compute clamp values in-range using + integer operations instead of float operations. This may be faster + on some platforms. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters + to use, you can change the compile-time defaults with + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are used. For a list of + supported filters see the stbir_filter enum. To add a new filter, + write a filter function and add it to stbir__filter_info_table. + + PROGRESS + For interactive use with slow resize operations, you can install + a progress-report callback: + + #define STBIR_PROGRESS_REPORT(val) some_func(val) + + The parameter val is a float which goes from 0 to 1 as progress is made. + + For example: + + static void my_progress_report(float progress); + #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) + + #define STB_IMAGE_RESIZE_IMPLEMENTATION + #include "stb_image_resize.h" + + static void my_progress_report(float progress) + { + printf("Progress: %f%%\n", progress*100); + } + + MAX CHANNELS + If your image has more than 64 channels, define STBIR_MAX_CHANNELS + to the max you'll have. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how + the alpha channel of an image is processed. The important things + to know about this: + + 1. The best mathematically-behaved version of alpha to use is + called "premultiplied alpha", in which the other color channels + have had the alpha value multiplied in. If you use premultiplied + alpha, linear filtering (such as image resampling done by this + library, or performed in texture units on GPUs) does the "right + thing". While premultiplied alpha is standard in the movie CGI + industry, it is still uncommon in the videogame/real-time world. + + If you linearly filter non-premultiplied alpha, strange effects + occur. (For example, the 50/50 average of 99% transparent bright green + and 1% transparent black produces 50% transparent dark green when + non-premultiplied, whereas premultiplied it produces 50% + transparent near-black. The former introduces green energy + that doesn't exist in the source image.) + + 2. Artists should not edit premultiplied-alpha images; artists + want non-premultiplied alpha images. Thus, art tools generally output + non-premultiplied alpha images. + + 3. You will get best results in most cases by converting images + to premultiplied alpha before processing them mathematically. + + 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the + resizer does not do anything special for the alpha channel; + it is resampled identically to other channels. This produces + the correct results for premultiplied-alpha images, but produces + less-than-ideal results for non-premultiplied-alpha images. + + 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, + then the resizer weights the contribution of input pixels + based on their alpha values, or, equivalently, it multiplies + the alpha value into the color channels, resamples, then divides + by the resultant alpha value. Input pixels which have alpha=0 do + not contribute at all to output pixels unless _all_ of the input + pixels affecting that output pixel have alpha=0, in which case + the result for that pixel is the same as it would be without + STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for + input images in integer formats. For input images in float format, + input pixels with alpha=0 have no effect, and output pixels + which have alpha=0 will be 0 in all channels. (For float images, + you can manually achieve the same result by adding a tiny epsilon + value to the alpha channel of every image, and then subtracting + or clamping it at the end.) + + 6. You can suppress the behavior described in #5 and make + all-0-alpha pixels have 0 in all channels by #defining + STBIR_NO_ALPHA_EPSILON. + + 7. You can separately control whether the alpha channel is + interpreted as linear or affected by the colorspace. By default + it is linear; you almost never want to apply the colorspace. + (For example, graphics hardware does not apply sRGB conversion + to the alpha channel.) + + CONTRIBUTORS + Jorge L Rodriguez: Implementation + Sean Barrett: API design, optimizations + Aras Pranckevicius: bugfix + Nathan Reed: warning fixes + + REVISIONS + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. + + TODO + Don't decode all of the image data when only processing a partial tile + Don't use full-width decode buffers when only processing a partial tile + When processing wide images, break processing into tiles so data fits in L1 cache + Installable filters? + Resize that respects alpha test coverage + (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: + https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) +*/ + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H + +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) +// * input_w is input image width (x-axis), input_h is input image height (y-axis) +// * stride is the offset between successive rows of image data in memory, in bytes. you can +// specify 0 to mean packed continuously in memory +// * alpha channel is treated identically to other channels. +// * colorspace is linear or sRGB as specified by function name +// * returned result is 1 for success or 0 in case of an error. +// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. +// * Memory required grows approximately linearly with input and output size, but with +// discontinuities at input_w == output_w and input_h == output_h. +// * These functions use a "default" resampling filter defined at compile time. To change the filter, +// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE +// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels); + + +// The following functions interpret image data as gamma-corrected sRGB. +// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, +// or otherwise provide the index of the alpha channel. Flags value +// of 0 will probably do the right thing if you're not sure what +// the flags mean. + +#define STBIR_ALPHA_CHANNEL_NONE -1 + +// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will +// use alpha-weighted resampling (effectively premultiplying, resampling, +// then unpremultiplying). +#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +// The specified alpha channel should be handled as gamma-corrected value even +// when doing sRGB operations. +#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags); + + +typedef enum +{ + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, +} stbir_edge; + +// This function adds the ability to specify how requests to sample off the edge of the image are handled. +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode); + +////////////////////////////////////////////////////////////////////////////// +// +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Alpha-channel can be processed separately +// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE +// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) +// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) +// * Filter can be selected explicitly +// * uint16 image type +// * sRGB colorspace available for all types +// * context parameter for passing to STBIR_MALLOC + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 +} stbir_filter; + +typedef enum +{ + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, + + STBIR_MAX_COLORSPACES, +} stbir_colorspace; + +// The following functions are all identical except for the type of the image data + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context); + + + +////////////////////////////////////////////////////////////////////////////// +// +// Full-complexity API +// +// This extends the medium API as follows: +// +// * uint32 image type +// * not typesafe +// * separate filter types for each axis +// * separate edge modes for each axis +// * can specify scale explicitly for subpixel correctness +// * can specify image source tile using texture coordinates + +typedef enum +{ + STBIR_TYPE_UINT8 , + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT , + + STBIR_MAX_TYPES +} stbir_datatype; + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset); + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1); +// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H + + + + + +#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +// For memset +#include + +#include + +#ifndef STBIR_MALLOC +#include +// use comma operator to evaluate c, to avoid "unused parameter" warnings +#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) +#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) +#endif + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbir__inline inline +#else +#define stbir__inline +#endif +#else +#define stbir__inline __forceinline +#endif + + +// should produce compiler error if size is wrong +typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBIR__NOTUSED(v) (void)(v) +#else +#define STBIR__NOTUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + +#ifndef STBIR_PROGRESS_REPORT +#define STBIR_PROGRESS_REPORT(float_0_to_1) +#endif + +#ifndef STBIR_MAX_CHANNELS +#define STBIR_MAX_CHANNELS 64 +#endif + +#if STBIR_MAX_CHANNELS > 65536 +#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +// because we store the indices in 16-bit variables +#endif + +// This value is added to alpha just before premultiplication to avoid +// zeroing out color values. It is equivalent to 2^-80. If you don't want +// that behavior (it may interfere if you have floating point images with +// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to +// disable it. +#ifndef STBIR_ALPHA_EPSILON +#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +#endif + + + +#ifdef _MSC_VER +#define STBIR__UNUSED_PARAM(v) (void)(v) +#else +#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) +#endif + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT +}; + +// Kernel function centered at 0 +typedef float (stbir__kernel_fn)(float x, float scale); +typedef float (stbir__support_fn)(float scale); + +typedef struct +{ + stbir__kernel_fn* kernel; + stbir__support_fn* support; +} stbir__filter_info; + +// When upsampling, the contributors are which source pixels contribute. +// When downsampling, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + const void* input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void* output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors* horizontal_contributors; + float* horizontal_coefficients; + + stbir__contributors* vertical_contributors; + float* vertical_coefficients; + + int decode_buffer_pixels; + float* decode_buffer; + + float* horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + float* ring_buffer; + + float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; +} stbir__info; + + +static const float stbir__max_uint8_as_float = 255.0f; +static const float stbir__max_uint16_as_float = 65535.0f; +static const double stbir__max_uint32_as_float = 4294967295.0; + + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline float stbir__saturate(float x) +{ + if (x < 0) + return 0; + + if (x > 1) + return 1; + + return x; +} + +#ifdef STBIR_SATURATE_INT +static stbir__inline stbir_uint8 stbir__saturate8(int x) +{ + if ((unsigned int) x <= 255) + return x; + + if (x < 0) + return 0; + + return 255; +} + +static stbir__inline stbir_uint16 stbir__saturate16(int x) +{ + if ((unsigned int) x <= 65535) + return x; + + if (x < 0) + return 0; + + return 65535; +} +#endif + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +static float stbir__srgb_to_linear(float f) +{ + if (f <= 0.04045f) + return f / 12.92f; + else + return (float)pow((f + 0.055f) / 1.055f, 2.4f); +} + +static float stbir__linear_to_srgb(float f) +{ + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; +} + +#ifndef STBIR_NON_IEEE_FLOAT +// From https://gist.github.com/rygorous/2203834 + +typedef union +{ + stbir_uint32 u; + float f; +} stbir__FP32; + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#else +// sRGB transition values, scaled by 1<<28 +static int stbir__srgb_offset_to_linear_scaled[256] = +{ + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, + 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, + 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, + 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, + 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, + 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, + 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, + 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, + 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, + 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, + 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, + 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, +}; + +static stbir_uint8 stbir__linear_to_srgb_uchar(float f) +{ + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; + + return (stbir_uint8) v; +} +#endif + +static float stbir__filter_trapezoid(float x, float scale) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + + x = (float)fabs(x); + + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale) +{ + STBIR_ASSERT(scale <= 1); + return 0.5f + scale / 2; +} + +static float stbir__filter_triangle(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x <= 1.0f) + return 1 - x; + else + return 0; +} + +static float stbir__filter_cubic(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (4 + x*x*(3*x - 6))/6; + else if (x < 2.0f) + return (8 + x*(-12 + x*(6 - x)))/6; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return 1 - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2 - x*(4 + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s) +{ + STBIR__UNUSED_PARAM(s); + + x = (float)fabs(x); + + if (x < 1.0f) + return (16 + x*x*(21 * x - 36))/18; + else if (x < 2.0f) + return (32 + x*(-60 + x*(36 - 7*x)))/18; + + return (0.0f); +} + +static float stbir__support_zero(float s) +{ + STBIR__UNUSED_PARAM(s); + return 0; +} + +static float stbir__support_one(float s) +{ + STBIR__UNUSED_PARAM(s); + return 1; +} + +static float stbir__support_two(float s) +{ + STBIR__UNUSED_PARAM(s); + return 2; +} + +static stbir__filter_info stbir__filter_info_table[] = { + { NULL, stbir__support_zero }, + { stbir__filter_trapezoid, stbir__support_trapezoid }, + { stbir__filter_triangle, stbir__support_one }, + { stbir__filter_cubic, stbir__support_two }, + { stbir__filter_catmullrom, stbir__support_two }, + { stbir__filter_mitchell, stbir__support_two }, +}; + +stbir__inline static int stbir__use_upsampling(float ratio) +{ + return ratio > 1; +} + +stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->horizontal_scale); +} + +stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +{ + return stbir__use_upsampling(stbir_info->vertical_scale); +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter +static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) +{ + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); +} + +// This is how much to expand buffers to account for filters seeking outside +// the image boundaries. +static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) +{ + return stbir__get_filter_pixel_width(filter, scale) / 2; +} + +static int stbir__get_coefficient_width(stbir_filter filter, float scale) +{ + if (stbir__use_upsampling(scale)) + return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); +} + +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +{ + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); +} + +static int stbir__get_total_horizontal_coefficients(stbir__info* info) +{ + return info->horizontal_num_contributors + * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); +} + +static int stbir__get_total_vertical_coefficients(stbir__info* info) +{ + return info->vertical_num_contributors + * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); +} + +static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +{ + return &contributors[n]; +} + +// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, +// if you change it here change it there too. +static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +{ + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width*n + c]; +} + +static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) +{ + switch (edge) + { + case STBIR_EDGE_ZERO: + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + + case STBIR_EDGE_CLAMP: + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED + + case STBIR_EDGE_REFLECT: + { + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED + } + + case STBIR_EDGE_WRAP: + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } + // NOTREACHED + + default: + STBIR_ASSERT(!"Unimplemented edge type"); + return 0; + } +} + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); +} + +// What input pixels contribute to this output pixel? +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +{ + float out_pixel_center = (float)n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); +} + +// What output pixels does this input pixel contribute to? +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +{ + float in_pixel_center = (float)n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); +} + +static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + float total_filter = 0; + float filter_scale; + + STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; + } + + total_filter += coefficient_group[i]; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + + STBIR_ASSERT(total_filter > 0.9); + STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; + + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; + + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +{ + int i; + + STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; + + STBIR_ASSERT(contributor->n1 >= contributor->n0); + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } + + STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } +} + +static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) +{ + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; + + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } + + STBIR_ASSERT(total > 0.9f); + STBIR_ASSERT(total < 1.1f); + + scale = 1 / total; + + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; + else if (i < contributors[j].n0) + break; + } + } + + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; + + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; + + contributors[j].n0 += skip; + + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } + + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); + + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; + + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); + } + + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); +} + +// Each scan line uses the same kernel values so we should calculate the kernel +// values once and then we can use them for every scan line. +static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +{ + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; + + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + + stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) + { + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + + stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } + + stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); + } +} + +static float* stbir__get_decode_buffer(stbir__info* stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +#define STBIR__DECODE(type, colorspace) ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + +static void stbir__decode_scanline(stbir__info* stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + size_t input_stride_bytes = stbir_info->input_stride_bytes; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + } + + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + { + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +#ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +#endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } + } + } + + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x*channels + c] = 0; + } + } +} + +static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} + +static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + stbir_info->ring_buffer_last_scanline = n; + + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } + + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + + return ring_buffer; +} + + +static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR_ASSERT(n1 >= n0); + STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (k = n0; k <= n1; k++) + { + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) +{ + int x, k; + int input_w = stbir_info->input_w; + int channels = stbir_info->channels; + float* decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; + float* horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; + + STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); + + switch (channels) { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; + + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; + + case 3: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; + + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; + + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; + + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } +} + +static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +{ + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); + + memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); + + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. +} + +// Get the specified scan line from the ring buffer. +static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) +{ + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); +} + + +static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +{ + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; + + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). + } + } + + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x = 0, num_nonalpha = 0; x < channels; ++x) + { + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + { + nonalpha[num_nonalpha++] = (stbir_uint16)x; + } + } + + #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) + #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) + + #ifdef STBIR__SATURATE_INT + #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) + #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) + #else + #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) + #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) + #endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } + + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float*)output_buffer)[index] = encode_buffer[index]; + } + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x=0; x < num_pixels; ++x) + { + int pixel_index = x*channels; + + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } + + if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; + } + break; + + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + void* output_data = stbir_info->output_data; + float* encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + int n0,n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; + float* vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + float* horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + int n0,n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); + + switch (channels) { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; + + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; + } + break; + } + } +} + +static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; + + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + + STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y); + + STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); + } +} + +static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +{ + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void* output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); + + float* ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } +} + +static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +{ + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; + + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; + + stbir__empty_ring_buffer(stbir_info, out_first_scanline); + + stbir__decode_and_resample_downsample(stbir_info, y); + + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y); + } + + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); +} + +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +{ + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; +} + +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +{ + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; + + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + + info->horizontal_shift = s0 * info->output_w / (s1 - s0); + info->vertical_shift = t0 * info->output_h / (t1 - t0); + } +} + +static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) +{ + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; +} + +static stbir_uint32 stbir__calculate_memory(stbir__info *info) +{ + int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + info->ring_buffer_num_entries = filter_height + 1; + + info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + + info->ring_buffer_size + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, + const void* input_data, int input_stride_in_bytes, + void* output_data, int output_stride_in_bytes, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, + void* tempmem, size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; + +#ifdef STBIR_DEBUG_OVERWRITE_TEST +#define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); +#endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; + + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; + + if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + } + + if (alpha_channel >= info->channels) + return 0; + + STBIR_ASSERT(tempmem); + + if (!tempmem) + return 0; + + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); + + if (tempmem_size_in_bytes < memory_required) + return 0; + + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); + info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); + } + +#undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); + stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +#ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); +#endif + + return 1; +} + + +static int stbir__resize_arbitrary( + void *alloc_context, + const void* input_data, int input_w, int input_h, int input_stride_in_bytes, + void* output_data, int output_w, int output_h, int output_stride_in_bytes, + float s0, float t0, float s1, float t1, float *transform, + int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void* extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0,t0,s1,t1,transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; + + result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, + output_data, output_stride_in_bytes, + alpha_channel, flags, type, + edge_horizontal, edge_vertical, + colorspace, extra_memory, memory_required); + + STBIR_FREE(extra_memory, alloc_context); + + return result; +} + +STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); +} + +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode) +{ + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, + edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); +} + +STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, + void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, + edge_wrap_mode, edge_wrap_mode, space); +} + + +STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float x_scale, float y_scale, + float x_offset, float y_offset) +{ + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, + float s0, float t0, float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image_write.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image_write.h new file mode 100644 index 0000000..c117344 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/3rdparty/stb/stb_image_write.h @@ -0,0 +1,1619 @@ +/* stb_image_write - v1.13 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBI_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a; arr[1] = b; arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_WANT_SECURE_LIB__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = snprintf(buffer, 128, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + float r, g, b; + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/CMakeLists.txt new file mode 100644 index 0000000..b3b9339 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_batch_inference_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +# utils +include_directories(${CMAKE_SOURCE_DIR}/src/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_batch_inference_demo + src/main.cc +) + +target_link_libraries(rknn_batch_inference_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_batch_inference_demo) +install(TARGETS rknn_batch_inference_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/README.md new file mode 100644 index 0000000..4a4f668 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_batch_inference_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_batch_inference_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_batch_inference_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/build.sh new file mode 100644 index 0000000..70781f8 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_batch_inference_demo/ +cp run_rv1109_rv1126.sh install/rknn_batch_inference_demo/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/dog_224x224.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/dog_224x224.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rk180x/mobilenet_v1_rk1808_batch_4.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rk180x/mobilenet_v1_rk1808_batch_4.rknn new file mode 100644 index 0000000..a0f9b4b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rk180x/mobilenet_v1_rk1808_batch_4.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rv1109_rv1126/mobilenet_v1_rv1109_rv1126_batch_4.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rv1109_rv1126/mobilenet_v1_rv1109_rv1126_batch_4.rknn new file mode 100644 index 0000000..8103d58 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/model/rv1109_rv1126/mobilenet_v1_rv1109_rv1126_batch_4.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rk180x.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rk180x.sh new file mode 100644 index 0000000..2ff2d2a --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rk180x.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rk180x" +./rknn_batch_inference_demo model/${chip_dir}/mobilenet_v1_rk1808_batch_4.rknn model/dog_224x224.jpg \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..efac954 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/run_rv1109_rv1126.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +./rknn_batch_inference_demo model/${chip_dir}/mobilenet_v1_rv1109_rv1126_batch_4.rknn model/dog_224x224.jpg \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/src/main.cc new file mode 100644 index 0000000..6cd5a6b --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_batch_inference_demo/src/main.cc @@ -0,0 +1,330 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "rknn_api.h" + +using namespace std; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static int rknn_GetTop( + float *pfProb, + float *pfMaxProb, + uint32_t *pMaxClass, + uint32_t outputCount, + uint32_t topNum) +{ + uint32_t i, j; + +#define MAX_TOP_NUM 20 + if (topNum > MAX_TOP_NUM) + return 0; + + memset(pfMaxProb, 0, sizeof(float) * topNum); + memset(pMaxClass, 0xff, sizeof(float) * topNum); + + for (j = 0; j < topNum; j++) + { + for (i = 0; i < outputCount; i++) + { + if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || + (i == *(pMaxClass + 3)) || (i == *(pMaxClass + 4))) + { + continue; + } + + if (pfProb[i] > *(pfMaxProb + j)) + { + *(pfMaxProb + j) = pfProb[i]; + *(pMaxClass + j) = i; + } + } + } + + return 1; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + const int MODEL_IN_WIDTH = 224; + const int MODEL_IN_HEIGHT = 224; + const int MODEL_IN_CHANNELS = 3; + const int MODEL_IN_BATCHSIZE = 4; + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + + // Load RKNN Model + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image + unsigned char *input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) + { + return -1; + } + + //concat img to batch + int img_size = input_attrs[0].dims[0] * input_attrs[0].dims[1] * input_attrs[0].dims[2] * sizeof(uint8_t); + unsigned char *in_data_batch = NULL; + in_data_batch = (unsigned char *)malloc(MODEL_IN_BATCHSIZE * img_size); + if (!in_data_batch) + { + return -1; + } + for (int i = 0; i < MODEL_IN_BATCHSIZE; ++i) + { + unsigned char *in_data_ptr = in_data_batch + img_size * i; + memcpy(in_data_ptr, input_data, img_size); + } + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = in_data_batch; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + for (int n = 0; n < MODEL_IN_BATCHSIZE; ++n) + { + for (int i = 0; i < io_num.n_output; i++) + { + uint32_t MaxClass[5]; + float fMaxProb[5]; + float *buffer_f32 = (float *)outputs[i].buf; + float *cur_batch_buf = buffer_f32 + 1001 * n; + uint32_t cur_batch_sz = outputs[i].size / (4 * MODEL_IN_BATCHSIZE); + rknn_GetTop(cur_batch_buf, fMaxProb, MaxClass, cur_batch_sz, 5); + + printf(" --- Top5 ---\n"); + for (int i = 0; i < 5; i++) + { + printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]); + } + } + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + if (input_data) + { + free(input_data); + } + if (in_data_batch) + { + stbi_image_free(in_data_batch); + } + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/CMakeLists.txt new file mode 100644 index 0000000..c2d48ec --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_mobilenet_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_mobilenet_demo + src/main.cc +) + +target_link_libraries(rknn_mobilenet_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_mobilenet_demo) +install(TARGETS rknn_mobilenet_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/README.md new file mode 100644 index 0000000..7a302a2 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_mobilenet_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_mobilenet_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_mobilenet_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/build.sh new file mode 100644 index 0000000..c869f2a --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +# GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/cat_224x224.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/cat_224x224.jpg new file mode 100644 index 0000000..11c5ba4 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/cat_224x224.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/dog_224x224.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/dog_224x224.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rk180x.rknn new file mode 100644 index 0000000..b02f728 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rv1109_rv1126.rknn new file mode 100644 index 0000000..b0f6735 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/model/mobilenet_v1_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/src/main.cc new file mode 100644 index 0000000..b0f7de2 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_mobilenet_demo/src/main.cc @@ -0,0 +1,327 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "rknn_api.h" + +using namespace std; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static inline int64_t getCurrentTimeUs() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static int rknn_GetTop( + float *pfProb, + float *pfMaxProb, + uint32_t *pMaxClass, + uint32_t outputCount, + uint32_t topNum) +{ + uint32_t i, j; + +#define MAX_TOP_NUM 20 + if (topNum > MAX_TOP_NUM) + return 0; + + memset(pfMaxProb, 0, sizeof(float) * topNum); + memset(pMaxClass, 0xff, sizeof(float) * topNum); + + for (j = 0; j < topNum; j++) + { + for (i = 0; i < outputCount; i++) + { + if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || + (i == *(pMaxClass + 3)) || (i == *(pMaxClass + 4))) + { + continue; + } + + if (pfProb[i] > *(pfMaxProb + j)) + { + *(pfMaxProb + j) = pfProb[i]; + *(pMaxClass + j) = i; + } + } + } + + return 1; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + int loop_count = 1; + if (argc > 3) + { + loop_count = atoi(argv[3]); + } + + // Load RKNN Model + model = load_model(model_path, &model_len); + + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image + unsigned char *input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) + { + return -1; + } + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = input_data; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + for (int n = 0; n < loop_count; ++n) + { + int64_t start_us = getCurrentTimeUs(); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + int64_t elapse_us = getCurrentTimeUs() - start_us; + printf("%4d: Elapse Time = %.2fms, FPS = %.2f\n", n, elapse_us / 1000.f, 1000.f * 1000.f / elapse_us); + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + for (int i = 0; i < io_num.n_output; i++) + { + uint32_t MaxClass[5]; + float fMaxProb[5]; + float *buffer = (float *)outputs[i].buf; + uint32_t sz = outputs[i].size / 4; + + rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5); + + printf(" --- Top5 ---\n"); + for (int i = 0; i < 5; i++) + { + printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]); + } + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + + if (input_data) + { + stbi_image_free(input_data); + } + + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/CMakeLists.txt new file mode 100644 index 0000000..3326955 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_multi_input_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +# utils +include_directories(${CMAKE_SOURCE_DIR}/src/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_multi_input_demo + src/main.cc +) + +target_link_libraries(rknn_multi_input_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_multi_input_demo) +install(TARGETS rknn_multi_input_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/README.md new file mode 100644 index 0000000..6782968 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_multi_input_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_multi_input_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_multi_input_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/build.sh new file mode 100644 index 0000000..cbcd900 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_multi_input_demo/ +cp run_rv1109_rv1126.sh install/rknn_multi_input_demo/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/dog_128x128_gray.png b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/dog_128x128_gray.png new file mode 100644 index 0000000..3b3c325 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/dog_128x128_gray.png differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/rk180x/multi_input_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/rk180x/multi_input_rk180x.rknn new file mode 100644 index 0000000..c3c5921 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/rk180x/multi_input_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/rv1109_rv1126/multi_input_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/rv1109_rv1126/multi_input_rv1109_rv1126.rknn new file mode 100644 index 0000000..4232100 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/model/rv1109_rv1126/multi_input_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/run_rk180x.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/run_rk180x.sh new file mode 100644 index 0000000..1f61582 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/run_rk180x.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rk180x" +./rknn_multi_input_demo model/${chip_dir}/multi_input_rk180x.rknn model/dog_128x128_gray.png \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..4b364fd --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/run_rv1109_rv1126.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +./rknn_multi_input_demo model/${chip_dir}/multi_input_rv1109_rv1126.rknn model/dog_128x128_gray.png \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/src/main.cc new file mode 100644 index 0000000..1c26d20 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_multi_input_demo/src/main.cc @@ -0,0 +1,284 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "rknn_api.h" + +using namespace std; + +#define DEMO_INPUT_NUM 4 + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + const int MODEL_IN_WIDTH = 128; + const int MODEL_IN_HEIGHT = 128; + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + + // Load RKNN Model + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + assert(DEMO_INPUT_NUM == io_num.n_input); + // Set Input struct + rknn_input inputs[DEMO_INPUT_NUM]; + memset(inputs, 0, DEMO_INPUT_NUM * sizeof(rknn_input)); + std::vector imgs; + for (int i = 0; i < io_num.n_input; ++i) + { + int channel = 0; + if (input_attrs[i].fmt == RKNN_TENSOR_NCHW) + { + channel = input_attrs[i].dims[2]; + } + else + { + channel = input_attrs[i].dims[0]; + } + //convert single channel to multi channels + // Load image + unsigned char *input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) + { + return -1; + } + imgs.push_back(input_data); + int img_size = input_attrs[0].size; + inputs[i].index = i; + inputs[i].type = RKNN_TENSOR_UINT8; + inputs[i].size = img_size; + inputs[i].fmt = RKNN_TENSOR_NHWC; + } + for (int i = 0; i < io_num.n_input; ++i) + { + inputs[i].buf = imgs[i]; + } + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + float ref_data[5] = {24.482, 20.453, 20.453, 20.453, 20.453}; + float *buffer = (float *)outputs[0].buf; + printf("first 5 result:\n"); + for (int i = 0; i < 5; ++i) + { + printf("[%d]:%.3f vs %.3f\n", i, buffer[i], ref_data[i]); + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + for (auto d : imgs) + { + stbi_image_free(d); + } + + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/CMakeLists.txt new file mode 100644 index 0000000..1cf20d4 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_pass_through_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "64bit") + set(LIB_ARCH lib64) +else() + message(STATUS "32bit") + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +# utils +include_directories(${CMAKE_SOURCE_DIR}/src/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_pass_through_demo + src/main.cc + src/quant_utils.cc +) + +target_link_libraries(rknn_pass_through_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_pass_through_demo) +install(TARGETS rknn_pass_through_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/README.md new file mode 100644 index 0000000..80d9556 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/README.md @@ -0,0 +1,38 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_pass_through_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_pass_through_demo /userdata/ +``` + +If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +``` +adb shell +cd /userdata/rknn_pass_through_demo/ +``` + +- RK1808/RK1806 +``` +./run_rk180x.sh int8 +``` + +- RV1109/RV1126 +``` +./run_rv1109_rv1126.sh int8 +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/build.sh new file mode 100644 index 0000000..b39e372 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/build.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_pass_through_demo/ +cp run_rv1109_rv1126.sh install/rknn_pass_through_demo/ \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/cat_224x224.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/cat_224x224.jpg new file mode 100644 index 0000000..11c5ba4 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/cat_224x224.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/dog_224x224.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/dog_224x224.jpg new file mode 100644 index 0000000..4f46457 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/dog_224x224.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_FP_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_FP_rk180x.rknn new file mode 100644 index 0000000..a6c328a Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_FP_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT16_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT16_rk180x.rknn new file mode 100644 index 0000000..61dc82d Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT16_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT8_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT8_rk180x.rknn new file mode 100644 index 0000000..b622a8e Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_INT8_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_UINT8_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_UINT8_rk180x.rknn new file mode 100644 index 0000000..c86d218 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rk180x/mobilenet_v2_UINT8_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_FP_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_FP_rv1109_rv1126.rknn new file mode 100644 index 0000000..651fcac Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_FP_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT16_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT16_rv1109_rv1126.rknn new file mode 100644 index 0000000..46b5aaf Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT16_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT8_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT8_rv1109_rv1126.rknn new file mode 100644 index 0000000..eed99da Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_INT8_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_UINT8_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_UINT8_rv1109_rv1126.rknn new file mode 100644 index 0000000..3308572 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/model/rv1109_rv1126/mobilenet_v2_UINT8_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/run_rk180x.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/run_rk180x.sh new file mode 100644 index 0000000..ffb9880 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/run_rk180x.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +chip_dir="rk180x" +case $1 in + uint8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_UINT8_rk180x.rknn model/dog_224x224.jpg + ;; + int8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT8_rk180x.rknn model/dog_224x224.jpg + ;; + int16) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT16_rk180x.rknn model/dog_224x224.jpg + ;; + fp) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_FP_rk180x.rknn model/dog_224x224.jpg + ;; + *) + echo './run.sh uint8 int8 int16 fp' + ;; +esac + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..4f7166e --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/run_rv1109_rv1126.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +case $1 in + uint8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_UINT8_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + int8) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT8_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + int16) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_INT16_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + fp) + ./rknn_pass_through_demo model/${chip_dir}/mobilenet_v2_FP_rv1109_rv1126.rknn model/dog_224x224.jpg + ;; + *) + echo './run.sh uint8 int8 int16 fp' + ;; +esac + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/main.cc new file mode 100644 index 0000000..59108c4 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/main.cc @@ -0,0 +1,484 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#include "quant_utils.h" +#include "rknn_api.h" + +using namespace std; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d " + "fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], + attr->dims[1], attr->dims[0], attr->n_elems, attr->size, 0, attr->type, + attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + FILE *fp = fopen(filename, "rb"); + if (fp == nullptr) + { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char *model = (unsigned char *)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) + { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) + { + fclose(fp); + } + return model; +} + +static int GetElementByte(rknn_tensor_attr *in_attr) +{ + int byte = 0; + switch (in_attr->type) + { + case RKNN_TENSOR_FLOAT32: + byte = 4; + break; + case RKNN_TENSOR_FLOAT16: + case RKNN_TENSOR_INT16: + byte = 2; + break; + case RKNN_TENSOR_INT8: + case RKNN_TENSOR_UINT8: + byte = 1; + break; + default: + break; + } + return byte; +} + +static unsigned char *load_image(const char *image_path, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) + { + unsigned char *image_resized = (unsigned char *)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) + { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) + { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +static int ProcessInput(unsigned char *src_buf, void **dst_buf, rknn_tensor_attr *in_attr, + std::vector mean, std::vector scale, + bool isReorder210, bool isNCHW) +{ + // check + if (3 != mean.size() || src_buf == NULL) + { + return -1; + } + int img_height, img_width, img_channels = 0; + if (isNCHW) + { + img_width = in_attr->dims[0]; + img_height = in_attr->dims[1]; + img_channels = in_attr->dims[2]; + } + else + { + img_channels = in_attr->dims[0]; + img_width = in_attr->dims[1]; + img_height = in_attr->dims[2]; + } + int HW = img_height * img_width; + int ele_count = HW * img_channels; + int ele_bytes = GetElementByte(in_attr); + printf("total element count = %d, bytes per element = %d\n", ele_count, + ele_bytes); + float *pixel_f = (float *)malloc(ele_count * sizeof(float)); + + // RGB2BGR + if (isReorder210 == true) + { + printf("perform RGB2BGR\n"); + float *f_ptr = pixel_f; + unsigned char *u_ptr = src_buf; + for (int i = 0; i < HW; ++i) + { + f_ptr[2] = u_ptr[0]; + f_ptr[1] = u_ptr[1]; + f_ptr[0] = u_ptr[2]; + u_ptr += img_channels; + f_ptr += img_channels; + } + } + else + { + float *f_ptr = pixel_f; + unsigned char *u_ptr = src_buf; + for (int i = 0; i < ele_count; ++i) + { + f_ptr[i] = u_ptr[i]; + } + } + + // normalize + printf("perform normalize\n"); + float *f_ptr = pixel_f; + for (int i = 0; i < HW; ++i) + { + for (int j = 0; j < img_channels; ++j) + { + f_ptr[j] -= mean[j]; + f_ptr[j] /= scale[j]; + } + f_ptr += img_channels; + } + + // quantize + printf("perform quantize\n"); + uint8_t *qnt_buf = (uint8_t *)malloc(ele_count * ele_bytes); + switch (in_attr->type) + { + case RKNN_TENSOR_FLOAT32: + memcpy(qnt_buf, pixel_f, ele_count * ele_bytes); + break; + case RKNN_TENSOR_FLOAT16: + f32_to_f16((uint16_t *)(qnt_buf), pixel_f, ele_count); + break; + case RKNN_TENSOR_INT16: + case RKNN_TENSOR_INT8: + case RKNN_TENSOR_UINT8: + switch (in_attr->qnt_type) + { + case RKNN_TENSOR_QNT_DFP: + qnt_f32_to_dfp(qnt_buf, in_attr->type, in_attr->fl, pixel_f, ele_count); + break; + case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + qnt_f32_to_affine(qnt_buf, in_attr->type, in_attr->zp, in_attr->scale, pixel_f, ele_count); + break; + case RKNN_TENSOR_QNT_NONE: + qnt_f32_to_none(qnt_buf, in_attr->type, pixel_f, ele_count); + default: + break; + } + break; + default: + break; + } + + // NHWC ==> NCHW + if (isNCHW) + { + printf("perform NHWC to NCHW\n"); + uint8_t *nchw_buf = (uint8_t *)malloc(ele_count * ele_bytes); + uint8_t *dst_ptr = nchw_buf; + uint8_t *src_ptr = qnt_buf; + for (int i = 0; i < img_channels; ++i) + { + src_ptr = qnt_buf + i * ele_bytes; + dst_ptr = nchw_buf + i * HW * ele_bytes; + for (int j = 0; j < HW; ++j) + { + // dst_ptr[i*HW+j] = src_ptr[j*C+i]; + memcpy(dst_ptr, src_ptr, ele_bytes); + src_ptr += img_channels * ele_bytes; + dst_ptr += ele_bytes; + } + } + *dst_buf = nchw_buf; + free(qnt_buf); + } + else + { + *dst_buf = qnt_buf; + } + + free(pixel_f); + + return 0; +} + +static int rknn_GetTop( + float *pfProb, + float *pfMaxProb, + uint32_t *pMaxClass, + uint32_t outputCount, + uint32_t topNum) +{ + uint32_t i, j; + +#define MAX_TOP_NUM 20 + if (topNum > MAX_TOP_NUM) + return 0; + + memset(pfMaxProb, 0, sizeof(float) * topNum); + memset(pMaxClass, 0xff, sizeof(float) * topNum); + + for (j = 0; j < topNum; j++) + { + for (i = 0; i < outputCount; i++) + { + if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || + (i == *(pMaxClass + 3)) || (i == *(pMaxClass + 4))) + { + continue; + } + + if (pfProb[i] > *(pfMaxProb + j)) + { + *(pfMaxProb + j) = pfProb[i]; + *(pMaxClass + j) = i; + } + } + } + + return 1; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char **argv) +{ + const int MODEL_IN_WIDTH = 224; + const int MODEL_IN_HEIGHT = 224; + const int MODEL_IN_CHANNELS = 3; + bool isNCHW = false; + // mobilenet v2 mean/scale in rknn.config() + bool isReorder210 = true; /* reorder= '2 1 0' */ + std::vector mean({103.94, 116.78, 123.68}); + std::vector scale({58.82, 58.82, 58.82}); + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char *model; + + const char *model_path = argv[1]; + const char *img_path = argv[2]; + + // Load RKNN Model + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, + io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) + { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image + unsigned char *img_data = NULL; + img_data = load_image(img_path, &input_attrs[0]); + if (!img_data) + { + return -1; + } + + // rknn model need nchw layout input + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) + { + isNCHW = true; + } + + // process input when pass_through = 1 + void *in_data = NULL; + ProcessInput(img_data, &in_data, &(input_attrs[0]), mean, scale, isReorder210, + isNCHW); + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NCHW; + inputs[0].buf = in_data; + inputs[0].pass_through = 1; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) + { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) + { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[1]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + ret = rknn_outputs_get(ctx, 1, outputs, NULL); + if (ret < 0) + { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + for (int i = 0; i < io_num.n_output; i++) + { + uint32_t MaxClass[5]; + float fMaxProb[5]; + float *buffer = (float *)outputs[i].buf; + uint32_t sz = outputs[i].size / 4; + + rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5); + + printf(" --- Top5 ---\n"); + for (int i = 0; i < 5; i++) + { + printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]); + } + } + + // Release rknn_outputs + rknn_outputs_release(ctx, 1, outputs); + + // Release + if (ctx >= 0) + { + rknn_destroy(ctx); + } + if (model) + { + free(model); + } + stbi_image_free(img_data); + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.cc new file mode 100644 index 0000000..d59e11c --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.cc @@ -0,0 +1,181 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "quant_utils.h" +#include "rknn_api.h" + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + // return (int32_t)((f > 0.0) ? (f + 0.5) : (f - 0.5)); //四舍五入 + return f; +} + +void f32_to_f16(uint16_t *f16, float *f32, int num) +{ + float *src = f32; + uint16_t *dst = f16; + int i = 0; + + for (; i < num; i++) + { + float in = *src; + + uint32_t fp32 = *((uint32_t *)&in); + uint32_t t1 = (fp32 & 0x80000000u) >> 16; /* sign bit. */ + uint32_t t2 = (fp32 & 0x7F800000u) >> 13; /* Exponent bits */ + uint32_t t3 = (fp32 & 0x007FE000u) >> 13; /* Mantissa bits, no rounding */ + uint32_t fp16 = 0u; + + if (t2 >= 0x023c00u) + { + fp16 = t1 | 0x7BFF; /* Don't round to infinity. */ + } + else if (t2 <= 0x01c000u) + { + fp16 = t1; + } + else + { + t2 -= 0x01c000u; + fp16 = t1 | t2 | t3; + } + + *dst = (uint16_t)fp16; + + src++; + dst++; + } +} + +void qnt_f32_to_dfp(uint8_t *qnt, uint8_t type, int8_t fl, float *f32, + int num) +{ + float *src_ptr = f32; + int i = 0; + float dst_val = 0.0; + + switch (type) + { + case RKNN_TENSOR_INT8: + for (; i < num; i++) + { + dst_val = (fl > 0) ? ((*src_ptr) * ((float)(1 << fl))) + : ((*src_ptr) / (float)(1 << -fl)); + *((int8_t *)qnt) = (int8_t)__clip(dst_val, -128, 127); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_UINT8: + for (; i < num; i++) + { + dst_val = (fl > 0) ? ((*src_ptr) * ((float)(1 << fl))) + : ((*src_ptr) / (float)(1 << -fl)); + *qnt = (uint8_t)__clip(dst_val, 0, 255); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_INT16: + for (; i < num; i++) + { + dst_val = (fl > 0) ? ((*src_ptr) * ((float)(1 << fl))) + : ((*src_ptr) / (float)(1 << -fl)); + *((int16_t *)qnt) = (int16_t)__clip(dst_val, -32768, 32767); + src_ptr++; + qnt += 2; + } + break; + default: + break; + } +} + +void qnt_f32_to_affine(uint8_t *qnt, uint8_t type, uint8_t zp, float scale, + float *f32, int num) +{ + float *src_ptr = f32; + int i = 0; + float dst_val = 0.0; + + switch (type) + { + case RKNN_TENSOR_INT8: + for (; i < num; i++) + { + dst_val = ((*src_ptr) / scale) + zp; + *((int8_t *)qnt) = (int8_t)__clip(dst_val, -128, 127); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_UINT8: + for (; i < num; i++) + { + dst_val = ((*src_ptr) / scale) + zp; + *qnt = (uint8_t)__clip(dst_val, 0, 255); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_INT16: + for (; i < num; i++) + { + dst_val = ((*src_ptr) / scale) + zp; + *((int16_t *)qnt) = (int16_t)__clip(dst_val, -32768, 32767); + src_ptr++; + qnt += 2; + } + break; + default: + break; + } +} + +void qnt_f32_to_none(uint8_t *qnt, uint8_t type, float *f32, int num) +{ + float *src_ptr = f32; + int i = 0; + + switch (type) + { + case RKNN_TENSOR_INT8: + for (; i < num; i++) + { + *((int8_t *)qnt) = (int8_t)__clip(*src_ptr, -128, 127); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_UINT8: + for (; i < num; i++) + { + *qnt = (uint8_t)__clip(*src_ptr, 0, 255); + src_ptr++; + qnt++; + } + break; + case RKNN_TENSOR_INT16: + for (; i < num; i++) + { + *((int16_t *)qnt) = (int16_t)__clip(*src_ptr, -32768, 32767); + src_ptr++; + qnt += 2; + } + break; + default: + break; + } +} \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.h new file mode 100644 index 0000000..f6fa12e --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_pass_through_demo/src/quant_utils.h @@ -0,0 +1,28 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _RKNN_DEMO_QUANT_UTILS_H_ +#define _RKNN_DEMO_QUANT_UTILS_H_ + +#include + +void f32_to_f16(uint16_t *f16, float *f32, int num); + +void qnt_f32_to_dfp(uint8_t *qnt, uint8_t type, int8_t fl, float *f32, int num); + +void qnt_f32_to_affine(uint8_t *qnt, uint8_t type, uint8_t zp, float scale, float *f32, int num); + +void qnt_f32_to_none(uint8_t *qnt, uint8_t type, float *f32, int num); + +#endif /*_RKNN_DEMO_QUANT_UTILS_H_ */ \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/CMakeLists.txt new file mode 100644 index 0000000..0fb7f63 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_ssd_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_ssd_demo + src/main.cc + src/ssd.cc + ) + +target_link_libraries(rknn_ssd_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_ssd_demo) +install(TARGETS rknn_ssd_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/README.md new file mode 100644 index 0000000..bae5217 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/README.md @@ -0,0 +1,39 @@ +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## Install + +Copy install/rknn_ssd_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +``` +adb push install/rknn_ssd_demo /userdata/ +``` + +- If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + + +## Run + +``` +adb shell +cd /userdata/rknn_ssd_demo/ +``` + +- RK1808/RK1806 +``` +./rknn_ssd_demo model/ssd_inception_v2_rk180x.rknn model/road.bmp +``` + +- RV1109/RV1126 +``` +./rknn_ssd_demo model/ssd_inception_v2_rv1109_rv1126.rknn model/road.bmp +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/build.sh new file mode 100644 index 0000000..6ddeae5 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/build.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=/work/projects/rv1109/git/rv1109/prebuilts/gcc/linux-x86/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=~/opts/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/box_priors.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/box_priors.txt new file mode 100644 index 0000000..efc3be4 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/box_priors.txt @@ -0,0 +1,4 @@ + 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.02631579 0.02631579 0.026315793 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.078947365 0.07894737 0.078947365 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.13157895 0.13157895 0.13157894 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.18421052 0.18421051 0.18421052 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.23684211 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.28947368 0.28947368 0.28947365 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.34210524 0.34210524 0.3421052 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.92105263 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.97368425 0.9736843 0.97368425 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.049999997 0.049999997 0.049999997 0.05 0.050000012 0.049999997 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.25 0.25 0.25 0.25 0.25000003 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000005 0.35000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.45 0.45000002 0.45000002 0.45000002 0.45000002 0.45000002 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.55 0.55 0.55 0.55 0.54999995 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.099999994 0.1 0.099999994 0.1 0.099999994 0.099999994 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.30000004 0.3 0.3 0.3 0.3 0.30000004 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.49999997 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.90000004 0.16666667 0.16666667 0.16666666 0.16666667 0.16666669 0.16666667 0.16666667 0.16666667 0.16666666 0.16666667 0.16666669 0.16666667 0.16666667 0.16666667 0.16666666 0.16666667 0.16666669 0.16666667 0.5 0.5 0.49999997 0.5 0.5 0.5 0.5 0.5 0.49999997 0.5 0.5 0.5 0.5 0.5 0.49999997 0.5 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.8333334 0.25 0.25 0.25 0.24999999 0.25 0.25 0.25 0.25 0.25 0.24999999 0.25 0.25 0.75 0.75 0.75 0.75 0.74999994 0.75 0.75 0.75 0.75 0.75 0.74999994 0.75 0.5 0.5 0.5 0.5 0.5 0.5 + 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.02631579 0.026315793 0.02631579 0.078947365 0.078947365 0.07894737 0.13157895 0.13157894 0.13157895 0.18421052 0.18421052 0.18421051 0.23684211 0.23684211 0.23684211 0.28947368 0.28947365 0.28947368 0.34210524 0.3421052 0.34210524 0.39473683 0.39473683 0.39473683 0.4473684 0.4473684 0.4473684 0.5 0.5 0.5 0.5526316 0.5526316 0.5526316 0.6052632 0.6052632 0.6052632 0.65789473 0.65789473 0.65789473 0.71052635 0.71052635 0.71052635 0.7631579 0.7631579 0.7631579 0.8157895 0.8157895 0.8157895 0.8684211 0.8684211 0.8684211 0.92105263 0.92105263 0.92105263 0.97368425 0.97368425 0.9736843 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.049999997 0.049999997 0.050000004 0.050000012 0.05 0.049999997 0.15 0.14999999 0.15 0.15 0.15 0.15 0.25 0.25 0.25 0.25 0.25 0.25 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.35000002 0.45000002 0.45 0.45000002 0.45000002 0.45 0.45000002 0.55 0.55 0.55 0.55 0.55 0.55 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.65000004 0.75 0.75 0.75 0.75 0.75 0.75 0.85 0.85 0.85 0.85 0.85 0.85 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.95000005 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.10000001 0.099999994 0.1 0.099999994 0.1 0.099999994 0.3 0.3 0.3 0.29999998 0.3 0.30000004 0.5 0.5 0.5 0.5 0.5 0.49999997 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.70000005 0.9 0.90000004 0.90000004 0.9 0.90000004 0.90000004 0.16666667 0.16666669 0.16666667 0.16666669 0.16666667 0.16666667 0.49999997 0.5 0.5 0.50000006 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333333 0.8333334 0.8333334 0.16666667 0.16666669 0.16666667 0.16666669 0.16666667 0.16666667 0.49999997 0.5 0.5 0.50000006 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333333 0.8333334 0.8333334 0.16666667 0.16666669 0.16666667 0.16666669 0.16666667 0.16666667 0.49999997 0.5 0.5 0.50000006 0.5 0.5 0.8333334 0.8333334 0.8333334 0.8333333 0.8333334 0.8333334 0.25 0.25 0.25 0.25 0.25 0.25 0.75 0.75 0.75 0.75 0.75 0.75 0.25 0.25 0.25 0.25 0.25 0.25 0.75 0.75 0.75 0.75 0.75 0.75 0.5 0.5 0.5 0.5 0.5 0.5 + 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.1 0.14142136 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.28284273 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142138 0.2828427 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.099999994 0.14142135 0.28284273 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.10000001 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142138 0.2828427 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142135 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.2828427 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142132 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.100000024 0.14142138 0.28284276 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.2474874 0.4949748 0.20207259 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748738 0.4949748 0.20207258 0.6062481 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.4949748 0.2020726 0.60624814 0.41833 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35000002 0.24748741 0.49497482 0.2020726 0.60624814 0.41832998 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35 0.24748737 0.4949748 0.20207256 0.6062481 0.41833 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497476 0.20207262 0.606248 0.41833004 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000002 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.35000008 0.24748743 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.34999996 0.24748737 0.49497485 0.20207262 0.60624814 0.41832995 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.50000006 0.3535534 0.7071068 0.28867513 0.8660687 0.57008773 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5000001 0.3535534 0.7071068 0.28867513 0.8660687 0.5700878 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.2886751 0.8660687 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5 0.3535534 0.7071068 0.28867507 0.8660688 0.5700877 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.5000001 0.3535534 0.70710677 0.2886752 0.8660687 0.5700878 0.65000004 0.45961943 0.91923887 0.37527767 1.1258893 0.7211102 0.65000004 0.45961943 0.91923887 0.37527767 1.1258893 0.7211102 0.65000004 0.45961943 0.91923887 0.37527767 1.1258893 0.7211102 0.6500001 0.4596194 0.9192388 0.37527764 1.1258893 0.7211102 0.6500001 0.4596194 0.9192388 0.37527764 1.1258893 0.7211102 0.6500001 0.4596194 0.9192388 0.37527764 1.1258893 0.7211102 0.6500001 0.45961946 0.9192388 0.3752777 1.1258893 0.72111017 0.6500001 0.45961946 0.9192388 0.3752777 1.1258893 0.72111017 0.6500001 0.45961946 0.9192388 0.3752777 1.1258893 0.72111017 0.8000001 0.5656855 1.131371 0.4618802 1.3857099 0.8717798 0.8000001 0.5656855 1.131371 0.4618802 1.3857099 0.8717798 0.80000013 0.5656855 1.131371 0.4618802 1.3857098 0.87177986 0.80000013 0.5656855 1.131371 0.4618802 1.3857098 0.87177986 0.95000005 0.6717515 1.343503 0.5484828 1.6455305 0.97467947 + 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.1 0.28284273 0.14142136 0.099999994 0.28284273 0.14142138 0.099999994 0.2828427 0.14142138 0.099999994 0.28284273 0.14142135 0.099999994 0.28284273 0.14142135 0.10000001 0.2828427 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142138 0.100000024 0.2828427 0.14142138 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142135 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.2828427 0.14142132 0.100000024 0.28284276 0.14142132 0.100000024 0.28284276 0.14142138 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.34999996 0.4949747 0.24748735 0.60621774 0.20206249 0.41833 0.34999996 0.49497467 0.24748737 0.60621774 0.20206249 0.41833 0.34999996 0.49497473 0.24748737 0.60621774 0.20206249 0.41833 0.34999993 0.49497473 0.24748737 0.60621774 0.20206249 0.41832998 0.34999996 0.49497467 0.24748737 0.60621774 0.20206246 0.41833 0.35 0.49497473 0.24748734 0.60621774 0.20206249 0.41833004 0.35 0.49497473 0.2474873 0.60621774 0.20206249 0.41833004 0.3499999 0.49497473 0.2474873 0.6062178 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062177 0.20206249 0.41832995 0.3499999 0.49497467 0.2474873 0.6062178 0.20206255 0.41832995 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.49999997 0.7071067 0.35355335 0.8660254 0.2886607 0.57008773 0.5 0.7071067 0.35355335 0.8660253 0.2886607 0.5700878 0.5 0.7071067 0.35355332 0.86602545 0.28866073 0.5700877 0.5 0.70710665 0.3535533 0.86602545 0.28866076 0.5700877 0.49999994 0.7071067 0.3535534 0.8660253 0.28866065 0.5700878 0.6499999 0.9192387 0.45961934 1.1258329 0.3752589 0.7211102 0.64999986 0.9192387 0.4596193 1.125833 0.37525892 0.7211102 0.64999986 0.91923875 0.45961928 1.1258328 0.37525892 0.72111017 0.6499999 0.9192387 0.45961934 1.1258329 0.3752589 0.7211102 0.64999986 0.9192387 0.4596193 1.125833 0.37525892 0.7211102 0.64999986 0.91923875 0.45961928 1.1258328 0.37525892 0.72111017 0.6499999 0.9192387 0.45961934 1.1258329 0.3752589 0.7211102 0.64999986 0.9192387 0.4596193 1.125833 0.37525892 0.7211102 0.64999986 0.91923875 0.45961928 1.1258328 0.37525892 0.72111017 0.79999995 1.1313708 0.5656854 1.3856406 0.46185714 0.8717798 0.79999995 1.1313708 0.56568533 1.3856406 0.46185708 0.87177986 0.79999995 1.1313708 0.5656854 1.3856406 0.46185714 0.8717798 0.79999995 1.1313708 0.56568533 1.3856406 0.46185708 0.87177986 0.9499999 1.3435028 0.6717514 1.6454482 0.54845536 0.97467947 \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/coco_labels_list.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/coco_labels_list.txt new file mode 100644 index 0000000..c634db4 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/coco_labels_list.txt @@ -0,0 +1,91 @@ +??? +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +??? +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +??? +backpack +umbrella +??? +??? +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +??? +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +??? +dining table +??? +??? +toilet +??? +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +??? +book +clock +vase +scissors +teddy bear +hair drier +toothbrush \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/road.bmp b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/road.bmp new file mode 100644 index 0000000..1254b96 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/road.bmp differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rk180x.rknn new file mode 100644 index 0000000..712b791 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rv1109_rv1126.rknn new file mode 100644 index 0000000..a65660e Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/model/ssd_inception_v2_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/main.cc new file mode 100644 index 0000000..b4e9559 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/main.cc @@ -0,0 +1,277 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include + +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb/stb_image_write.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#undef cimg_display +#define cimg_display 0 +#include "CImg/CImg.h" +#include "rknn_api.h" +#include "ssd.h" + +using namespace std; +using namespace cimg_library; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +static void printRKNNTensor(rknn_tensor_attr* attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d fmt=%d type=%d qnt_type=%d fl=%d zp=%d " + "scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, 0, attr->type, attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static unsigned char* load_model(const char* filename, int* model_size) +{ + FILE* fp = fopen(filename, "rb"); + if (fp == nullptr) { + printf("fopen %s fail!\n", filename); + return NULL; + } + fseek(fp, 0, SEEK_END); + int model_len = ftell(fp); + unsigned char* model = (unsigned char*)malloc(model_len); + fseek(fp, 0, SEEK_SET); + if (model_len != fread(model, 1, model_len, fp)) { + printf("fread %s fail!\n", filename); + free(model); + return NULL; + } + *model_size = model_len; + if (fp) { + fclose(fp); + } + return model; +} +static unsigned char* load_image(const char* image_path, rknn_tensor_attr* input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + printf("w=%d,h=%d,c=%d, fmt=%d\n", req_width, req_height, req_channel, input_attr->fmt); + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char* image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) { + printf("load image failed!\n"); + return NULL; + } + + if (width != req_width || height != req_height) { + unsigned char* image_resized = (unsigned char*)STBI_MALLOC(req_width * req_height * req_channel); + if (!image_resized) { + printf("malloc image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + if (stbir_resize_uint8(image_data, width, height, 0, image_resized, req_width, req_height, 0, channel) != 1) { + printf("resize image failed!\n"); + STBI_FREE(image_data); + return NULL; + } + STBI_FREE(image_data); + image_data = image_resized; + } + + return image_data; +} + +/*------------------------------------------- + Main Function +-------------------------------------------*/ +int main(int argc, char** argv) +{ + const int img_width = 300; + const int img_height = 300; + const int img_channels = 3; + + rknn_context ctx; + int ret; + int model_len = 0; + unsigned char* model; + + const char* model_path = argv[1]; + const char* img_path = argv[2]; + + if (argc != 3) { + printf("Usage:%s model image\n", argv[0]); + return -1; + } + if (strstr(img_path, ".jpg") != NULL || strstr(img_path, ".png") != NULL) { + printf("Error: read %s failed! only support .bmp format image\n", img_path); + return -1; + } + + // Load RKNN Model + printf("Loading model ...\n"); + model = load_model(model_path, &model_len); + ret = rknn_init(&ctx, model, model_len, 0); + if (ret < 0) { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + // Get Model Input Output Info + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret != RKNN_SUCC) { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + printf("input tensors:\n"); + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(input_attrs[i])); + } + + printf("output tensors:\n"); + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret != RKNN_SUCC) { + printf("rknn_query fail! ret=%d\n", ret); + return -1; + } + printRKNNTensor(&(output_attrs[i])); + } + + // Load image, only support .bmp format + CImg img(img_path); + if (img.height() != img_height || img.width() != img_width) { + img = img.resize(img_width, img_height); + } + unsigned char* input_data = NULL; + input_data = load_image(img_path, &input_attrs[0]); + if (!input_data) { + return -1; + } + + // Set Input Data + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_attrs[0].size; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = input_data; + + ret = rknn_inputs_set(ctx, io_num.n_input, inputs); + if (ret < 0) { + printf("rknn_input_set fail! ret=%d\n", ret); + return -1; + } + + // Run + printf("rknn_run\n"); + ret = rknn_run(ctx, nullptr); + if (ret < 0) { + printf("rknn_run fail! ret=%d\n", ret); + return -1; + } + + // Get Output + rknn_output outputs[2]; + memset(outputs, 0, sizeof(outputs)); + outputs[0].want_float = 1; + outputs[1].want_float = 1; + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + if (ret < 0) { + printf("rknn_outputs_get fail! ret=%d\n", ret); + return -1; + } + + // Post Process + detect_result_group_t detect_result_group; + postProcessSSD((float*)(outputs[0].buf), (float*)(outputs[1].buf), img_height, img_width, &detect_result_group); + // Release rknn_outputs + rknn_outputs_release(ctx, 2, outputs); + + // Draw Objects + const unsigned char blue[] = {0, 0, 255}; + for (int i = 0; i < detect_result_group.count; i++) { + detect_result_t* det_result = &(detect_result_group.results[i]); + printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top, + det_result->box.right, det_result->box.bottom, det_result->prop); + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + // draw box + img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U); + img.draw_text(x1, y1 - 12, det_result->name, blue); + } + img.save("./out.bmp"); + + // Release + if (ctx >= 0) { + rknn_destroy(ctx); + } + if (model) { + free(model); + } + if (input_data) { + stbi_image_free(input_data); + } + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.cc new file mode 100644 index 0000000..2dccac7 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.cc @@ -0,0 +1,323 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "ssd.h" + +#define BOX_PRIORS_TXT_PATH "./model/box_priors.txt" +#define LABEL_NALE_TXT_PATH "./model/coco_labels_list.txt" + +float MIN_SCORE = 0.4f; +float NMS_THRESHOLD = 0.45f; + +static char* labels[NUM_CLASS]; +static float box_priors[4][NUM_RESULTS]; + +int64_t getCurrentTimeUs() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +char* readLine(FILE* fp, char* buffer, int* len) +{ + int ch; + int i = 0; + size_t buff_len = 0; + + buffer = (char*)malloc(buff_len + 1); + if (!buffer) + return NULL; // Out of memory + + while ((ch = fgetc(fp)) != '\n' && ch != EOF) { + buff_len++; + void* tmp = realloc(buffer, buff_len + 1); + if (tmp == NULL) { + free(buffer); + return NULL; // Out of memory + } + buffer = (char*)tmp; + + buffer[i] = (char)ch; + i++; + } + buffer[i] = '\0'; + + *len = buff_len; + + // Detect end + if (ch == EOF && (i == 0 || ferror(fp))) { + free(buffer); + return NULL; + } + return buffer; +} + +int readLines(const char* fileName, char* lines[], int max_line) +{ + FILE* file = fopen(fileName, "r"); + char* s; + int i = 0; + int n = 0; + while ((s = readLine(file, s, &n)) != NULL) { + lines[i++] = s; + if (i >= max_line) + break; + } + return i; +} + +int loadLabelName(const char* locationFilename, char* label[]) +{ + printf("ssd - loadLabelName %s\n", locationFilename); + readLines(locationFilename, label, NUM_CLASS); + return 0; +} + +int loadBoxPriors(const char* locationFilename, float (*boxPriors)[NUM_RESULTS]) +{ + const char* d = " "; + char* lines[4]; + int count = readLines(locationFilename, lines, 4); + // printf("line count %d\n", count); + // for (int i = 0; i < count; i++) { + // printf("%s\n", lines[i]); + // } + for (int i = 0; i < 4; i++) { + char* line_str = lines[i]; + char* p; + p = strtok(line_str, d); + int priorIndex = 0; + while (p) { + float number = (float)(atof(p)); + boxPriors[i][priorIndex++] = number; + p = strtok(NULL, d); + } + if (priorIndex != NUM_RESULTS) { + printf("error\n"); + return -1; + } + } + return 0; +} + +float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, + float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1)); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1)); + float i = w * h; + float u = (xmax0 - xmin0) * (ymax0 - ymin0) + (xmax1 - xmin1) * (ymax1 - ymin1) - i; + return u <= 0.f ? 0.f : (i / u); +} + +float unexpit(float y) { return -1.0 * logf((1.0 / y) - 1.0); } + +float expit(float x) { return (float)(1.0 / (1.0 + expf(-x))); } + +void decodeCenterSizeBoxes(float* predictions, float (*boxPriors)[NUM_RESULTS]) +{ + for (int i = 0; i < NUM_RESULTS; ++i) { + float ycenter = predictions[i * 4 + 0] / Y_SCALE * boxPriors[2][i] + boxPriors[0][i]; + float xcenter = predictions[i * 4 + 1] / X_SCALE * boxPriors[3][i] + boxPriors[1][i]; + float h = (float)expf(predictions[i * 4 + 2] / H_SCALE) * boxPriors[2][i]; + float w = (float)expf(predictions[i * 4 + 3] / W_SCALE) * boxPriors[3][i]; + + float ymin = ycenter - h / 2.0f; + float xmin = xcenter - w / 2.0f; + float ymax = ycenter + h / 2.0f; + float xmax = xcenter + w / 2.0f; + + predictions[i * 4 + 0] = ymin; + predictions[i * 4 + 1] = xmin; + predictions[i * 4 + 2] = ymax; + predictions[i * 4 + 3] = xmax; + } +} + +int filterValidResult(float* outputClasses, int (*output)[NUM_RESULTS], int numClasses, float* props) +{ + int validCount = 0; + float min_score = unexpit(MIN_SCORE); + + // Scale them back to the input size. + for (int i = 0; i < NUM_RESULTS; ++i) { + float topClassScore = (float)(-1000.0); + int topClassScoreIndex = -1; + + // Skip the first catch-all class. + for (int j = 1; j < numClasses; ++j) { + // x and expit(x) has same monotonicity + // so compare x and comare expit(x) is same + // float score = expit(outputClasses[i*numClasses+j]); + float score = outputClasses[i * numClasses + j]; + + if (score > topClassScore) { + topClassScoreIndex = j; + topClassScore = score; + } + } + + if (topClassScore >= min_score) { + output[0][validCount] = i; + output[1][validCount] = topClassScoreIndex; + props[validCount] = expit(outputClasses[i * numClasses + topClassScoreIndex]); + ++validCount; + } + } + + return validCount; +} + +int nms(int validCount, float* outputLocations, int (*output)[NUM_RESULTS]) +{ + for (int i = 0; i < validCount; ++i) { + if (output[0][i] == -1) { + continue; + } + int n = output[0][i]; + for (int j = i + 1; j < validCount; ++j) { + int m = output[0][j]; + if (m == -1) { + continue; + } + float xmin0 = outputLocations[n * 4 + 1]; + float ymin0 = outputLocations[n * 4 + 0]; + float xmax0 = outputLocations[n * 4 + 3]; + float ymax0 = outputLocations[n * 4 + 2]; + + float xmin1 = outputLocations[m * 4 + 1]; + float ymin1 = outputLocations[m * 4 + 0]; + float xmax1 = outputLocations[m * 4 + 3]; + float ymax1 = outputLocations[m * 4 + 2]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou >= NMS_THRESHOLD) { + output[0][j] = -1; + } + } + } + return 0; +} + +void sort(int output[][1917], float* props, int sz) +{ + int i = 0; + int j = 0; + + if (sz < 2) { + return; + } + + for (i = 0; i < sz - 1; i++) { + int top = i; + for (j = i + 1; j < sz; j++) { + if (props[top] < props[j]) { + top = j; + } + } + + if (i != top) { + int tmp1 = output[0][i]; + int tmp2 = output[1][i]; + float prop = props[i]; + output[0][i] = output[0][top]; + output[1][i] = output[1][top]; + props[i] = props[top]; + output[0][top] = tmp1; + output[1][top] = tmp2; + props[top] = prop; + } + } +} + +int postProcessSSD(float* predictions, float* output_classes, int width, int heigh, detect_result_group_t* group) +{ + static int init = -1; + if (init == -1) { + int ret = 0; + printf("loadLabelName\n"); + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) { + return -1; + } + + printf("loadBoxPriors\n"); + ret = loadBoxPriors(BOX_PRIORS_TXT_PATH, box_priors); + if (ret < 0) { + return -1; + } + + init = 0; + } + + int output[2][NUM_RESULTS]; + float props[NUM_RESULTS]; + memset(output, 0, 2 * NUM_RESULTS); + memset(props, 0, sizeof(float) * NUM_RESULTS); + + decodeCenterSizeBoxes(predictions, box_priors); + + int validCount = filterValidResult(output_classes, output, NUM_CLASS, props); + + if (validCount > OBJ_NUMB_MAX_SIZE) { + printf("validCount too much !!\n"); + return -1; + } + + sort(output, props, validCount); + + /* detect nest box */ + nms(validCount, predictions, output); + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) { + if (output[0][i] == -1) { + continue; + } + int n = output[0][i]; + int topClassScoreIndex = output[1][i]; + + int x1 = (int)(predictions[n * 4 + 1] * width); + int y1 = (int)(predictions[n * 4 + 0] * heigh); + int x2 = (int)(predictions[n * 4 + 3] * width); + int y2 = (int)(predictions[n * 4 + 2] * heigh); + // There are a bug show toothbrush always + if (x1 == 0 && x2 == 0 && y1 == 0 && y2 == 0) + continue; + char* label = labels[topClassScoreIndex]; + + group->results[last_count].box.left = x1; + group->results[last_count].box.top = y1; + group->results[last_count].box.right = x2; + group->results[last_count].box.bottom = y2; + group->results[last_count].prop = props[i]; + memcpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE); + + // printf("ssd result %2d: (%4d, %4d, %4d, %4d), %4.2f, %s\n", i, x1, y1, x2, y2, props[i], label); + last_count++; + } + + group->count = last_count; + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.h new file mode 100644 index 0000000..ff910a0 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_ssd_demo/src/ssd.h @@ -0,0 +1,58 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SSD_H_ +#define _SSD_H_ + +#define OBJ_NAME_MAX_SIZE 16 +#define OBJ_NUMB_MAX_SIZE 64 + +typedef struct _BOX_RECT { + int left; + int right; + int top; + int bottom; +} BOX_RECT; + +typedef struct __detect_result_t { + char name[OBJ_NAME_MAX_SIZE]; + BOX_RECT box; + float prop; +} detect_result_t; + +typedef struct _detect_result_group_t { + int id; + int count; + detect_result_t results[OBJ_NUMB_MAX_SIZE]; +} detect_result_group_t; + +#define IMG_CHANNEL 3 +#define MODEL_INPUT_SIZE 300 +#define NUM_RESULTS 1917 +#define NUM_CLASS 91 + +#define Y_SCALE 10.0f +#define X_SCALE 10.0f +#define H_SCALE 5.0f +#define W_SCALE 5.0f + +int loadLabelName(const char* locationFilename, char* labels[]); + +int loadBoxPriors(const char* locationFilename, float (*boxPriors)[NUM_RESULTS]); + +int postProcessSSD(float * predictions, float *output_classes, int width, int heigh, detect_result_group_t *group); + +int64_t getCurrentTimeUs(); + +#endif diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/CMakeLists.txt new file mode 100644 index 0000000..d7ea9d6 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_yolov5_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s -O3") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rga +set(RGA_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/rga) +include_directories(${RGA_DIR}/include) + +# drm +set(DRM_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/drm) +include_directories(${DRM_DIR}/include) +include_directories(${DRM_DIR}/include/libdrm) + +include_directories(${CMAKE_SOURCE_DIR}/include) + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_yolov5_demo + src/drm_func.c + src/rga_func.c + src/postprocess.cc + src/main.cc + ) + +target_link_libraries(rknn_yolov5_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_yolov5_demo) +install(TARGETS rknn_yolov5_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/README.md new file mode 100644 index 0000000..e8086cc --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/README.md @@ -0,0 +1,64 @@ +# Yolo-v5 demo + +## 导出rknn模型 + +请参考 https://github.com/airockchip/rknn_model_zoo/tree/main/models/vision/object_detection/yolov5-pytorch + + + +## 注意事项: + +1. 使用rknn-toolkit版本大于等于1.7.0。 +2. 本Demo只支持8比特非对称量化的rknn模型推理。 +3. 切换成自己训练的模型时,请注意对齐anchor等后处理参数,否则会导致后处理解析出错。 +4. 官网和rk预训练模型都是检测80类的目标,如果自己训练的模型,自行更改include/postprocess.h中的OBJ_CLASS_NUM以及NMS_THRESH,BOX_THRESH后处理参数后再编译。 +5. 由于硬件限制,该demo的模型默认把 yolov5 模型的后处理部分,移至cpu实现。本demo附带的模型均使用relu为激活函数,相比silu激活函数精度略微下降,推理速度更快。 +6. 关于加载时间:model目录下均是预编译rknn模型,加载速度比非预编译rknn模型快。convert_rknn_demo目录下的转换脚本生成非预编译rknn模型,如需重新生成预编译rknn模型,请参考rknn-toolkit的User Guide. + + +## Aarch64 Linux Demo + +### 编译 + +根据指定平台修改`build.sh`中的交叉编译器所在目录的路径`GCC_COMPILER`,例如修改成 + +```sh +GCC_COMPILER=~/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf +``` + +然后执行: + +```sh +./build.sh +``` + +### 推送执行文件到板子 + +将 install/rknn_yolov5_demo 拷贝到板子的/userdata/目录. + +- 如果使用rockchip的EVB板子,可以使用adb将文件推到板子上: + +```sh +adb push install/rknn_yolov5_demo /userdata/ +``` + +如果使用其他板子,可以使用scp等方式将install/rknn_yolov5_demo拷贝到板子的/userdata/目录 + +## 运行 + +```sh +adb shell +cd /userdata/rknn_yolov5_demo/ +``` + +- rk180x + +```sh +./run_rk180x.sh +``` + +- rv1109/rv1126 + +```sh +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/build.sh new file mode 100644 index 0000000..334bf73 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_yolov5_demo/ +cp run_rv1109_rv1126.sh install/rknn_yolov5_demo/ \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/bus.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/bus.jpg new file mode 100644 index 0000000..d8ef30b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/bus.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/dataset.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/dataset.txt new file mode 100644 index 0000000..a81b1f8 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/dataset.txt @@ -0,0 +1 @@ +bus.jpg diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx2rknn.py b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx2rknn.py new file mode 100644 index 0000000..9f4ec6e --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx2rknn.py @@ -0,0 +1,40 @@ +from rknn.api import RKNN + +ONNX_MODEL = './onnx_models/yolov5s_rm_transpose.onnx' +# platform="rk1808" +platform = "rv1109" +RKNN_MODEL = 'yolov5s_relu_{}_out_opt.rknn'.format(platform) + + +if __name__ == '__main__': + + add_perm = False # 如果设置成True,则将模型输入layout修改成NHWC + # Create RKNN object + rknn = RKNN(verbose=True) + + # pre-process config + print('--> config model') + rknn.config(batch_size=1, mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], reorder_channel='0 1 2', target_platform=[platform], + force_builtin_perm=add_perm, output_optimize=1) + print('done') + + # Load tensorflow model + print('--> Loading model') + ret = rknn.load_onnx(model=ONNX_MODEL) + if ret != 0: + print('Load resnet50v2 failed!') + exit(ret) + print('done') + + # Build model + print('--> Building model') + ret = rknn.build(do_quantization=True, dataset='./dataset.txt') + if ret != 0: + print('Build resnet50 failed!') + exit(ret) + print('done') + + # rknn.export_rknn_precompile_model(RKNN_MODEL) + rknn.export_rknn(RKNN_MODEL) + + rknn.release() diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/bus.jpg b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/bus.jpg new file mode 100644 index 0000000..d8ef30b Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/bus.jpg differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/dataset.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/dataset.txt new file mode 100644 index 0000000..a81b1f8 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/dataset.txt @@ -0,0 +1 @@ +bus.jpg diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/yolov5s_rm_transpose.onnx b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/yolov5s_rm_transpose.onnx new file mode 100644 index 0000000..0a340ed Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/convert_rknn_demo/yolov5/onnx_models/yolov5s_rm_transpose.onnx differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/drm_func.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/drm_func.h new file mode 100644 index 0000000..0a9e3b3 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/drm_func.h @@ -0,0 +1,53 @@ +#ifndef __DRM_FUNC_H__ +#define __DRM_FUNC_H__ +#include +#include +#include +#include +#include // open function +#include // close function +#include +#include + + +#include +#include "libdrm/drm_fourcc.h" +#include "xf86drm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (* FUNC_DRM_IOCTL)(int fd, unsigned long request, void *arg); + +typedef struct _drm_context{ + void *drm_handle; + FUNC_DRM_IOCTL io_func; +} drm_context; + +/* memory type definitions. */ +enum drm_rockchip_gem_mem_type +{ + /* Physically Continuous memory and used as default. */ + ROCKCHIP_BO_CONTIG = 1 << 0, + /* cachable mapping. */ + ROCKCHIP_BO_CACHABLE = 1 << 1, + /* write-combine mapping. */ + ROCKCHIP_BO_WC = 1 << 2, + ROCKCHIP_BO_SECURE = 1 << 3, + ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE | + ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE +}; + +int drm_init(drm_context *drm_ctx); + +void* drm_buf_alloc(drm_context *drm_ctx,int drm_fd, int TexWidth, int TexHeight,int bpp,int *fd,unsigned int *handle,size_t *actual_size); + +int drm_buf_destroy(drm_context *drm_ctx,int drm_fd,int buf_fd, int handle,void *drm_buf,size_t size); + +void drm_deinit(drm_context *drm_ctx, int drm_fd); + +#ifdef __cplusplus +} +#endif +#endif /*__DRM_FUNC_H__*/ \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/postprocess.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/postprocess.h new file mode 100644 index 0000000..e474505 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/postprocess.h @@ -0,0 +1,40 @@ +#ifndef _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ +#define _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ + +#include + +#define OBJ_NAME_MAX_SIZE 16 +#define OBJ_NUMB_MAX_SIZE 64 +#define OBJ_CLASS_NUM 80 +#define NMS_THRESH 0.6 +#define BOX_THRESH 0.5 +#define PROP_BOX_SIZE (5+OBJ_CLASS_NUM) + +typedef struct _BOX_RECT +{ + int left; + int right; + int top; + int bottom; +} BOX_RECT; + +typedef struct __detect_result_t +{ + char name[OBJ_NAME_MAX_SIZE]; + BOX_RECT box; + float prop; +} detect_result_t; + +typedef struct _detect_result_group_t +{ + int id; + int count; + detect_result_t results[OBJ_NUMB_MAX_SIZE]; +} detect_result_group_t; + +int post_process(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector &qnt_zps, std::vector &qnt_scales, + detect_result_group_t *group); + +#endif //_RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/rga_func.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/rga_func.h new file mode 100644 index 0000000..beeb441 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/include/rga_func.h @@ -0,0 +1,33 @@ +#ifndef __RGA_FUNC_H__ +#define __RGA_FUNC_H__ + +#include +#include "RgaApi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(* FUNC_RGA_INIT)(); +typedef void(* FUNC_RGA_DEINIT)(); +typedef int(* FUNC_RGA_BLIT)(rga_info_t *, rga_info_t *, rga_info_t *); + +typedef struct _rga_context{ + void *rga_handle; + FUNC_RGA_INIT init_func; + FUNC_RGA_DEINIT deinit_func; + FUNC_RGA_BLIT blit_func; +} rga_context; + +int RGA_init(rga_context* rga_ctx); + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h); + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h); + +int RGA_deinit(rga_context* rga_ctx); + +#ifdef __cplusplus +} +#endif +#endif/*__RGA_FUNC_H__*/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/bus.bmp b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/bus.bmp new file mode 100644 index 0000000..82df079 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/bus.bmp differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/coco_80_labels_list.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/coco_80_labels_list.txt new file mode 100644 index 0000000..941cb4e --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/coco_80_labels_list.txt @@ -0,0 +1,80 @@ +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +dining table +toilet +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/rk180x/yolov5s_relu_rk180x_out_opt.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/rk180x/yolov5s_relu_rk180x_out_opt.rknn new file mode 100644 index 0000000..b240c62 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/rk180x/yolov5s_relu_rk180x_out_opt.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_out_opt.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_out_opt.rknn new file mode 100644 index 0000000..0692c1c Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_out_opt.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/run_rk180x.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/run_rk180x.sh new file mode 100644 index 0000000..f8492f8 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/run_rk180x.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +chip_dir="rk180x" +./rknn_yolov5_demo model/${chip_dir}/yolov5s_relu_rk180x_out_opt.rknn model/bus.bmp + + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..54d81fe --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/run_rv1109_rv1126.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +./rknn_yolov5_demo model/${chip_dir}/yolov5s_relu_rv1109_rv1126_out_opt.rknn model/bus.bmp + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/drm_func.c b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/drm_func.c new file mode 100644 index 0000000..2992326 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/drm_func.c @@ -0,0 +1,163 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "drm_func.h" + +#include + +int drm_init(drm_context* drm_ctx) +{ + static const char* card = "/dev/dri/card0"; + int flag = O_RDWR; + int drm_fd = -1; + + drm_fd = open(card, flag); + if (drm_fd < 0) { + printf("failed to open %s\n", card); + return -1; + } + + drm_ctx->drm_handle = dlopen("/usr/lib/libdrm.so", RTLD_LAZY); + if (!drm_ctx->drm_handle) { + printf("failed to dlopen /usr/lib/libdrm.so\n"); + drm_deinit(drm_ctx, drm_fd); + return -1; + } + + drm_ctx->io_func = (FUNC_DRM_IOCTL)dlsym(drm_ctx->drm_handle, "drmIoctl"); + if (drm_ctx->io_func == NULL) { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + drm_deinit(drm_ctx, drm_fd); + printf("failed to dlsym drmIoctl\n"); + return -1; + } + return drm_fd; +} + +void drm_deinit(drm_context* drm_ctx, int drm_fd) +{ + if (drm_ctx->drm_handle) { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + } + if (drm_fd > 0) { + close(drm_fd); + } +} + +void* drm_buf_alloc(drm_context* drm_ctx, int drm_fd, int TexWidth, int TexHeight, int bpp, int* fd, + unsigned int* handle, size_t* actual_size) +{ + int ret; + if (drm_ctx == NULL) { + printf("drm context is unvalid\n"); + return NULL; + } + char* map = NULL; + + void* vir_addr = NULL; + struct drm_prime_handle fd_args; + struct drm_mode_map_dumb mmap_arg; + struct drm_mode_destroy_dumb destory_arg; + + struct drm_mode_create_dumb alloc_arg; + + memset(&alloc_arg, 0, sizeof(alloc_arg)); + alloc_arg.bpp = bpp; + alloc_arg.width = TexWidth; + alloc_arg.height = TexHeight; + // alloc_arg.flags = ROCKCHIP_BO_CONTIG; + + //获取handle和size + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); + if (ret) { + printf("failed to create dumb buffer: %s\n", strerror(errno)); + return NULL; + } + if (handle != NULL) { + *handle = alloc_arg.handle; + } + if (actual_size != NULL) { + *actual_size = alloc_arg.size; + } + // printf("create width=%u, height=%u, bpp=%u, size=%lu dumb + // buffer\n",alloc_arg.width,alloc_arg.height,alloc_arg.bpp,alloc_arg.size); printf("out handle= + // %d\n",alloc_arg.handle); + + //获取fd + memset(&fd_args, 0, sizeof(fd_args)); + fd_args.fd = -1; + fd_args.handle = alloc_arg.handle; + ; + fd_args.flags = 0; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &fd_args); + if (ret) { + printf("rk-debug handle_to_fd failed ret=%d,err=%s, handle=%x \n", ret, strerror(errno), fd_args.handle); + return NULL; + } + // printf("out fd = %d, drm fd: %d\n",fd_args.fd,drm_fd); + if (fd != NULL) { + *fd = fd_args.fd; + } + + //获取虚拟地址 + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = alloc_arg.handle; + + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); + if (ret) { + printf("failed to create map dumb: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + vir_addr = map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, mmap_arg.offset); + if (map == MAP_FAILED) { + printf("failed to mmap buffer: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + // printf("alloc map=%x \n",map); + return vir_addr; +destory_dumb: + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = alloc_arg.handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d\n", ret); + return vir_addr; +} + +int drm_buf_destroy(drm_context* drm_ctx, int drm_fd, int buf_fd, int handle, void* drm_buf, size_t size) +{ + int ret = -1; + if (drm_buf == NULL) { + printf("drm buffer is NULL\n"); + return -1; + } + + munmap(drm_buf, size); + + struct drm_mode_destroy_dumb destory_arg; + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d, error=%s\n", ret, strerror(errno)); + if (buf_fd > 0) { + close(buf_fd); + } + + return ret; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/main.cc new file mode 100644 index 0000000..db229bc --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/main.cc @@ -0,0 +1,417 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include + +#include + +#define _BASETSD_H + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#undef cimg_display +#define cimg_display 0 +#include "CImg/CImg.h" +#include "drm_func.h" +#include "postprocess.h" +#include "rga_func.h" +#include "rknn_api.h" + +#define PERF_WITH_POST 1 + +using namespace cimg_library; +/*------------------------------------------- + Functions +-------------------------------------------*/ + +inline const char* get_type_string(rknn_tensor_type type) +{ + switch (type) { + case RKNN_TENSOR_FLOAT32: + return "FP32"; + case RKNN_TENSOR_FLOAT16: + return "FP16"; + case RKNN_TENSOR_INT8: + return "INT8"; + case RKNN_TENSOR_UINT8: + return "UINT8"; + case RKNN_TENSOR_INT16: + return "INT16"; + default: + return "UNKNOW"; + } +} + +inline const char* get_qnt_type_string(rknn_tensor_qnt_type type) +{ + switch (type) { + case RKNN_TENSOR_QNT_NONE: + return "NONE"; + case RKNN_TENSOR_QNT_DFP: + return "DFP"; + case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + return "AFFINE"; + default: + return "UNKNOW"; + } +} + +inline const char* get_format_string(rknn_tensor_format fmt) +{ + switch (fmt) { + case RKNN_TENSOR_NCHW: + return "NCHW"; + case RKNN_TENSOR_NHWC: + return "NHWC"; + default: + return "UNKNOW"; + } +} + +static void dump_tensor_attr(rknn_tensor_attr* attr) +{ + printf(" index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, " + "zp=%d, scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, get_format_string(attr->fmt), get_type_string(attr->type), + get_qnt_type_string(attr->qnt_type), attr->zp, attr->scale); +} + +double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); } + +static unsigned char* load_data(FILE* fp, size_t ofst, size_t sz) +{ + unsigned char* data; + int ret; + + data = NULL; + + if (NULL == fp) { + return NULL; + } + + ret = fseek(fp, ofst, SEEK_SET); + if (ret != 0) { + printf("blob seek failure.\n"); + return NULL; + } + + data = (unsigned char*)malloc(sz); + if (data == NULL) { + printf("buffer malloc failure.\n"); + return NULL; + } + ret = fread(data, 1, sz, fp); + return data; +} + +static unsigned char* load_model(const char* filename, int* model_size) +{ + FILE* fp; + unsigned char* data; + + fp = fopen(filename, "rb"); + if (NULL == fp) { + printf("Open file %s failed.\n", filename); + return NULL; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + + data = load_data(fp, 0, size); + + fclose(fp); + + *model_size = size; + return data; +} + +static int saveFloat(const char* file_name, float* output, int element_size) +{ + FILE* fp; + fp = fopen(file_name, "w"); + for (int i = 0; i < element_size; i++) { + fprintf(fp, "%.6f\n", output[i]); + } + fclose(fp); + return 0; +} + +static unsigned char* load_image(const char* image_path, int* org_height, int* org_width, int* org_ch, + rknn_tensor_attr* input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char* image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) { + printf("load image failed!\n"); + return NULL; + } + *org_width = width; + *org_height = height; + *org_ch = channel; + + return image_data; +} + +/*------------------------------------------- + Main Functions +-------------------------------------------*/ +int main(int argc, char** argv) +{ + int status = 0; + char* model_name = NULL; + rknn_context ctx; + void* drm_buf = NULL; + int drm_fd = -1; + int buf_fd = -1; // converted from buffer handle + unsigned int handle; + size_t actual_size = 0; + int img_width = 0; + int img_height = 0; + int img_channel = 0; + rga_context rga_ctx; + drm_context drm_ctx; + const float nms_threshold = NMS_THRESH; + const float box_conf_threshold = BOX_THRESH; + struct timeval start_time, stop_time; + int ret; + memset(&rga_ctx, 0, sizeof(rga_context)); + memset(&drm_ctx, 0, sizeof(drm_context)); + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + printf("post process config: box_conf_threshold = %.2f, nms_threshold = %.2f\n", box_conf_threshold, nms_threshold); + + model_name = (char*)argv[1]; + char* image_name = argv[2]; + + if (strstr(image_name, ".jpg") != NULL || strstr(image_name, ".png") != NULL) { + printf("Error: read %s failed! only support .bmp format image\n", image_name); + return -1; + } + + /* Create the neural network */ + printf("Loading mode...\n"); + int model_data_size = 0; + unsigned char* model_data = load_model(model_name, &model_data_size); + ret = rknn_init(&ctx, model_data, model_data_size, 0); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, sizeof(rknn_sdk_version)); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("sdk version: %s driver version: %s\n", version.api_version, version.drv_version); + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output); + + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr)); + if (ret < 0) { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + dump_tensor_attr(&(input_attrs[i])); + } + + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr)); + dump_tensor_attr(&(output_attrs[i])); + if (output_attrs[i].qnt_type != RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC || output_attrs[i].type != RKNN_TENSOR_UINT8) { + fprintf(stderr, + "The Demo required for a Affine asymmetric u8 quantized rknn model, but output quant type is %s, output " + "data type is %s\n", + get_qnt_type_string(output_attrs[i].qnt_type), get_type_string(output_attrs[i].type)); + return -1; + } + } + + int channel = 3; + int width = 0; + int height = 0; + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) { + printf("model is NCHW input fmt\n"); + width = input_attrs[0].dims[0]; + height = input_attrs[0].dims[1]; + } else { + printf("model is NHWC input fmt\n"); + width = input_attrs[0].dims[1]; + height = input_attrs[0].dims[2]; + } + + printf("model input height=%d, width=%d, channel=%d\n", height, width, channel); + + // Load image + CImg img(image_name); + unsigned char* input_data = NULL; + input_data = load_image(image_name, &img_height, &img_width, &img_channel, &input_attrs[0]); + if (!input_data) { + return -1; + } + + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = width * height * channel; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].pass_through = 0; + + // DRM alloc buffer + drm_fd = drm_init(&drm_ctx); + drm_buf = drm_buf_alloc(&drm_ctx, drm_fd, img_width, img_height, channel * 8, &buf_fd, &handle, &actual_size); + memcpy(drm_buf, input_data, img_width * img_height * channel); + void* resize_buf = malloc(height * width * channel); + + // init rga context + RGA_init(&rga_ctx); + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + inputs[0].buf = resize_buf; + gettimeofday(&start_time, NULL); + rknn_inputs_set(ctx, io_num.n_input, inputs); + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) { + outputs[i].want_float = 0; + } + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + gettimeofday(&stop_time, NULL); + printf("once run use %f ms\n", (__get_us(stop_time) - __get_us(start_time)) / 1000); + + // post process + float scale_w = (float)width / img_width; + float scale_h = (float)height / img_height; + + detect_result_group_t detect_result_group; + std::vector out_scales; + std::vector out_zps; + for (int i = 0; i < io_num.n_output; ++i) { + out_scales.push_back(output_attrs[i].scale); + out_zps.push_back(output_attrs[i].zp); + } + post_process((uint8_t*)outputs[0].buf, (uint8_t*)outputs[1].buf, (uint8_t*)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); + + // Draw Objects + char text[256]; + const unsigned char blue[] = {0, 0, 255}; + const unsigned char white[] = {255, 255, 255}; + for (int i = 0; i < detect_result_group.count; i++) { + detect_result_t* det_result = &(detect_result_group.results[i]); + sprintf(text, "%s %.2f", det_result->name, det_result->prop); + printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top, + det_result->box.right, det_result->box.bottom, det_result->prop); + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + // draw box + img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U); + img.draw_text(x1, y1 - 12, text, white); + } + img.save("./out.bmp"); + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + + // loop test + int test_count = 10; + gettimeofday(&start_time, NULL); + for (int i = 0; i < test_count; ++i) { + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + rknn_inputs_set(ctx, io_num.n_input, inputs); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); +#if PERF_WITH_POST + post_process((uint8_t*)outputs[0].buf, (uint8_t*)outputs[1].buf, (uint8_t*)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); +#endif + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + } + gettimeofday(&stop_time, NULL); + printf("loop count = %d , average run %f ms\n", test_count, + (__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count); + + // release + ret = rknn_destroy(ctx); + drm_buf_destroy(&drm_ctx, drm_fd, buf_fd, handle, drm_buf, actual_size); + + drm_deinit(&drm_ctx, drm_fd); + RGA_deinit(&rga_ctx); + if (model_data) { + free(model_data); + } + + if (resize_buf) { + free(resize_buf); + } + stbi_image_free(input_data); + + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/postprocess.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/postprocess.cc new file mode 100644 index 0000000..793efd4 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/postprocess.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "postprocess.h" + + +#define LABEL_NALE_TXT_PATH "./model/coco_80_labels_list.txt" + +static char* labels[OBJ_CLASS_NUM]; + +const int anchor0[6] = {10, 13, 16, 30, 33, 23}; +const int anchor1[6] = {30, 61, 62, 45, 59, 119}; +const int anchor2[6] = {116, 90, 156, 198, 373, 326}; + +inline static int clamp(float val, int min, int max) { return val > min ? (val < max ? val : max) : min; } + +char* readLine(FILE* fp, char* buffer, int* len) +{ + int ch; + int i = 0; + size_t buff_len = 0; + + buffer = (char*)malloc(buff_len + 1); + if (!buffer) + return NULL; // Out of memory + + while ((ch = fgetc(fp)) != '\n' && ch != EOF) { + buff_len++; + void* tmp = realloc(buffer, buff_len + 1); + if (tmp == NULL) { + free(buffer); + return NULL; // Out of memory + } + buffer = (char*)tmp; + + buffer[i] = (char)ch; + i++; + } + buffer[i] = '\0'; + + *len = buff_len; + + // Detect end + if (ch == EOF && (i == 0 || ferror(fp))) { + free(buffer); + return NULL; + } + return buffer; +} + +int readLines(const char* fileName, char* lines[], int max_line) +{ + FILE* file = fopen(fileName, "r"); + char* s; + int i = 0; + int n = 0; + while ((s = readLine(file, s, &n)) != NULL) { + lines[i++] = s; + if (i >= max_line) + break; + } + return i; +} + +int loadLabelName(const char* locationFilename, char* label[]) +{ + printf("loadLabelName %s\n", locationFilename); + readLines(locationFilename, label, OBJ_CLASS_NUM); + return 0; +} + +static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, + float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1) + 1.0); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1) + 1.0); + float i = w * h; + float u = (xmax0 - xmin0 + 1.0) * (ymax0 - ymin0 + 1.0) + (xmax1 - xmin1 + 1.0) * (ymax1 - ymin1 + 1.0) - i; + return u <= 0.f ? 0.f : (i / u); +} + +static int nms(int validCount, std::vector& outputLocations, std::vector classIds, std::vector& order, + int filterId, float threshold) +{ + for (int i = 0; i < validCount; ++i) { + if (order[i] == -1 || classIds[i] != filterId) { + continue; + } + int n = order[i]; + for (int j = i + 1; j < validCount; ++j) { + int m = order[j]; + if (m == -1 || classIds[i] != filterId) { + continue; + } + float xmin0 = outputLocations[n * 4 + 0]; + float ymin0 = outputLocations[n * 4 + 1]; + float xmax0 = outputLocations[n * 4 + 0] + outputLocations[n * 4 + 2]; + float ymax0 = outputLocations[n * 4 + 1] + outputLocations[n * 4 + 3]; + + float xmin1 = outputLocations[m * 4 + 0]; + float ymin1 = outputLocations[m * 4 + 1]; + float xmax1 = outputLocations[m * 4 + 0] + outputLocations[m * 4 + 2]; + float ymax1 = outputLocations[m * 4 + 1] + outputLocations[m * 4 + 3]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou > threshold) { + order[j] = -1; + } + } + } + return 0; +} + +static int quick_sort_indice_inverse(std::vector& input, int left, int right, std::vector& indices) +{ + float key; + int key_index; + int low = left; + int high = right; + if (left < right) { + key_index = indices[left]; + key = input[left]; + while (low < high) { + while (low < high && input[high] <= key) { + high--; + } + input[low] = input[high]; + indices[low] = indices[high]; + while (low < high && input[low] >= key) { + low++; + } + input[high] = input[low]; + indices[high] = indices[low]; + } + input[low] = key; + indices[low] = key_index; + quick_sort_indice_inverse(input, left, low - 1, indices); + quick_sort_indice_inverse(input, low + 1, right, indices); + } + return low; +} + +static float sigmoid(float x) { return 1.0 / (1.0 + expf(-x)); } + +static float unsigmoid(float y) { return -1.0 * logf((1.0 / y) - 1.0); } + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + return f; +} + +static uint8_t qnt_f32_to_affine(float f32, uint32_t zp, float scale) +{ + float dst_val = (f32 / scale) + zp; + uint8_t res = (uint8_t)__clip(dst_val, 0, 255); + return res; +} + +static float deqnt_affine_to_f32(uint8_t qnt, uint32_t zp, float scale) { return ((float)qnt - (float)zp) * scale; } + +static int process(uint8_t* input, int* anchor, int grid_h, int grid_w, int height, int width, int stride, + std::vector& boxes, std::vector& objProbs, std::vector& classId, float threshold, + uint32_t zp, float scale) +{ + int validCount = 0; + int grid_len = grid_h * grid_w; + float thres = unsigmoid(threshold); + uint8_t thres_u8 = qnt_f32_to_affine(thres, zp, scale); + for (int a = 0; a < 3; a++) { + for (int i = 0; i < grid_h; i++) { + for (int j = 0; j < grid_w; j++) { + uint8_t box_confidence = input[(PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j]; + if (box_confidence >= thres_u8) { + int offset = (PROP_BOX_SIZE * a) * grid_len + i * grid_w + j; + uint8_t* in_ptr = input + offset; + float box_x = sigmoid(deqnt_affine_to_f32(*in_ptr, zp, scale)) * 2.0 - 0.5; + float box_y = sigmoid(deqnt_affine_to_f32(in_ptr[grid_len], zp, scale)) * 2.0 - 0.5; + float box_w = sigmoid(deqnt_affine_to_f32(in_ptr[2 * grid_len], zp, scale)) * 2.0; + float box_h = sigmoid(deqnt_affine_to_f32(in_ptr[3 * grid_len], zp, scale)) * 2.0; + box_x = (box_x + j) * (float)stride; + box_y = (box_y + i) * (float)stride; + box_w = box_w * box_w * (float)anchor[a * 2]; + box_h = box_h * box_h * (float)anchor[a * 2 + 1]; + box_x -= (box_w / 2.0); + box_y -= (box_h / 2.0); + boxes.push_back(box_x); + boxes.push_back(box_y); + boxes.push_back(box_w); + boxes.push_back(box_h); + + uint8_t maxClassProbs = in_ptr[5 * grid_len]; + int maxClassId = 0; + for (int k = 1; k < OBJ_CLASS_NUM; ++k) { + uint8_t prob = in_ptr[(5 + k) * grid_len]; + if (prob > maxClassProbs) { + maxClassId = k; + maxClassProbs = prob; + } + } + objProbs.push_back(sigmoid(deqnt_affine_to_f32(maxClassProbs, zp, scale))); + classId.push_back(maxClassId); + validCount++; + } + } + } + } + return validCount; +} + +int post_process(uint8_t* input0, uint8_t* input1, uint8_t* input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector& qnt_zps, std::vector& qnt_scales, detect_result_group_t* group) +{ + static int init = -1; + if (init == -1) { + int ret = 0; + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) { + return -1; + } + + init = 0; + } + memset(group, 0, sizeof(detect_result_group_t)); + + std::vector filterBoxes; + std::vector objProbs; + std::vector classId; + + // stride 8 + int stride0 = 8; + int grid_h0 = model_in_h / stride0; + int grid_w0 = model_in_w / stride0; + int validCount0 = 0; + validCount0 = process(input0, (int*)anchor0, grid_h0, grid_w0, model_in_h, model_in_w, stride0, filterBoxes, objProbs, + classId, conf_threshold, qnt_zps[0], qnt_scales[0]); + + // stride 16 + int stride1 = 16; + int grid_h1 = model_in_h / stride1; + int grid_w1 = model_in_w / stride1; + int validCount1 = 0; + validCount1 = process(input1, (int*)anchor1, grid_h1, grid_w1, model_in_h, model_in_w, stride1, filterBoxes, objProbs, + classId, conf_threshold, qnt_zps[1], qnt_scales[1]); + + // stride 32 + int stride2 = 32; + int grid_h2 = model_in_h / stride2; + int grid_w2 = model_in_w / stride2; + int validCount2 = 0; + validCount2 = process(input2, (int*)anchor2, grid_h2, grid_w2, model_in_h, model_in_w, stride2, filterBoxes, objProbs, + classId, conf_threshold, qnt_zps[2], qnt_scales[2]); + + int validCount = validCount0 + validCount1 + validCount2; + // no object detect + if (validCount <= 0) { + return 0; + } + + std::vector indexArray; + for (int i = 0; i < validCount; ++i) { + indexArray.push_back(i); + } + + quick_sort_indice_inverse(objProbs, 0, validCount - 1, indexArray); + + std::set class_set(std::begin(classId), std::end(classId)); + + for (auto c : class_set) { + nms(validCount, filterBoxes, classId, indexArray, c, nms_threshold); + } + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) { + if (indexArray[i] == -1 || last_count >= OBJ_NUMB_MAX_SIZE) { + continue; + } + int n = indexArray[i]; + + float x1 = filterBoxes[n * 4 + 0]; + float y1 = filterBoxes[n * 4 + 1]; + float x2 = x1 + filterBoxes[n * 4 + 2]; + float y2 = y1 + filterBoxes[n * 4 + 3]; + int id = classId[n]; + float obj_conf = objProbs[i]; + + group->results[last_count].box.left = (int)(clamp(x1, 0, model_in_w) / scale_w); + group->results[last_count].box.top = (int)(clamp(y1, 0, model_in_h) / scale_h); + group->results[last_count].box.right = (int)(clamp(x2, 0, model_in_w) / scale_w); + group->results[last_count].box.bottom = (int)(clamp(y2, 0, model_in_h) / scale_h); + group->results[last_count].prop = obj_conf; + char* label = labels[id]; + strncpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE); + + // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, + // group->results[last_count].box.top, + // group->results[last_count].box.right, group->results[last_count].box.bottom, label); + last_count++; + } + group->count = last_count; + + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/rga_func.c b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/rga_func.c new file mode 100644 index 0000000..c9e8a90 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_yolov5_demo/src/rga_func.c @@ -0,0 +1,108 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rga_func.h" + +int RGA_init(rga_context* rga_ctx) +{ + rga_ctx->rga_handle = dlopen("/usr/lib/librga.so", RTLD_LAZY); + if (!rga_ctx->rga_handle) { + printf("dlopen /usr/lib/librga.so failed\n"); + return -1; + } + rga_ctx->init_func = (FUNC_RGA_INIT)dlsym(rga_ctx->rga_handle, "c_RkRgaInit"); + rga_ctx->deinit_func = (FUNC_RGA_DEINIT)dlsym(rga_ctx->rga_handle, "c_RkRgaDeInit"); + rga_ctx->blit_func = (FUNC_RGA_BLIT)dlsym(rga_ctx->rga_handle, "c_RkRgaBlit"); + rga_ctx->init_func(); + return 0; +} + +void img_resize_fast(rga_context* rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h) +{ + // printf("rga use fd, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = src_fd; + src.mmuFlag = 1; + // src.virAddr = (void *)psrc; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 0; + +#if defined(__arm__) + dst.phyAddr = (void*)((uint32_t)dst_phys); +#else + dst.phyAddr = (void*)dst_phys; +#endif + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +void img_resize_slow(rga_context* rga_ctx, void* src_virt, int src_w, int src_h, void* dst_virt, int dst_w, int dst_h) +{ + // printf("rga use virtual, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = -1; + src.mmuFlag = 1; + src.virAddr = (void*)src_virt; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 1; + dst.virAddr = dst_virt; + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +int RGA_deinit(rga_context* rga_ctx) +{ + if (rga_ctx->rga_handle) { + dlclose(rga_ctx->rga_handle); + rga_ctx->rga_handle = NULL; + } +} \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/CMakeLists.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/CMakeLists.txt new file mode 100644 index 0000000..cdcfc38 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_zero_copy_demo_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s -O3") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rga +set(RGA_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/rga) +include_directories(${RGA_DIR}/include) + +# drm +set(DRM_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/drm) +include_directories(${DRM_DIR}/include) +include_directories(${DRM_DIR}/include/libdrm) + +include_directories(${CMAKE_SOURCE_DIR}/include) + +# rknn api +set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../librknn_api) +include_directories(${RKNN_API_PATH}/include) +set(RKNN_API_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknn_api.so) + +#stb +include_directories(${CMAKE_SOURCE_DIR}/../3rdparty/) + +set(CMAKE_INSTALL_RPATH "lib") + +add_executable(rknn_zero_copy_demo + src/drm_func.c + src/rga_func.c + src/postprocess.cc + src/main.cc + ) + +target_link_libraries(rknn_zero_copy_demo + ${RKNN_API_LIB} + dl +) + +# install target and libraries +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_zero_copy_demo) +install(TARGETS rknn_zero_copy_demo DESTINATION ./) +install(DIRECTORY model DESTINATION ./) +install(PROGRAMS ${RKNN_API_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/README.md b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/README.md new file mode 100644 index 0000000..500cb9b --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/README.md @@ -0,0 +1,88 @@ +# Zero copy demo + +In specific case, the number of input data copies can be reduced to 0, that is, zero copy. For example, when the RKNN model is asymmetric quantization, the quantization data type is uint8, the mean value of the 3 channels is the same integer and the scaling factor is the same, the normalization and quantization can be omitted. + +This demo compares the performance difference between zero-copy and normal process inference. + +```txt +a. zero-copy inference uses the "rknn_inputs_map" interface + +b. normal inference uses the "rknn_inputs_set" interface. +``` + +In the "model" directory, models without NHWC suffix have input in NCHW format, and models with NHWC suffix have input in NHWC format. The model with NHWC suffix is ​​exported using rknn-toolkit by adding the following parameters + +```python +rknn.config(force_builtin_perm=True) +``` + +Note: if it shows "**get unvalid input physical address, please extend in/out memory space**", you may need to modify the size of **CMA** in the **kernel's dts**, such as: + +``` +diff --git a/arch/arm/boot/dts/rv1126.dtsi b/arch/arm/boot/dts/rv1126.dtsi +index f5f0a07..1d0487c 100644 +--- a/arch/arm/boot/dts/rv1126.dtsi ++++ b/arch/arm/boot/dts/rv1126.dtsi +@@ -362,11 +362,17 @@ + #size-cells = <1>; + ranges; + ++ nouse@0 { ++ reg = <0x00000000 0x4000>; ++ no-map; ++ }; ++ ++ + linux,cma { + compatible = "shared-dma-pool"; + inactive; + reusable; +- size = <0x800000>; ++ size = <0x8000000>; + linux,cma-default; + }; + +``` + +This patch is only for RV1109/RV1126,if you use RK1808,you need to select the appropriate dts file to modify. + +## Build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +```sh +./build.sh +``` + +## Install + +Copy install/rknn_zero_copy_demo to the devices under /userdata/. + +- If you use rockchip's evb board, you can use the following way: + +Connect device and push the program and rknn model to `/userdata` + +```sh +adb push install/rknn_zero_copy_demo /userdata/ +``` + +- If your board has sshd service, you can use scp or other methods to copy the program and rknn model to the board. + +## Run + +```sh +adb shell +cd /userdata/rknn_zero_copy_demo/ +``` + +- RK1808/RK1806 + +```sh +./run_rk180x.sh +``` + +- RV1109/RV1126 + +```sh +./run_rv1109_rv1126.sh +``` diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/build.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/build.sh new file mode 100644 index 0000000..01da901 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +# GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - + +cp run_rk180x.sh install/rknn_zero_copy_demo/ +cp run_rv1109_rv1126.sh install/rknn_zero_copy_demo/ \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/drm_func.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/drm_func.h new file mode 100644 index 0000000..0a9e3b3 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/drm_func.h @@ -0,0 +1,53 @@ +#ifndef __DRM_FUNC_H__ +#define __DRM_FUNC_H__ +#include +#include +#include +#include +#include // open function +#include // close function +#include +#include + + +#include +#include "libdrm/drm_fourcc.h" +#include "xf86drm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (* FUNC_DRM_IOCTL)(int fd, unsigned long request, void *arg); + +typedef struct _drm_context{ + void *drm_handle; + FUNC_DRM_IOCTL io_func; +} drm_context; + +/* memory type definitions. */ +enum drm_rockchip_gem_mem_type +{ + /* Physically Continuous memory and used as default. */ + ROCKCHIP_BO_CONTIG = 1 << 0, + /* cachable mapping. */ + ROCKCHIP_BO_CACHABLE = 1 << 1, + /* write-combine mapping. */ + ROCKCHIP_BO_WC = 1 << 2, + ROCKCHIP_BO_SECURE = 1 << 3, + ROCKCHIP_BO_MASK = ROCKCHIP_BO_CONTIG | ROCKCHIP_BO_CACHABLE | + ROCKCHIP_BO_WC | ROCKCHIP_BO_SECURE +}; + +int drm_init(drm_context *drm_ctx); + +void* drm_buf_alloc(drm_context *drm_ctx,int drm_fd, int TexWidth, int TexHeight,int bpp,int *fd,unsigned int *handle,size_t *actual_size); + +int drm_buf_destroy(drm_context *drm_ctx,int drm_fd,int buf_fd, int handle,void *drm_buf,size_t size); + +void drm_deinit(drm_context *drm_ctx, int drm_fd); + +#ifdef __cplusplus +} +#endif +#endif /*__DRM_FUNC_H__*/ \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/postprocess.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/postprocess.h new file mode 100644 index 0000000..e474505 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/postprocess.h @@ -0,0 +1,40 @@ +#ifndef _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ +#define _RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ + +#include + +#define OBJ_NAME_MAX_SIZE 16 +#define OBJ_NUMB_MAX_SIZE 64 +#define OBJ_CLASS_NUM 80 +#define NMS_THRESH 0.6 +#define BOX_THRESH 0.5 +#define PROP_BOX_SIZE (5+OBJ_CLASS_NUM) + +typedef struct _BOX_RECT +{ + int left; + int right; + int top; + int bottom; +} BOX_RECT; + +typedef struct __detect_result_t +{ + char name[OBJ_NAME_MAX_SIZE]; + BOX_RECT box; + float prop; +} detect_result_t; + +typedef struct _detect_result_group_t +{ + int id; + int count; + detect_result_t results[OBJ_NUMB_MAX_SIZE]; +} detect_result_group_t; + +int post_process(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector &qnt_zps, std::vector &qnt_scales, + detect_result_group_t *group); + +#endif //_RKNN_ZERO_COPY_DEMO_POSTPROCESS_H_ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/rga_func.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/rga_func.h new file mode 100644 index 0000000..beeb441 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/include/rga_func.h @@ -0,0 +1,33 @@ +#ifndef __RGA_FUNC_H__ +#define __RGA_FUNC_H__ + +#include +#include "RgaApi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int(* FUNC_RGA_INIT)(); +typedef void(* FUNC_RGA_DEINIT)(); +typedef int(* FUNC_RGA_BLIT)(rga_info_t *, rga_info_t *, rga_info_t *); + +typedef struct _rga_context{ + void *rga_handle; + FUNC_RGA_INIT init_func; + FUNC_RGA_DEINIT deinit_func; + FUNC_RGA_BLIT blit_func; +} rga_context; + +int RGA_init(rga_context* rga_ctx); + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h); + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h); + +int RGA_deinit(rga_context* rga_ctx); + +#ifdef __cplusplus +} +#endif +#endif/*__RGA_FUNC_H__*/ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/bus.bmp b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/bus.bmp new file mode 100644 index 0000000..82df079 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/bus.bmp differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/coco_80_labels_list.txt b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/coco_80_labels_list.txt new file mode 100644 index 0000000..941cb4e --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/coco_80_labels_list.txt @@ -0,0 +1,80 @@ +person +bicycle +car +motorcycle +airplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +couch +potted plant +bed +dining table +toilet +tv +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x.rknn new file mode 100644 index 0000000..f8170d3 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x_NHWC.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x_NHWC.rknn new file mode 100644 index 0000000..0b8c68c Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rk180x/yolov5s_relu_rk180x_NHWC.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126.rknn new file mode 100644 index 0000000..eba83c6 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_NHWC.rknn b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_NHWC.rknn new file mode 100644 index 0000000..80f2a2e Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/model/rv1109_rv1126/yolov5s_relu_rv1109_rv1126_NHWC.rknn differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rk180x.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rk180x.sh new file mode 100644 index 0000000..d74398a --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rk180x.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +chip_dir="rk180x" +echo '********************* run zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rk180x_NHWC.rknn model/bus.bmp 1 +echo '********************* run non-zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rk180x.rknn model/bus.bmp 0 + + + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rv1109_rv1126.sh b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rv1109_rv1126.sh new file mode 100644 index 0000000..0701370 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/run_rv1109_rv1126.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +chip_dir="rv1109_rv1126" +echo '********************* run zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rv1109_rv1126_NHWC.rknn model/bus.bmp 1 + +echo '********************* run non-zero-copy model *********************' +./rknn_zero_copy_demo model/${chip_dir}/yolov5s_relu_rv1109_rv1126.rknn model/bus.bmp 0 + + + diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/drm_func.c b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/drm_func.c new file mode 100644 index 0000000..5811922 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/drm_func.c @@ -0,0 +1,175 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "drm_func.h" +#include + +int drm_init(drm_context *drm_ctx) +{ + static const char *card = "/dev/dri/card0"; + int flag = O_RDWR; + int drm_fd = -1; + + drm_fd = open(card, flag); + if (drm_fd < 0) + { + printf("failed to open %s\n", card); + return -1; + } + + drm_ctx->drm_handle = dlopen("/usr/lib/libdrm.so", RTLD_LAZY); + if (!drm_ctx->drm_handle) + { + printf("failed to dlopen /usr/lib/libdrm.so\n"); + drm_deinit(drm_ctx, drm_fd); + return -1; + } + + drm_ctx->io_func = (FUNC_DRM_IOCTL)dlsym(drm_ctx->drm_handle, "drmIoctl"); + if (drm_ctx->io_func == NULL) + { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + drm_deinit(drm_ctx, drm_fd); + printf("failed to dlsym drmIoctl\n"); + return -1; + } + return drm_fd; +} + +void drm_deinit(drm_context *drm_ctx, int drm_fd) +{ + if (drm_ctx->drm_handle) + { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + } + if (drm_fd > 0) + { + close(drm_fd); + } +} + +void *drm_buf_alloc(drm_context *drm_ctx, int drm_fd, int TexWidth, int TexHeight, int bpp, int *fd, unsigned int *handle, size_t *actual_size) +{ + int ret; + if (drm_ctx == NULL) + { + printf("drm context is unvalid\n"); + return NULL; + } + char *map = NULL; + + void *vir_addr = NULL; + struct drm_prime_handle fd_args; + struct drm_mode_map_dumb mmap_arg; + struct drm_mode_destroy_dumb destory_arg; + + struct drm_mode_create_dumb alloc_arg; + + memset(&alloc_arg, 0, sizeof(alloc_arg)); + alloc_arg.bpp = bpp; + alloc_arg.width = TexWidth; + alloc_arg.height = TexHeight; + // alloc_arg.flags = ROCKCHIP_BO_CONTIG; + + //获取handle和size + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); + if (ret) + { + printf("failed to create dumb buffer: %s\n", strerror(errno)); + return NULL; + } + if (handle != NULL) + { + *handle = alloc_arg.handle; + } + if (actual_size != NULL) + { + *actual_size = alloc_arg.size; + } + // printf("create width=%u, height=%u, bpp=%u, size=%lu dumb buffer\n",alloc_arg.width,alloc_arg.height,alloc_arg.bpp,alloc_arg.size); + // printf("out handle= %d\n",alloc_arg.handle); + + //获取fd + memset(&fd_args, 0, sizeof(fd_args)); + fd_args.fd = -1; + fd_args.handle = alloc_arg.handle; + ; + fd_args.flags = 0; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &fd_args); + if (ret) + { + printf("rk-debug handle_to_fd failed ret=%d,err=%s, handle=%x \n", ret, strerror(errno), fd_args.handle); + return NULL; + } + // printf("out fd = %d, drm fd: %d\n",fd_args.fd,drm_fd); + if (fd != NULL) + { + *fd = fd_args.fd; + } + + //获取虚拟地址 + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = alloc_arg.handle; + + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); + if (ret) + { + printf("failed to create map dumb: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + vir_addr = map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, mmap_arg.offset); + if (map == MAP_FAILED) + { + printf("failed to mmap buffer: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + // printf("alloc map=%x \n",map); + return vir_addr; +destory_dumb: + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = alloc_arg.handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d\n", ret); + return vir_addr; +} + +int drm_buf_destroy(drm_context *drm_ctx, int drm_fd, int buf_fd, int handle, void *drm_buf, size_t size) +{ + int ret = -1; + if (drm_buf == NULL) + { + printf("drm buffer is NULL\n"); + return -1; + } + + munmap(drm_buf, size); + + struct drm_mode_destroy_dumb destory_arg; + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d, error=%s\n", ret, strerror(errno)); + if (buf_fd > 0) + { + close(buf_fd); + } + + return ret; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/main.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/main.cc new file mode 100644 index 0000000..60ce6c8 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/main.cc @@ -0,0 +1,500 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#define _BASETSD_H + +#define STB_IMAGE_IMPLEMENTATION +#include "stb/stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include + +#undef cimg_display +#define cimg_display 0 +#include "CImg/CImg.h" + +#include "drm_func.h" +#include "rga_func.h" +#include "rknn_api.h" +#include "postprocess.h" + +#define PERF_WITH_POST 1 + +using namespace cimg_library; + +/*------------------------------------------- + Functions +-------------------------------------------*/ + +inline const char *get_type_string(rknn_tensor_type type) +{ + switch (type) + { + case RKNN_TENSOR_FLOAT32: + return "FP32"; + case RKNN_TENSOR_FLOAT16: + return "FP16"; + case RKNN_TENSOR_INT8: + return "INT8"; + case RKNN_TENSOR_UINT8: + return "UINT8"; + case RKNN_TENSOR_INT16: + return "INT16"; + default: + return "UNKNOW"; + } +} + +inline const char *get_qnt_type_string(rknn_tensor_qnt_type type) +{ + switch (type) + { + case RKNN_TENSOR_QNT_NONE: + return "NONE"; + case RKNN_TENSOR_QNT_DFP: + return "DFP"; + case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + return "AFFINE"; + default: + return "UNKNOW"; + } +} + +inline const char *get_format_string(rknn_tensor_format fmt) +{ + switch (fmt) + { + case RKNN_TENSOR_NCHW: + return "NCHW"; + case RKNN_TENSOR_NHWC: + return "NHWC"; + default: + return "UNKNOW"; + } +} + +static void dump_tensor_attr(rknn_tensor_attr *attr) +{ + printf(" index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, " + "zp=%d, scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], attr->dims[1], attr->dims[0], + attr->n_elems, attr->size, get_format_string(attr->fmt), get_type_string(attr->type), + get_qnt_type_string(attr->qnt_type), attr->zp, attr->scale); +} + +double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); } + +static unsigned char *load_data(FILE *fp, size_t ofst, size_t sz) +{ + unsigned char *data; + int ret; + + data = NULL; + + if (NULL == fp) + { + return NULL; + } + + ret = fseek(fp, ofst, SEEK_SET); + if (ret != 0) + { + printf("blob seek failure.\n"); + return NULL; + } + + data = (unsigned char *)malloc(sz); + if (data == NULL) + { + printf("buffer malloc failure.\n"); + return NULL; + } + ret = fread(data, 1, sz, fp); + return data; +} + +static unsigned char *load_model(const char *filename, int *model_size) +{ + + FILE *fp; + unsigned char *data; + + fp = fopen(filename, "rb"); + if (NULL == fp) + { + printf("Open file %s failed.\n", filename); + return NULL; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + + data = load_data(fp, 0, size); + + fclose(fp); + + *model_size = size; + return data; +} + +static int saveFloat(const char *file_name, float *output, int element_size) +{ + FILE *fp; + fp = fopen(file_name, "w"); + for (int i = 0; i < element_size; i++) + { + fprintf(fp, "%.6f\n", output[i]); + } + fclose(fp); + return 0; +} + +static unsigned char *load_image(const char *image_path, int *org_height, int *org_width, int *org_ch, rknn_tensor_attr *input_attr) +{ + int req_height = 0; + int req_width = 0; + int req_channel = 0; + + switch (input_attr->fmt) + { + case RKNN_TENSOR_NHWC: + req_height = input_attr->dims[2]; + req_width = input_attr->dims[1]; + req_channel = input_attr->dims[0]; + break; + case RKNN_TENSOR_NCHW: + req_height = input_attr->dims[1]; + req_width = input_attr->dims[0]; + req_channel = input_attr->dims[2]; + break; + default: + printf("meet unsupported layout\n"); + return NULL; + } + + int height = 0; + int width = 0; + int channel = 0; + + unsigned char *image_data = stbi_load(image_path, &width, &height, &channel, req_channel); + if (image_data == NULL) + { + printf("load image failed!\n"); + return NULL; + } + *org_width = width; + *org_height = height; + *org_ch = channel; + + return image_data; +} + +/*------------------------------------------- + Main Functions +-------------------------------------------*/ +int main(int argc, char **argv) +{ + int status = 0; + char *model_name = NULL; + rknn_context ctx; + int is_map = 0; + rknn_tensor_mem in_mem[1]; + void *drm_buf = NULL; + int drm_fd = -1; + int buf_fd = -1; // converted from buffer handle + unsigned int handle; + size_t actual_size = 0; + int img_width = 0; + int img_height = 0; + int img_channel = 0; + rga_context rga_ctx; + drm_context drm_ctx; + const float nms_threshold = NMS_THRESH; + const float box_conf_threshold = BOX_THRESH; + struct timeval start_time, stop_time; + int ret; + memset(&rga_ctx, 0, sizeof(rga_context)); + memset(&drm_ctx, 0, sizeof(drm_context)); + + if (argc != 4) + { + printf("Note: rknn model need meet zero-copy condition: 3-channel with same integer means and same scales\n"); + printf("Usage: %s \n", argv[0]); + printf("flag:\n"); + printf("\t 0: run builtin_permute=False rknn model, perform rknn_inputs_set\n"); + printf("\t 1: run builtin_permute=True rknn model, perform rknn_inputs_map\n"); + return -1; + } + + printf("post process config: box_conf_threshold = %.2f, nms_threshold = %.2f\n", + box_conf_threshold, nms_threshold); + model_name = (char *)argv[1]; + char *image_name = argv[2]; + is_map = atoi(argv[3]); + + if (strstr(image_name, ".jpg") != NULL || strstr(image_name, ".png") != NULL) + { + printf("Error: read %s failed! only support .bmp format image\n", image_name); + return -1; + } + + /* Create the neural network */ + printf("Loading mode...\n"); + int model_data_size = 0; + unsigned char *model_data = load_model(model_name, &model_data_size); + ret = rknn_init(&ctx, model_data, model_data_size, 0); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + sizeof(rknn_sdk_version)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("sdk version: %s driver version: %s\n", version.api_version, + version.drv_version); + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + printf("model input num: %d, output num: %d\n", io_num.n_input, + io_num.n_output); + + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + dump_tensor_attr(&(input_attrs[i])); + } + + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + sizeof(rknn_tensor_attr)); + dump_tensor_attr(&(output_attrs[i])); + } + + int channel = 3; + int width = 0; + int height = 0; + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) + { + printf("model is NCHW input fmt\n"); + width = input_attrs[0].dims[0]; + height = input_attrs[0].dims[1]; + } + else + { + printf("model is NHWC input fmt\n"); + width = input_attrs[0].dims[1]; + height = input_attrs[0].dims[2]; + } + + printf("model input height=%d, width=%d, channel=%d\n", height, width, + channel); + + // Load image + CImg img(image_name); + unsigned char *input_data = NULL; + input_data = load_image(image_name, &img_height, &img_width, &img_channel, &input_attrs[0]); + if (!input_data) + { + return -1; + } + + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = width * height * channel; + inputs[0].fmt = RKNN_TENSOR_NHWC; + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) + { + outputs[i].want_float = 0; + } + + // DRM alloc buffer + drm_fd = drm_init(&drm_ctx); + drm_buf = drm_buf_alloc(&drm_ctx, drm_fd, img_width, img_height, channel * 8, + &buf_fd, &handle, &actual_size); + memcpy(drm_buf, input_data, img_width * img_height * channel); + void *resize_buf = malloc(height * width * channel); + + // init rga context + RGA_init(&rga_ctx); + if (is_map == 0) + { + inputs[0].pass_through = 0; + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + inputs[0].buf = resize_buf; + gettimeofday(&start_time, NULL); + rknn_inputs_set(ctx, io_num.n_input, inputs); + } + else + { + gettimeofday(&start_time, NULL); + ret = rknn_inputs_map(ctx, io_num.n_input, in_mem); + printf("input virt_addr = %p, phys_addr = 0x%llx, fd = %d, size = %d\n", + in_mem[0].logical_addr, in_mem[0].physical_addr, in_mem[0].fd, + in_mem[0].size); + if (in_mem[0].physical_addr == 0xffffffffffffffff) + { + printf("get unvalid input physical address, please extend in/out memory space\n"); + exit(-1); + } + else + { + // rga process by physical addr + img_resize_fast(&rga_ctx, buf_fd, img_width, img_height, in_mem[0].physical_addr, width, height); + } + rknn_inputs_sync(ctx, io_num.n_input, in_mem); + } + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + gettimeofday(&stop_time, NULL); + printf("once run use %f ms\n", + (__get_us(stop_time) - __get_us(start_time)) / 1000); + + //post process + float scale_w = (float)width / img_width; + float scale_h = (float)height / img_height; + + detect_result_group_t detect_result_group; + std::vector out_scales; + std::vector out_zps; + for (int i = 0; i < io_num.n_output; ++i) + { + out_scales.push_back(output_attrs[i].scale); + out_zps.push_back(output_attrs[i].zp); + } + post_process((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); + + // Draw Objects + char text[256]; + const unsigned char blue[] = {0, 0, 255}; + const unsigned char white[] = {255, 255, 255}; + for (int i = 0; i < detect_result_group.count; i++) + { + detect_result_t *det_result = &(detect_result_group.results[i]); + sprintf(text, "%s %.2f", det_result->name, det_result->prop); + printf("%s @ (%d %d %d %d) %f\n", + det_result->name, + det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom, + det_result->prop); + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + //draw box + img.draw_rectangle(x1, y1, x2, y2, blue, 1, ~0U); + img.draw_text(x1, y1 - 12, text, white); + } + img.save("./out.bmp"); + + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + + // loop test + int test_count = 10; + gettimeofday(&start_time, NULL); + if (is_map == 0) + { + for (int i = 0; i < test_count; ++i) + { + img_resize_slow(&rga_ctx, drm_buf, img_width, img_height, resize_buf, width, height); + rknn_inputs_set(ctx, io_num.n_input, inputs); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); +#if PERF_WITH_POST + post_process((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); +#endif + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + } + } + else + { + for (int i = 0; i < test_count; ++i) + { + img_resize_fast(&rga_ctx, buf_fd, img_width, img_height, in_mem[0].physical_addr, width, height); + rknn_inputs_sync(ctx, io_num.n_input, in_mem); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); +#if PERF_WITH_POST + post_process((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, height, width, + box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, &detect_result_group); +#endif + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + } + } + gettimeofday(&stop_time, NULL); + printf("run loop count = %d , average time: %f ms\n", test_count, + (__get_us(stop_time) - __get_us(start_time)) / 1000.0 / test_count); + + // release + if (is_map) + { + ret = rknn_inputs_unmap(ctx, io_num.n_input, in_mem); + } + ret = rknn_destroy(ctx); + drm_buf_destroy(&drm_ctx, drm_fd, buf_fd, handle, drm_buf, actual_size); + + drm_deinit(&drm_ctx, drm_fd); + RGA_deinit(&rga_ctx); + if (model_data) + { + free(model_data); + } + + if (resize_buf) + { + free(resize_buf); + } + + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/postprocess.cc b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/postprocess.cc new file mode 100644 index 0000000..adf6ea8 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/postprocess.cc @@ -0,0 +1,366 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include "postprocess.h" +#include +#define LABEL_NALE_TXT_PATH "./model/coco_80_labels_list.txt" + +static char *labels[OBJ_CLASS_NUM]; + +const int anchor0[6] = {10, 13, 16, 30, 33, 23}; +const int anchor1[6] = {30, 61, 62, 45, 59, 119}; +const int anchor2[6] = {116, 90, 156, 198, 373, 326}; + +inline static int clamp(float val, int min, int max) +{ + return val > min ? (val < max ? val : max) : min; +} + +char *readLine(FILE *fp, char *buffer, int *len) +{ + int ch; + int i = 0; + size_t buff_len = 0; + + buffer = (char *)malloc(buff_len + 1); + if (!buffer) + return NULL; // Out of memory + + while ((ch = fgetc(fp)) != '\n' && ch != EOF) + { + buff_len++; + void *tmp = realloc(buffer, buff_len + 1); + if (tmp == NULL) + { + free(buffer); + return NULL; // Out of memory + } + buffer = (char *)tmp; + + buffer[i] = (char)ch; + i++; + } + buffer[i] = '\0'; + + *len = buff_len; + + // Detect end + if (ch == EOF && (i == 0 || ferror(fp))) + { + free(buffer); + return NULL; + } + return buffer; +} + +int readLines(const char *fileName, char *lines[], int max_line) +{ + FILE *file = fopen(fileName, "r"); + char *s; + int i = 0; + int n = 0; + while ((s = readLine(file, s, &n)) != NULL) + { + lines[i++] = s; + if (i >= max_line) + break; + } + return i; +} + +int loadLabelName(const char *locationFilename, char *label[]) +{ + printf("loadLabelName %s\n", locationFilename); + readLines(locationFilename, label, OBJ_CLASS_NUM); + return 0; +} + +static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1) + 1.0); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1) + 1.0); + float i = w * h; + float u = (xmax0 - xmin0 + 1.0) * (ymax0 - ymin0 + 1.0) + (xmax1 - xmin1 + 1.0) * (ymax1 - ymin1 + 1.0) - i; + return u <= 0.f ? 0.f : (i / u); +} + +static int nms(int validCount, std::vector &outputLocations, std::vector classIds, std::vector &order, int filterId, float threshold) +{ + for (int i = 0; i < validCount; ++i) + { + if (order[i] == -1 || classIds[i] != filterId) + { + continue; + } + int n = order[i]; + for (int j = i + 1; j < validCount; ++j) + { + int m = order[j]; + if (m == -1 || classIds[i] != filterId) + { + continue; + } + float xmin0 = outputLocations[n * 4 + 0]; + float ymin0 = outputLocations[n * 4 + 1]; + float xmax0 = outputLocations[n * 4 + 0] + outputLocations[n * 4 + 2]; + float ymax0 = outputLocations[n * 4 + 1] + outputLocations[n * 4 + 3]; + + float xmin1 = outputLocations[m * 4 + 0]; + float ymin1 = outputLocations[m * 4 + 1]; + float xmax1 = outputLocations[m * 4 + 0] + outputLocations[m * 4 + 2]; + float ymax1 = outputLocations[m * 4 + 1] + outputLocations[m * 4 + 3]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou > threshold) + { + order[j] = -1; + } + } + } + return 0; +} + +static int quick_sort_indice_inverse( + std::vector &input, + int left, + int right, + std::vector &indices) +{ + float key; + int key_index; + int low = left; + int high = right; + if (left < right) + { + key_index = indices[left]; + key = input[left]; + while (low < high) + { + while (low < high && input[high] <= key) + { + high--; + } + input[low] = input[high]; + indices[low] = indices[high]; + while (low < high && input[low] >= key) + { + low++; + } + input[high] = input[low]; + indices[high] = indices[low]; + } + input[low] = key; + indices[low] = key_index; + quick_sort_indice_inverse(input, left, low - 1, indices); + quick_sort_indice_inverse(input, low + 1, right, indices); + } + return low; +} + +static float sigmoid(float x) +{ + return 1.0 / (1.0 + expf(-x)); +} + +static float unsigmoid(float y) +{ + return -1.0 * logf((1.0 / y) - 1.0); +} + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + return f; +} + +static uint8_t qnt_f32_to_affine(float f32, uint32_t zp, float scale) +{ + float dst_val = (f32 / scale) + zp; + uint8_t res = (uint8_t)__clip(dst_val, 0, 255); + return res; +} + +static float deqnt_affine_to_f32(uint8_t qnt, uint32_t zp, float scale) +{ + return ((float)qnt - (float)zp) * scale; +} + +static int process(uint8_t *input, int *anchor, int grid_h, int grid_w, int height, int width, int stride, + std::vector &boxes, std::vector &objProbs, std::vector &classId, + float threshold, uint32_t zp, float scale) +{ + + int validCount = 0; + int grid_len = grid_h * grid_w; + float thres = unsigmoid(threshold); + uint8_t thres_u8 = qnt_f32_to_affine(thres, zp, scale); + for (int a = 0; a < 3; a++) + { + for (int i = 0; i < grid_h; i++) + { + for (int j = 0; j < grid_w; j++) + { + uint8_t box_confidence = input[(PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j]; + if (box_confidence >= thres_u8) + { + int offset = (PROP_BOX_SIZE * a) * grid_len + i * grid_w + j; + uint8_t *in_ptr = input + offset; + float box_x = sigmoid(deqnt_affine_to_f32(*in_ptr, zp, scale)) * 2.0 - 0.5; + float box_y = sigmoid(deqnt_affine_to_f32(in_ptr[grid_len], zp, scale)) * 2.0 - 0.5; + float box_w = sigmoid(deqnt_affine_to_f32(in_ptr[2 * grid_len], zp, scale)) * 2.0; + float box_h = sigmoid(deqnt_affine_to_f32(in_ptr[3 * grid_len], zp, scale)) * 2.0; + box_x = (box_x + j) * (float)stride; + box_y = (box_y + i) * (float)stride; + box_w = box_w * box_w * (float)anchor[a * 2]; + box_h = box_h * box_h * (float)anchor[a * 2 + 1]; + box_x -= (box_w / 2.0); + box_y -= (box_h / 2.0); + boxes.push_back(box_x); + boxes.push_back(box_y); + boxes.push_back(box_w); + boxes.push_back(box_h); + + uint8_t maxClassProbs = in_ptr[5 * grid_len]; + int maxClassId = 0; + for (int k = 1; k < OBJ_CLASS_NUM; ++k) + { + uint8_t prob = in_ptr[(5 + k) * grid_len]; + if (prob > maxClassProbs) + { + maxClassId = k; + maxClassProbs = prob; + } + } + objProbs.push_back(sigmoid(deqnt_affine_to_f32(maxClassProbs, zp, scale))); + classId.push_back(maxClassId); + validCount++; + } + } + } + } + return validCount; +} + +int post_process(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, float scale_w, float scale_h, + std::vector &qnt_zps, std::vector &qnt_scales, + detect_result_group_t *group) +{ + static int init = -1; + if (init == -1) + { + int ret = 0; + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) + { + return -1; + } + + init = 0; + } + memset(group, 0, sizeof(detect_result_group_t)); + + std::vector filterBoxes; + std::vector objProbs; + std::vector classId; + + // stride 8 + int stride0 = 8; + int grid_h0 = model_in_h / stride0; + int grid_w0 = model_in_w / stride0; + int validCount0 = 0; + validCount0 = process(input0, (int *)anchor0, grid_h0, grid_w0, model_in_h, model_in_w, + stride0, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[0], qnt_scales[0]); + + // stride 16 + int stride1 = 16; + int grid_h1 = model_in_h / stride1; + int grid_w1 = model_in_w / stride1; + int validCount1 = 0; + validCount1 = process(input1, (int *)anchor1, grid_h1, grid_w1, model_in_h, model_in_w, + stride1, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[1], qnt_scales[1]); + + // stride 32 + int stride2 = 32; + int grid_h2 = model_in_h / stride2; + int grid_w2 = model_in_w / stride2; + int validCount2 = 0; + validCount2 = process(input2, (int *)anchor2, grid_h2, grid_w2, model_in_h, model_in_w, + stride2, filterBoxes, objProbs, classId, conf_threshold, qnt_zps[2], qnt_scales[2]); + + int validCount = validCount0 + validCount1 + validCount2; + // no object detect + if (validCount <= 0) + { + return 0; + } + + std::vector indexArray; + for (int i = 0; i < validCount; ++i) + { + indexArray.push_back(i); + } + + quick_sort_indice_inverse(objProbs, 0, validCount - 1, indexArray); + + std::set class_set(std::begin(classId), std::end(classId)); + + for (auto c : class_set) + { + nms(validCount, filterBoxes, classId, indexArray, c, nms_threshold); + } + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) + { + + if (indexArray[i] == -1 || last_count >= OBJ_NUMB_MAX_SIZE) + { + continue; + } + int n = indexArray[i]; + + float x1 = filterBoxes[n * 4 + 0]; + float y1 = filterBoxes[n * 4 + 1]; + float x2 = x1 + filterBoxes[n * 4 + 2]; + float y2 = y1 + filterBoxes[n * 4 + 3]; + int id = classId[n]; + float obj_conf = objProbs[i]; + + group->results[last_count].box.left = (int)(clamp(x1, 0, model_in_w) / scale_w); + group->results[last_count].box.top = (int)(clamp(y1, 0, model_in_h) / scale_h); + group->results[last_count].box.right = (int)(clamp(x2, 0, model_in_w) / scale_w); + group->results[last_count].box.bottom = (int)(clamp(y2, 0, model_in_h) / scale_h); + group->results[last_count].prop = obj_conf; + char *label = labels[id]; + strncpy(group->results[last_count].name, label, OBJ_NAME_MAX_SIZE); + + // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, group->results[last_count].box.top, + // group->results[last_count].box.right, group->results[last_count].box.bottom, label); + last_count++; + } + group->count = last_count; + + return 0; +} diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/rga_func.c b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/rga_func.c new file mode 100644 index 0000000..69caada --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/examples/rknn_zero_copy_demo/src/rga_func.c @@ -0,0 +1,114 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "rga_func.h" + +int RGA_init(rga_context *rga_ctx) +{ + rga_ctx->rga_handle = dlopen("/usr/lib/librga.so", RTLD_LAZY); + if (!rga_ctx->rga_handle) + { + printf("dlopen /usr/lib/librga.so failed\n"); + return -1; + } + rga_ctx->init_func = (FUNC_RGA_INIT)dlsym(rga_ctx->rga_handle, "c_RkRgaInit"); + rga_ctx->deinit_func = (FUNC_RGA_DEINIT)dlsym(rga_ctx->rga_handle, "c_RkRgaDeInit"); + rga_ctx->blit_func = (FUNC_RGA_BLIT)dlsym(rga_ctx->rga_handle, "c_RkRgaBlit"); + rga_ctx->init_func(); + return 0; +} + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h) +{ + // printf("rga use fd, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) + { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = src_fd; + src.mmuFlag = 1; + // src.virAddr = (void *)psrc; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 0; + +#if defined(__arm__) + dst.phyAddr = (void *)((uint32_t)dst_phys); +#else + dst.phyAddr = (void *)dst_phys; +#endif + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) + { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h) +{ + // printf("rga use virtual, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) + { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = -1; + src.mmuFlag = 1; + src.virAddr = (void *)src_virt; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 1; + dst.virAddr = dst_virt; + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) + { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +int RGA_deinit(rga_context *rga_ctx) +{ + if(rga_ctx->rga_handle) + { + dlclose(rga_ctx->rga_handle); + rga_ctx->rga_handle = NULL; + } +} \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/include/rknn_api.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/include/rknn_api.h new file mode 100644 index 0000000..a114c1f --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/include/rknn_api.h @@ -0,0 +1,504 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + + +#ifndef _RKNN_RUNTIME_H +#define _RKNN_RUNTIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + Definition of extended flag for rknn_init. +*/ +/* set high priority context. */ +#define RKNN_FLAG_PRIOR_HIGH 0x00000000 + +/* set medium priority context */ +#define RKNN_FLAG_PRIOR_MEDIUM 0x00000001 + +/* set low priority context. */ +#define RKNN_FLAG_PRIOR_LOW 0x00000002 + +/* asynchronous mode. + when enable, rknn_outputs_get will not block for too long because it directly retrieves the result of + the previous frame which can increase the frame rate on single-threaded mode, but at the cost of + rknn_outputs_get not retrieves the result of the current frame. + in multi-threaded mode you do not need to turn this mode on. */ +#define RKNN_FLAG_ASYNC_MASK 0x00000004 + +/* collect performance mode. + when enable, you can get detailed performance reports via rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, ...), + but it will reduce the frame rate. */ +#define RKNN_FLAG_COLLECT_PERF_MASK 0x00000008 + +/* + save pre-compile model. +*/ +#define RKNN_FLAG_PRECOMPILE_MASK 0x00000020 + +/* + Error code returned by the RKNN API. +*/ +#define RKNN_SUCC 0 /* execute succeed. */ +#define RKNN_ERR_FAIL -1 /* execute failed. */ +#define RKNN_ERR_TIMEOUT -2 /* execute timeout. */ +#define RKNN_ERR_DEVICE_UNAVAILABLE -3 /* device is unavailable. */ +#define RKNN_ERR_MALLOC_FAIL -4 /* memory malloc fail. */ +#define RKNN_ERR_PARAM_INVALID -5 /* parameter is invalid. */ +#define RKNN_ERR_MODEL_INVALID -6 /* model is invalid. */ +#define RKNN_ERR_CTX_INVALID -7 /* context is invalid. */ +#define RKNN_ERR_INPUT_INVALID -8 /* input is invalid. */ +#define RKNN_ERR_OUTPUT_INVALID -9 /* output is invalid. */ +#define RKNN_ERR_DEVICE_UNMATCH -10 /* the device is unmatch, please update rknn sdk + and npu driver/firmware. */ +#define RKNN_ERR_INCOMPATILE_PRE_COMPILE_MODEL -11 /* This RKNN model use pre_compile mode, but not compatible with current driver. */ +//add by chifred: for reporting optimization version bug info +#define RKNN_ERR_INCOMPATILE_OPTIMIZATION_LEVEL_VERSION -12 /* This RKNN model set optimization level, but not compatible with current driver. */ +#define RKNN_ERR_TARGET_PLATFORM_UNMATCH -13 /* This RKNN model set target platform, but not compatible with current platform. */ +//chifred add end +#define RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER -14 /* This RKNN model is not a pre-compiled model, but the npu driver is mini driver. */ + +/* + Definition for tensor +*/ +#define RKNN_MAX_DIMS 16 /* maximum dimension of tensor. */ +#define RKNN_MAX_NAME_LEN 256 /* maximum name lenth of tensor. */ + + + +#ifdef __arm__ +typedef uint32_t rknn_context; +#else +typedef uint64_t rknn_context; +#endif + +/* + The query command for rknn_query +*/ +typedef enum _rknn_query_cmd { + RKNN_QUERY_IN_OUT_NUM = 0, /* query the number of input & output tensor. */ + RKNN_QUERY_INPUT_ATTR, /* query the attribute of input tensor. */ + RKNN_QUERY_OUTPUT_ATTR, /* query the attribute of output tensor. */ + RKNN_QUERY_PERF_DETAIL, /* query the detail performance, need set + RKNN_FLAG_COLLECT_PERF_MASK when call rknn_init. */ + RKNN_QUERY_PERF_RUN, /* query the time of run. */ + RKNN_QUERY_SDK_VERSION, /* query the sdk & driver version */ + RKNN_QUERY_PRE_COMPILE, /* query the pre compile model */ + + RKNN_QUERY_CMD_MAX +} rknn_query_cmd; + +/* + the tensor data type. +*/ +typedef enum _rknn_tensor_type { + RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */ + RKNN_TENSOR_FLOAT16, /* data type is float16. */ + RKNN_TENSOR_INT8, /* data type is int8. */ + RKNN_TENSOR_UINT8, /* data type is uint8. */ + RKNN_TENSOR_INT16, /* data type is int16. */ + + RKNN_TENSOR_TYPE_MAX +} rknn_tensor_type; + +/* + the quantitative type. +*/ +typedef enum _rknn_tensor_qnt_type { + RKNN_TENSOR_QNT_NONE = 0, /* none. */ + RKNN_TENSOR_QNT_DFP, /* dynamic fixed point. */ + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC, /* asymmetric affine. */ + + RKNN_TENSOR_QNT_MAX +} rknn_tensor_qnt_type; + +/* + the tensor data format. +*/ +typedef enum _rknn_tensor_format { + RKNN_TENSOR_NCHW = 0, /* data format is NCHW. */ + RKNN_TENSOR_NHWC, /* data format is NHWC. */ + + RKNN_TENSOR_FORMAT_MAX +} rknn_tensor_format; + +/* + the information for RKNN_QUERY_IN_OUT_NUM. +*/ +typedef struct _rknn_input_output_num { + uint32_t n_input; /* the number of input. */ + uint32_t n_output; /* the number of output. */ +} rknn_input_output_num; + +/* + the information for RKNN_QUERY_INPUT_ATTR / RKNN_QUERY_OUTPUT_ATTR. +*/ +typedef struct _rknn_tensor_attr { + uint32_t index; /* input parameter, the index of input/output tensor, + need set before call rknn_query. */ + + uint32_t n_dims; /* the number of dimensions. */ + uint32_t dims[RKNN_MAX_DIMS]; /* the dimensions array. */ + char name[RKNN_MAX_NAME_LEN]; /* the name of tensor. */ + + uint32_t n_elems; /* the number of elements. */ + uint32_t size; /* the bytes size of tensor. */ + + rknn_tensor_format fmt; /* the data format of tensor. */ + rknn_tensor_type type; /* the data type of tensor. */ + rknn_tensor_qnt_type qnt_type; /* the quantitative type of tensor. */ + int8_t fl; /* fractional length for RKNN_TENSOR_QNT_DFP. */ + uint32_t zp; /* zero point for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ + float scale; /* scale for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ +} rknn_tensor_attr; + +/* + the information for RKNN_QUERY_PERF_DETAIL. +*/ +typedef struct _rknn_perf_detail { + char* perf_data; /* the string pointer of perf detail. don't need free it by user. */ + uint64_t data_len; /* the string length. */ +} rknn_perf_detail; + +/* + the information for RKNN_QUERY_PERF_RUN. +*/ +typedef struct _rknn_perf_run { + int64_t run_duration; /* real inference time (us) */ +} rknn_perf_run; + +/* + the information for RKNN_QUERY_SDK_VERSION. +*/ +typedef struct _rknn_sdk_version { + char api_version[256]; /* the version of rknn api. */ + char drv_version[256]; /* the version of rknn driver. */ +} rknn_sdk_version; + +/* + The flags of rknn_tensor_mem. +*/ +typedef enum _rknn_tensor_mem_flags { + RKNN_TENSOR_MEMORY_FLAGS_UNKNOWN = 0, + RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE = 1, /*Used to mark in rknn_destroy_mem() whether it is necessary to release the "mem" pointer itself. + If the flag RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE is set, rknn_destroy_mem() will call free(mem).*/ + +} rknn_tensor_mem_flags; + + +/* + the memory information of tensor. +*/ +typedef struct _rknn_tensor_memory { + void* logical_addr; /* the virtual address of tensor buffer. */ + uint64_t physical_addr; /* the physical address of tensor buffer. */ + int32_t fd; /* the fd of tensor buffer. */ + uint32_t size; /* the size of tensor buffer. */ + uint32_t handle; /* the handle tensor buffer. */ + void * priv_data; /* the data which is reserved. */ + uint64_t reserved_flag; /* the flag which is reserved. */ +} rknn_tensor_mem; + +/* + the input information for rknn_input_set. +*/ +typedef struct _rknn_input { + uint32_t index; /* the input index. */ + void* buf; /* the input buf for index. */ + uint32_t size; /* the size of input buf. */ + uint8_t pass_through; /* pass through mode. + if TRUE, the buf data is passed directly to the input node of the rknn model + without any conversion. the following variables do not need to be set. + if FALSE, the buf data is converted into an input consistent with the model + according to the following type and fmt. so the following variables + need to be set.*/ + rknn_tensor_type type; /* the data type of input buf. */ + rknn_tensor_format fmt; /* the data format of input buf. + currently the internal input format of NPU is NCHW by default. + so entering NCHW data can avoid the format conversion in the driver. */ +} rknn_input; + +/* + the output information for rknn_outputs_get. +*/ +typedef struct _rknn_output { + uint8_t want_float; /* want transfer output data to float */ + uint8_t is_prealloc; /* whether buf is pre-allocated. + if true, the following variables need to be set. + if false, The following variables do not need to be set. */ + uint32_t index; /* the output index. */ + void* buf; /* the output buf for index. + when is_prealloc = FALSE and rknn_outputs_release called, + this buf pointer will be free and don't use it anymore. */ + uint32_t size; /* the size of output buf. */ +} rknn_output; + +/* + the extend information for rknn_run. +*/ +typedef struct _rknn_run_extend { + uint64_t frame_id; /* output parameter, indicate current frame id of run. */ +} rknn_run_extend; + +/* + the extend information for rknn_outputs_get. +*/ +typedef struct _rknn_output_extend { + uint64_t frame_id; /* output parameter, indicate the frame id of outputs, corresponds to + struct rknn_run_extend.frame_id.*/ +} rknn_output_extend; + +/* + the information for RKNN_QUERY_RKNN_PRECOMPILE. +*/ +typedef struct _rknn_precompile { + void* model_data; /* the pointer of precompile model. don't need free it by user. */ + uint32_t data_len; /* the model length. */ +} rknn_precompile; + + +/* rknn_init + + initial the context and load the rknn model. + + input: + rknn_context* context the pointer of context handle. + void* model pointer to the rknn model. + uint32_t size the size of rknn model. + uint32_t flag extend flag, see the define of RKNN_FLAG_XXX_XXX. + return: + int error code. +*/ +int rknn_init(rknn_context* context, void* model, uint32_t size, uint32_t flag); + + +/* rknn_destroy + + unload the rknn model and destroy the context. + + input: + rknn_context context the handle of context. + return: + int error code. +*/ +int rknn_destroy(rknn_context context); + + +/* rknn_query + + query the information about model or others. see rknn_query_cmd. + + input: + rknn_context context the handle of context. + rknn_query_cmd cmd the command of query. + void* info the buffer point of information. + uint32_t size the size of information. + return: + int error code. +*/ +int rknn_query(rknn_context context, rknn_query_cmd cmd, void* info, uint32_t size); + + +/* rknn_inputs_set + + set inputs information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_input inputs[] the arrays of inputs information, see rknn_input. + return: + int error code +*/ +int rknn_inputs_set(rknn_context context, uint32_t n_inputs, rknn_input inputs[]); + + +/* rknn_inputs_map + + map inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_map(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_sync + + synchronize inputs tensor buffer by input index of rknn model. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_sync(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_unmap + + unmap inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_unmap(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_run + + run the model to execute inference. + + input: + rknn_context context the handle of context. + rknn_run_extend* extend the extend information of run. + return: + int error code. +*/ +int rknn_run(rknn_context context, rknn_run_extend* extend); + + +/* rknn_outputs_get + + wait the inference to finish and get the outputs. + this function will block until inference finish. + the results will set to outputs[]. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_output outputs[] the arrays of output, see rknn_output. + rknn_output_extend* the extend information of output. + return: + int error code. +*/ +int rknn_outputs_get(rknn_context context, uint32_t n_outputs, rknn_output outputs[], rknn_output_extend* extend); + + +/* rknn_outputs_release + + release the outputs that get by rknn_outputs_get. + after called, the rknn_output[x].buf get from rknn_outputs_get will + also be free when rknn_output[x].is_prealloc = FALSE. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_output outputs[] the arrays of output. + return: + int error code +*/ +int rknn_outputs_release(rknn_context context, uint32_t n_ouputs, rknn_output outputs[]); + + +/* rknn_outputs_map + + map the model output tensors memory information. + The difference between this function and "rknn_outputs_get" is + that it directly maps the model output tensor memory location to the user. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_map(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_sync + + synchronize the output tensors buffer to ensure cache cohenrency, wait the inference to finish. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_sync(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_unmap + + unmap the outputs memory information that get by rknn_outputs_map. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_outputs_unmap(rknn_context context, uint32_t n_ouputs, rknn_tensor_mem mem[]); + +/* rknn_create_mem (memory allocated inside) + + Create tensor memory. This API require libdrm support! + + input: + rknn_context ctx the handle of context. + uint64_t size the size of tensor buffer. + return: + rknn_tensor_mem the pointer of tensor memory information. +*/ +rknn_tensor_mem* rknn_create_mem(rknn_context ctx, uint64_t size); + +/* rknn_destroy_mem (support allocate inside and outside) + + destroy tensor memory. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the pointer of tensor memory information. + return: + int error code +*/ +int rknn_destroy_mem(rknn_context ctx, rknn_tensor_mem *mem); + + + +/* rknn_set_io_mem + + set the input and output tensors buffer. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the array of tensor memory information. + rknn_tensor_attr *attr the attribute of input or output tensor buffer. + return: + int error code. +*/ +int rknn_set_io_mem(rknn_context ctx, rknn_tensor_mem *mem, rknn_tensor_attr *attr); + +#ifdef __cplusplus +} //extern "C" +#endif + +#endif //_RKNN_RUNTIME_H diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/lib/librknn_api.so b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/lib/librknn_api.so new file mode 100644 index 0000000..ae3b0d1 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/lib/librknn_api.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/lib64/librknn_api.so b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/lib64/librknn_api.so new file mode 100644 index 0000000..950bc6e Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_api/librknn_api/lib64/librknn_api.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/include/rknn_matmul_api.h b/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/include/rknn_matmul_api.h new file mode 100644 index 0000000..74752d1 --- /dev/null +++ b/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/include/rknn_matmul_api.h @@ -0,0 +1,110 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2021 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + + +#ifndef _RKNN_MATMUL_H +#define _RKNN_MATMUL_H + + +/** + * Perform specific Matrix Multiply like AxB = C, where, + * Matrix A : [input] shape is [K,M], K is the high rank dimension in memory, M is the low rank dimension in memory and must be 1 now! + * Matrix B : [input] shape is [K,N], N is the low rank dimension in memory. + * Matrix C : [output] shape is [M,N] + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + the tensor data type. +*/ + +typedef enum _rknn_tensor_type { + RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */ + RKNN_TENSOR_FLOAT16, /* data type is float16. */ + RKNN_TENSOR_INT8, /* data type is int8. */ + RKNN_TENSOR_UINT8, /* data type is uint8. */ + RKNN_TENSOR_INT16, /* data type is int16. */ + + RKNN_TENSOR_TYPE_MAX +} rknn_tensor_type; + +#ifdef __arm__ +typedef uint32_t rknn_context; +#else +typedef uint64_t rknn_context; +#endif + + +/* + Definition of handle for rknn_matmul_run. +*/ +typedef struct _rknn_matmul_t{ + void* A; + void* B; + int32_t M; /* the low rank dimension of A */ + int32_t K; /* the high rank dimension of A and B*/ + int32_t N; /* the low rank dimension of B */ + rknn_tensor_type in_dtype; /* the input buffer type */ + rknn_context rknn_ctx; /* rknn context handle */ +} rknn_matmul_t; + +typedef rknn_matmul_t* rknn_matmul_handle_t; + +/* rknn_matmul_load + + load input buffer and initial the context. + + [input]: + void* a the pointer of A Matrix buffer, number of element is K*M, alloced by user. + void* b the pointer to B Matrix buffer, number of element is K*N, alloced by user. + int32_t M the low rank dimension of A + int32_t K the high rank dimension of A + int32_t N the low rank dimension of B + rknn_tensor_type dtype; the input buffer type, now only support RKNN_TENSOR_UINT8 and RKNN_TENSOR_INT8. + [output]: + rknn_matmul_handle_t the handle of mamul. it will return NULL if failed. +*/ +rknn_matmul_handle_t rknn_matmul_load(void *a, void *b, int M, int K, int N, rknn_tensor_type dtype); + +/* rknn_matmul_run + + run Matmul with fixed shape. + + [input]: + rknn_matmul_handle_t matmul_handle the handle of mamul, get by rknn_matmul_load. + float *c the pointer of C Matrix buffer, number of element is 256x4096, alloced by user. + [output]: + int error code. +*/ +int rknn_matmul_run(rknn_matmul_handle_t matmul_handle, float *c); + +/* rknn_matmul_unload + + destroy the context. + + [input]: + rknn_matmul_handle_t matmul_handle the handle of mamul, get by rknn_matmul_load. + [output]: + int error code. +*/ +int rknn_matmul_unload(rknn_matmul_handle_t matmul_handle); + +#ifdef __cplusplus +} //extern "C" +#endif + +#endif //_RKNN_MATMUL_H \ No newline at end of file diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/lib/librknn_utils.so b/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/lib/librknn_utils.so new file mode 100644 index 0000000..a35f60e Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/lib/librknn_utils.so differ diff --git a/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/lib64/librknn_utils.so b/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/lib64/librknn_utils.so new file mode 100644 index 0000000..2ca9bf6 Binary files /dev/null and b/libs/rklibs/rknpu-1.7.3/rknn/rknn_utils/librknn_utils/lib64/librknn_utils.so differ diff --git a/libs/rklibs/rknpu/.gitignore b/libs/rklibs/rknpu/.gitignore new file mode 100644 index 0000000..70b84df --- /dev/null +++ b/libs/rklibs/rknpu/.gitignore @@ -0,0 +1 @@ +.gitreview diff --git a/libs/rklibs/rknpu/LICENSE b/libs/rklibs/rknpu/LICENSE new file mode 100644 index 0000000..0fbe7ec --- /dev/null +++ b/libs/rklibs/rknpu/LICENSE @@ -0,0 +1,28 @@ +// Copyright 2020 Rockchip Electronics Co.,Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/rklibs/rknpu/README.md b/libs/rklibs/rknpu/README.md new file mode 100644 index 0000000..08efd61 --- /dev/null +++ b/libs/rklibs/rknpu/README.md @@ -0,0 +1,140 @@ +# RKNPU + +本工程主要为Rockchip NPU提供驱动、示例等。 + + + +## 适用平台 + +- RK1808/RK1806 + +- RV1109/RV1126 + + + +**注意:** + +- **RK3399Pro用户态的库及驱动不在本工程**,请参考:https://github.com/airockchip/RK3399Pro_npu +- RK3566/RK3568/RK3588/RV1103/RV1106请参考:https://github.com/rockchip-linux/rknpu2 + + + +## RKNN Toolkit + +​ 在使用RKNN API进行部署之前,需要使用RKNN Toolkit将原始的模型转化成rknn模型。 + +- RK1808/RK1806/RV1109/RV1126/RK3399Pro 使用: https://github.com/rockchip-linux/rknn-toolkit +- RK3566/RK3568/RK3588/RV1103/RV1106使用:https://github.com/rockchip-linux/rknn-toolkit2 + +​ 具体的使用说明请参考相应的网址。 + + + +## NPU驱动说明 + +### NPU驱动目录说明 + +NPU的驱动在$SDK/external/rknpu/drivers/目录下或者https://github.com/rockchip-linux/rknpu/tree/master/drivers + +其中的编译、安装规则参考$SDK/buildroot/package/rockchip/rknpu/rknpu.mk + +主要目录包括: + +``` +drivers/ +├── common +├── linux-aarch64 +├── linux-aarch64-mini +├── linux-armhf +├── linux-armhf-mini +├── linux-armhf-puma +├── linux-armhf-puma-mini +├── npu_ko +``` + +- linux-aarch64:RK1808 full driver + +- linux-aarch64-mini: RK1808 mini driver + +- linux-armhf: RK1806 full driver + +- linux-armhf-mini: RK1806 mini driver + +- linux-armhf-puma: RV1109/RV1126 full driver + +- linux-armhf-puma-mini: RV1109/RV1126 mini driver + +- npu_ko:NPU内核驱动KO + + 在更新驱动时,需要同时更新用户态驱动及内核驱动,不然会产生类似错误: + + ``` + [ 1] HAL user version 6.4.6.5.351518 + [ 2] HAL kernel version 6.4.6.5.351518 + ``` + + 另外,npu ko与内核配置强相关,有可能会加载不成功,运行程序时,会产生类似错误: + + ``` + [ 1] Failed to open device: No such file or directory, Try again... + [ 2] Failed to open device: No such file or directory, Try again... + ``` + + 遇到这种情况需要联系开发团队解决。 + + + +### NPU full driver与mini driver的区别 + +主要包含以下几点: + +- Mini driver只支持预编译的rknn模型,如果跑非预编译模型,会出现 RKNN_ERR_MODEL_INVALID的错误,从1.6.0开始,会返回RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER的错误; + +- Full driver支持RKNN Toolkit的联机调试功能,mini driver不支持; + +- Mini driver 库大小比full driver小很多,以RV1109/RV1126 1.6.0驱动为例,full driver大小为87MB,mini driver大小为7.1MB,可以有效的节省flash大小。 + +- Mini driver库运行时占用的内存比full driver小。 + + + +### 手动更新NPU驱动 + +有时需要手动更新NPU驱动,只要将相关的驱动拷贝到对应的目录就可以。 + +- 更新RK1808的驱动: + +``` +adb push drivers/linux-aarch64/ / +adb push drivers/npu_ko/galcore.ko /lib/modules/galcore.ko +``` + +- 更新RK1808 mini driver: + +``` +adb push drivers/linux-aarch64-mini/ / +adb push drivers/npu_ko/galcore.ko /lib/modules/galcore.ko +``` +- 更新RV1109/RV1126的驱动: + +``` +adb push drivers/linux-armhf-puma/ / +adb push drivers/npu_ko/galcore_puma.ko /lib/modules/galcore.ko +``` + +- 更新RV1109/RV1126 mini driver: + +``` +adb push drivers/linux-armhf-puma-mini/ / +adb push drivers/npu_ko/galcore_puma.ko /lib/modules/galcore.ko +``` + +注意:adb不同版本的默认行为不一样,有些是直接push目录的,有些是push目录下的文件。因此替换完后,需要检查一下相关的库是否已经替换成功。比如通过md5sum检查对应文件的md5值是否相同。部分板子不支持adb命令,请通过ssh等其他方式拷贝文件到对应目录即可。 + +**另外,toybrick可能对文件路径进行了调整,因此最好通过toybrick自带的方式进行更新。** + + + +## librknn_api与librknn_runtime的区别 + +librknn_api是对librknn_runtime的封装,主要是为了减少对其他so的编译依赖,功能上并没有区别。检查驱动版本时,一般以librknn_runtime.so为准。 diff --git a/libs/rklibs/rknpu/drivers/README b/libs/rklibs/rknpu/drivers/README new file mode 100644 index 0000000..221dc00 --- /dev/null +++ b/libs/rklibs/rknpu/drivers/README @@ -0,0 +1,5 @@ +Mini driver (folder name contains mini) only support rknn models with precompiled mode. + +linux-aarch64: RK1808 +linux-arm: RK1806 +linux-armhf-puma: RV1109/RV1126 diff --git a/libs/rklibs/rknpu/drivers/common/etc/init.d/S05NPU_init b/libs/rklibs/rknpu/drivers/common/etc/init.d/S05NPU_init new file mode 100644 index 0000000..b9e97ab --- /dev/null +++ b/libs/rklibs/rknpu/drivers/common/etc/init.d/S05NPU_init @@ -0,0 +1,43 @@ +#!/bin/sh + +GetDDRBitWidth() +{ + bit=1 + a=1 + while [ 1 ];do + a=$((1<<$bit)) + if [ $a -ge $1 ];then + break + fi + let bit++ + done + return $(($bit<16?16:$bit)) +} + + + +case "$1" in + start) + printf "insmod NPU modules: " + cp /usr/lib/cl_*.h /tmp/ + DDR_SIZE=`cat /proc/meminfo|grep MemTotal|awk '{print $2}'` + GetDDRBitWidth $DDR_SIZE + bitWidth=$(($?+10)) + insmod /lib/modules/galcore.ko contiguousSize=0x400000 DDRBitWidth=$bitWidth + unset MAX_FREQ + read MAX_FREQ < /sys/class/devfreq/ffbc0000.npu/max_freq + echo $MAX_FREQ > /sys/class/devfreq/ffbc0000.npu/userspace/set_freq + [ $? = 0 ] && echo "OK" || echo "FAIL" +#start_rknn.sh & + ;; + stop) + ;; + restart|reload) + $0 stop + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac +exit 0 diff --git a/libs/rklibs/rknpu/drivers/common/etc/init.d/S60NPU_init b/libs/rklibs/rknpu/drivers/common/etc/init.d/S60NPU_init new file mode 100644 index 0000000..2b3e454 --- /dev/null +++ b/libs/rklibs/rknpu/drivers/common/etc/init.d/S60NPU_init @@ -0,0 +1,43 @@ +#!/bin/sh -x + +GetDDRBitWidth() +{ + bit=1 + a=1 + while [ 1 ];do + a=$((1<<$bit)) + if [ $a -ge $1 ];then + break + fi + let bit++ + done + return $(($bit<16?16:$bit)) +} + + + +case "$1" in + start) + printf "insmod NPU modules: " + cp /usr/lib/cl_*.h /tmp/ + DDR_SIZE=`cat /proc/meminfo|grep MemTotal|awk '{print $2}'` + GetDDRBitWidth $DDR_SIZE + bitWidth=$(($?+10)) + insmod /lib/modules/galcore.ko contiguousSize=0x400000 DDRBitWidth=$bitWidth + unset MAX_FREQ + read MAX_FREQ < /sys/class/devfreq/ffbc0000.npu/max_freq + echo $MAX_FREQ > /sys/class/devfreq/ffbc0000.npu/userspace/set_freq + [ $? = 0 ] && echo "OK" || echo "FAIL" + start_rknn.sh & + ;; + stop) + ;; + restart|reload) + $0 stop + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac +exit 0 diff --git a/libs/rklibs/rknpu/drivers/common/usr/bin/restart_rknn.sh b/libs/rklibs/rknpu/drivers/common/usr/bin/restart_rknn.sh new file mode 100644 index 0000000..a38bee3 --- /dev/null +++ b/libs/rklibs/rknpu/drivers/common/usr/bin/restart_rknn.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +killall start_rknn.sh > /dev/null 2>&1 +killall rknn_server > /dev/null 2>&1 +start_rknn.sh & diff --git a/libs/rklibs/rknpu/drivers/common/usr/bin/start_rknn.sh b/libs/rklibs/rknpu/drivers/common/usr/bin/start_rknn.sh new file mode 100644 index 0000000..4de64ac --- /dev/null +++ b/libs/rklibs/rknpu/drivers/common/usr/bin/start_rknn.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +export RKNN_SERVER_PLUGINS='/usr/lib/npu/rknn/plugins/' + +while true +do + sleep 1 + rknn_server #>/dev/null 2>&1 +done diff --git a/libs/rklibs/rknpu/drivers/common/usr/bin/start_usb.sh b/libs/rklibs/rknpu/drivers/common/usr/bin/start_usb.sh new file mode 100644 index 0000000..0dc8ab4 --- /dev/null +++ b/libs/rklibs/rknpu/drivers/common/usr/bin/start_usb.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +configfs_init() +{ + PID=$1 + CONFIG_STRING=$2 + mkdir -p /dev/usb-ffs -m 0770 + mkdir -p /dev/usb-ffs/$CONFIG_STRING -m 0770 + mount -t configfs none /sys/kernel/config + mkdir -p /sys/kernel/config/usb_gadget/rockchip -m 0770 + echo 0x2207 > /sys/kernel/config/usb_gadget/rockchip/idVendor + echo $PID > /sys/kernel/config/usb_gadget/rockchip/idProduct + mkdir -p /sys/kernel/config/usb_gadget/rockchip/strings/0x409 -m 0770 + echo "0123456789ABCDEF" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/serialnumber + echo "rockchip" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/manufacturer + echo "rk3xxx" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/product + mkdir -p /sys/kernel/config/usb_gadget/rockchip/configs/b.1 -m 0770 + mkdir -p /sys/kernel/config/usb_gadget/rockchip/configs/b.1/strings/0x409 -m 0770 + echo 500 > /sys/kernel/config/usb_gadget/rockchip/configs/b.1/MaxPower + echo \"$CONFIG_STRING\" > /sys/kernel/config/usb_gadget/rockchip/configs/b.1/strings/0x409/configuration +} + +function_init() +{ + CONFIG_STRING=$1 + mkdir -p /sys/kernel/config/usb_gadget/rockchip/functions/ffs.$CONFIG_STRING + rm -f /sys/kernel/config/usb_gadget/rockchip/configs/b.1/ffs.* + ln -s /sys/kernel/config/usb_gadget/rockchip/functions/ffs.$CONFIG_STRING /sys/kernel/config/usb_gadget/rockchip/configs/b.1/ffs.$CONFIG_STRING +} + +case "$1" in +adb) + killall adbd start_rknn.sh rknn_server > /dev/null 2>&1 + + echo "none" > /sys/kernel/config/usb_gadget/rockchip/UDC + + umount /sys/kernel/config + umount /dev/usb-ffs/ntb > /dev/null 2>&1 + rm -rf /dev/usb-ffs/ntb + + configfs_init 0x0006 adb + function_init adb + + # START_APP_BEFORE_UDC + mkdir -p /dev/usb-ffs/adb + mount -o uid=2000,gid=2000 -t functionfs adb /dev/usb-ffs/adb + export service_adb_tcp_port=5555 + adbd& + sleep 1 + + UDC=`ls /sys/class/udc/| awk '{print $1}'` + echo $UDC > /sys/kernel/config/usb_gadget/rockchip/UDC + # START_APP_AFTER_UDC + + start_rknn.sh & + + ;; +ntb) + killall adbd start_rknn.sh rknn_server > /dev/null 2>&1 + + echo "none" > /sys/kernel/config/usb_gadget/rockchip/UDC + + umount /sys/kernel/config + umount /dev/usb-ffs/adb > /dev/null 2>&1 + rm -rf /dev/usb-ffs/adb + + configfs_init 0x1808 ntb + function_init ntb + + # START_APP_BEFORE_UDC + mkdir -p /dev/usb-ffs/ntb + mount -o uid=2000,gid=2000 -t functionfs ntb /dev/usb-ffs/ntb + + start_rknn.sh & + + ;; +*) + echo "Usage: $0 {adb|ntb}" + exit 1 +esac + +exit 0 diff --git a/libs/rklibs/rknpu/drivers/common/usr/lib/cl_viv_vx_ext.h b/libs/rklibs/rknpu/drivers/common/usr/lib/cl_viv_vx_ext.h new file mode 100644 index 0000000..a6ecd50 --- /dev/null +++ b/libs/rklibs/rknpu/drivers/common/usr/lib/cl_viv_vx_ext.h @@ -0,0 +1,1456 @@ + + + +#ifndef _GC_VX_H +#define _GC_VX_H 1 + +#ifdef _VIV_VX_EXTENSION + +#pragma OPENCL EXTENSION CL_VIV_asm : enable + +#ifndef VX_VERSION +#define VX_VERSION 1 +#endif + +typedef enum _VXC_FilterMode +{ + VXC_FM_BOX = 0, + VXC_FM_Guassian = 1, + VXC_FM_SobelX = 2, + VXC_FM_SobelY = 3, + VXC_FM_ScharrX = 4, + VXC_FM_ScharrY = 5, + VXC_FM_Max = 8, + VXC_FM_Min = 9, + VXC_FM_Median = 10 +} vxc_filter_mode; + +typedef enum _VXC_RoundMode +{ + VXC_RM_Truncate = 0, + VXC_RM_TowardZero = 0, + VXC_RM_TowardInf = 1, + VXC_RM_ToNearestEven = 2 +} vxc_round_mode; + +typedef enum _VXC_ScatteredOffsetType +{ + VXC_OFFSET_UNSIGNED32 = 0, + VXC_OFFSET_SIGNED32 = 1, + VXC_OFFSET_UNSIGNED16 = 2, + VXC_OFFSET_SIGNED16 = 3, + VXC_OFFSET_UNSIGNED8 = 4, + VXC_OFFSET_SIGNED8 = 5, +} VXC_ScatteredOffsetType; + +typedef enum _VXC_AtomicOp +{ + VXC_ATOMIC_OP_ADD = 0, + VXC_ATOMIC_OP_MIN = 1, + VXC_ATOMIC_OP_MAX = 2, + VXC_ATOMIC_OP_OR = 3, + VXC_ATOMIC_OP_AND = 4, + VXC_ATOMIC_OP_XOR = 5, + VXC_ATOMIC_OP_XCHG = 6, +}VXC_AtomicOpType; + +#define VXC_CLAMP_BITMASK 0x00400000 +#define VXC_PREADJ_BITMASK 0x00200000 +#define VXC_RANGEPI_BITMASK 0x00100000 +#define VXC_FILTER_BITMASK 0x000F0000 +#define VXC_START_BIN_BITMASK 0x0000F000 +#define VXC_END_BIN_BITMASK 0x00000F00 +#define VXC_SOURCE_BIN_BITMASK 0x000000F0 +#define VXC_ROUNDING_MODE_BITMASK 0x0000000C +#define VXC_ENABLEBOOL_BITMASK 0x00000002 +#define VXC_SIGNEXT_BITMASK 0x00000001 + + +#define VXC_OFFSET_TYPE_BITMASK 0x00070000 +#define VXC_OFFSET_TYPE_SHIFT 16 + + +#define VXC_ATOM_OP_BITMASK 0x00380000 +#define VXC_ATOM_OP_SHIFT 19 + +#define VXC_MODIFIER(StartBin, EndBin, SourceBin, RoundingMode, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) | \ + ((RoundingMode << 2)&VXC_ROUNDING_MODE_BITMASK) \ + ) + +#define VXC_MODIFIER_SIGNEXT(StartBin, EndBin, SourceBin, SignExt, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + ((SignExt)&VXC_SIGNEXT_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_MAGPHASE(StartBin, EndBin, SourceBin, NoPreAdjust, RangePi) \ + ( \ + (VXC_CLAMP_BITMASK) | \ + (((RangePi) << 20)&VXC_RANGEPI_BITMASK) | \ + (((NoPreAdjust) << 21)&VXC_PREADJ_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_CLAMP(StartBin, EndBin, SourceBin, EnableBool) \ + ( \ + (((EnableBool) << 1)&VXC_ENABLEBOOL_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_FILTER(StartBin, EndBin, SourceBin, Filter, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + (((Filter) << 16)&VXC_FILTER_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_BIN(StartBin, EndBin, Clamp) \ + ( \ + (((Clamp) << 22)&VXC_CLAMP_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_GATHER(StartBin, EndBin, SourceBin, OffsetType) \ + ( \ + (((OffsetType) << VXC_OFFSET_TYPE_SHIFT)&VXC_OFFSET_TYPE_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_SCATTER(StartBin, EndBin, SourceBin, OffsetType) \ + ( \ + (((OffsetType) << VXC_OFFSET_TYPE_SHIFT)&VXC_OFFSET_TYPE_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + +#define VXC_MODIFIER_ATOMIC_S(StartBin, EndBin, SourceBin, OffsetType, AtomOp) \ + ( \ + (((OffsetType) << VXC_OFFSET_TYPE_SHIFT)&VXC_OFFSET_TYPE_BITMASK) | \ + (((AtomOp) << VXC_ATOM_OP_SHIFT)&VXC_ATOM_OP_BITMASK) | \ + (((StartBin) << 12)&VXC_START_BIN_BITMASK) | \ + (((EndBin) << 8)&VXC_END_BIN_BITMASK) | \ + (((SourceBin) << 4)&VXC_SOURCE_BIN_BITMASK) \ + ) + + +#define VXC_MODIFIER_SetDestClamp(VxModifier, Clamp) ((VxModifier) | (((Clamp) << 22)&VXC_CLAMP_BITMASK)) + +#define VXC_DEFAULT_MODIFIER (-1) + +typedef unsigned int vxc_modifier; + + +typedef _viv_char2_packed vxc_char2; +typedef _viv_char4_packed vxc_char4; +typedef _viv_char8_packed vxc_char8; +typedef _viv_char16_packed vxc_char16; +typedef struct _vxc_char32 +{ + vxc_char16 hi; + vxc_char16 lo; +} vxc_char32; + + +typedef _viv_uchar2_packed vxc_uchar2; +typedef _viv_uchar4_packed vxc_uchar4; +typedef _viv_uchar8_packed vxc_uchar8; +typedef _viv_uchar16_packed vxc_uchar16; +typedef struct _vxc_uchar32 +{ + vxc_uchar16 hi; + vxc_uchar16 lo; +} vxc_uchar32; + + +typedef _viv_short2_packed vxc_short2; +typedef _viv_short4_packed vxc_short4; +typedef _viv_short8_packed vxc_short8; +typedef struct _vxc_short16 +{ + vxc_short8 hi; + vxc_short8 lo; +} vxc_short16; + + +typedef _viv_ushort2_packed vxc_ushort2; +typedef _viv_ushort4_packed vxc_ushort4; +typedef _viv_ushort8_packed vxc_ushort8; +typedef struct _vxc_ushort16 +{ + vxc_ushort8 hi; + vxc_ushort8 lo; +} vxc_ushort16; + + +typedef int vxc_int; +typedef int2 vxc_int2; +typedef int4 vxc_int4; +typedef int8 vxc_int8; +typedef int16 vxc_int16; + + +typedef uint vxc_uint; +typedef uint2 vxc_uint2; +typedef uint4 vxc_uint4; +typedef uint8 vxc_uint8; +typedef uint16 vxc_uint16; + + +typedef float vxc_float; +typedef float2 vxc_float2; +typedef float4 vxc_float4; +typedef float8 vxc_float8; +typedef float16 vxc_float16; + + +typedef half vxc_half; +typedef _viv_half2_packed vxc_half2; +typedef _viv_half4_packed vxc_half4; +typedef _viv_half8_packed vxc_half8; +typedef struct _vxc_half16 +{ + vxc_half8 hi; + vxc_half8 lo; +} vxc_half16; + +typedef uint16 vxc_512bits; +typedef uint4 vxc_128bits; + +typedef vxc_512bits VXC_512Bits; +typedef vxc_128bits VXC_128Bits; +typedef vxc_modifier VXC_Modifier_t ; +typedef vxc_round_mode VXC_RoundMode; +typedef vxc_filter_mode VXC_FilterMode; + +#ifndef VX_USE_INTRINSIC +#define VX_USE_INTRINSIC 0 +#endif + +enum VXC_OP { + VXC_OP_abs_diff = 3, + VXC_OP_iadd, + VXC_OP_iacc_sq, + VXC_OP_lerp, + VXC_OP_filter, + VXC_OP_mag_phase, + VXC_OP_mul_shift, + VXC_OP_dp16x1, + VXC_OP_dp8x2, + VXC_OP_dp4x4, + VXC_OP_dp2x8, + VXC_OP_clamp, + VXC_OP_bi_linear, + VXC_OP_select_add, + VXC_OP_atomic_add, + VXC_OP_bit_extract, + VXC_OP_bit_replace, + VXC_OP_dp32x1, + VXC_OP_dp16x2, + VXC_OP_dp8x4, + VXC_OP_dp4x8, + VXC_OP_dp2x16, + VXC_OP_dp32x1_b, + VXC_OP_dp16x2_b, + VXC_OP_dp8x4_b, + VXC_OP_dp4x8_b, + VXC_OP_dp2x16_b, + VXC_OP_img_load, + VXC_OP_img_load_3d, + VXC_OP_img_store, + VXC_OP_img_store_3d, + VXC_OP_vload2, + VXC_OP_vload3, + VXC_OP_vload4, + VXC_OP_vload8, + VXC_OP_vload16, + VXC_OP_vstore2, + VXC_OP_vstore3, + VXC_OP_vstore4, + VXC_OP_vstore8, + VXC_OP_vstore16, + VXC_OP_index_add, + VXC_OP_vert_min3, + VXC_OP_vert_max3, + VXC_OP_vert_med3, + VXC_OP_horz_min3, + VXC_OP_horz_max3, + VXC_OP_horz_med3, + VXC_OP_error, + OP_bit_extract, + VXC_OP_dp16x1_b, + VXC_OP_dp8x2_b, + VXC_OP_dp4x4_b, + VXC_OP_dp2x8_b, + VXC_OP_gather, + VXC_OP_gather_b, + VXC_OP_scatter, + VXC_OP_scatter_b, + VXC_OP_atomic_s, + VXC_OP_atomic_s_b, +}; + +enum eVXC_ERROR +{ + ERROR_DP2x16_NOT_SUPPORTED, + ERROR_IADD_NOT_SUPPORTED, + ERROR_SELECTADD_NOT_SUPPORTED, + ERROR_BITREPLACE_NOT_SUPPORTED +}; + +#define VXC_OP1(Op, Dest, Src0) _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, Src0) + +#define VXC_OP2(Op, Dest, Src0, Src1) \ + do { \ + int _t1; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t1); \ + } while(0) + +#define VXC_OP3(Op, Dest, Src0, Src1, Src2) \ + do { \ + int _t1, _t2; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t2); \ + } while(0) + +#define VXC_OP3_NoDest(Op, Src0, Src1, Src2) \ + do { \ + int _t1, _t2, _t3; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(INTRINSIC_ST, _t3, VXC_OP_##Op, _t2); \ + } while(0) + + +#define VXC_OP4(Op, Dest, Src0, Src1, Src2, Src3) \ + do { \ + int _t1, _t2, _t3; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t3); \ + } while(0) + +#define VXC_OP4_NoDest(Op, Src0, Src1, Src2, Src3) \ + do { \ + int _t1, _t2, _t3, _t4; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(INTRINSIC_ST, _t4, VXC_OP_##Op, _t3); \ + } while(0) + +#define VXC_OP4_ST(Op, Dest, Src0, Src1, Src2, Src3) \ + do { \ + int _t1, _t2, _t3; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(INTRINSIC_ST, Dest, VXC_OP_##Op, _t3);\ + } while(0) + +#define VXC_OP5(Op, Dest, Src0, Src1, Src2, Src3, Src4) \ + do { \ + int _t1, _t2, _t3, _t4; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(PARAM_CHAIN, _t4, _t3, Src4); \ + _viv_asm(INTRINSIC, Dest, VXC_OP_##Op, _t4); \ + } while(0) + +#define VXC_OP5_NoDest(Op, Src0, Src1, Src2, Src3, Src4) \ + do { \ + int _t1, _t2, _t3, _t4, _t5; \ + _viv_asm(PARAM_CHAIN, _t1, Src0, Src1); \ + _viv_asm(PARAM_CHAIN, _t2, _t1, Src2); \ + _viv_asm(PARAM_CHAIN, _t3, _t2, Src3); \ + _viv_asm(PARAM_CHAIN, _t4, _t3, Src4); \ + _viv_asm(INTRINSIC_ST, _t5, VXC_OP_##Op, _t4); \ + } while(0) + + +#define VXC_5BITOFFSET_XY(offsetX, offsetY) ((((offsetY) & 0x1F) << 5) | ((offsetX) & 0x1F)) + + + +#if !VX_USE_INTRINSIC +#define VXC_AbsDiff(Dest, Src0, Src1, Info) VXC_OP3(abs_diff, Dest, Src0, Src1, Info) +#define VXC_IAccSq(Dest, Src0, Src1, Imm, Info) VXC_OP4(iacc_sq, Dest, Src0, Src1, Imm, Info) +#define VXC_Lerp(Dest, Src0, Src1, Src2, Info) VXC_OP4(lerp, Dest, Src0, Src1, Src2, Info) + +#define VXC_MulShift(Dest, Src0, Src1, Imm, Info) VXC_OP4(mul_shift, Dest, Src0, Src1, Imm, Info) +#define VXC_Clamp(Dest, Src0, Src1, Src2, Info) VXC_OP4(clamp, Dest, Src0, Src1, Src2, Info) +#define VXC_AtomicAdd(Dest, Base, Offset, Data, Info) VXC_OP4_ST(atomic_add, Dest, Base, Offset, Data, Info) +#define VXC_BitExtract(Dest, Src0, Src1, Src2, Info) VXC_OP4(bit_extract, Dest, Src0, Src1, Src2, Info) + +#define VXC_DP16x1(Dest, Src0, Src1, Info, U512) VXC_OP4(dp16x1, Dest, Src0, Src1, Info, U512) +#define VXC_DP8x2(Dest, Src0, Src1, Info, U512) VXC_OP4(dp8x2, Dest, Src0, Src1, Info, U512) +#define VXC_DP4x4(Dest, Src0, Src1, Info, U512) VXC_OP4(dp4x4, Dest, Src0, Src1, Info, U512) +#define VXC_DP2x8(Dest, Src0, Src1, Info, U512) VXC_OP4(dp2x8, Dest, Src0, Src1, Info, U512) + +#define VXC_DP32x1(Dest, Src0, Src1, Info, U512) VXC_OP4(dp32x1, Dest, Src0, Src1, Info, U512) +#define VXC_DP16x2(Dest, Src0, Src1, Info, U512) VXC_OP4(dp16x2, Dest, Src0, Src1, Info, U512) +#define VXC_DP8x4(Dest, Src0, Src1, Info, U512) VXC_OP4(dp8x4, Dest, Src0, Src1, Info, U512) +#define VXC_DP4x8(Dest, Src0, Src1, Info, U512) VXC_OP4(dp4x8, Dest, Src0, Src1, Info, U512) +#if (VX_VERSION >= 2) +#define VXC_DP2x16(Dest, Src0, Src1, Info, U512) VXC_OP1(error, ERROR_DP2x16_NOT_SUPPORTED) +#else +#define VXC_DP2x16(Dest, Src0, Src1, Info, U512) VXC_OP4(dp2x16, Dest, Src0, Src1, Info, U512) +#endif + +#if (VX_VERSION >= 2) + +#define VXC_DP16x1_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp16x1_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP8x2_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp8x2_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP4x4_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp4x4_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP2x8_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp2x8_b, Dest, Src0, Src1, Src2, Info, U512) +#endif + + +#define VXC_DP32x1_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp32x1_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP16x2_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp16x2_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP8x4_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp8x4_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP4x8_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp4x8_b, Dest, Src0, Src1, Src2, Info, U512) +#define VXC_DP2x16_b(Dest, Src0, Src1, Src2, Info, U512) VXC_OP5(dp2x16_b, Dest, Src0, Src1, Src2, Info, U512) + +#define VXC_Gather(Dest, BaseAddr, Offsets, GatherInfo) VXC_OP3(gather, Dest, BaseAddr, Offsets, GatherInfo) +#define VXC_Gather_b(Dest, BaseAddr, Offsets, Offsets_b, GatherInfo) VXC_OP4(gather_b, Dest, BaseAddr, Offsets, Offsets_b, GatherInfo) + +#define VXC_Scatter(BaseAddr, Offsets, Data, ScatterInfo) VXC_OP4_NoDest(scatter, BaseAddr, Offsets, Data, ScatterInfo) +#define VXC_Scatter_b(BaseAddr, Offsets, Offsets_b, Data, ScatterInfo) VXC_OP5_NoDest(scatter_b, BaseAddr, Offsets, Offsets_b, Data, ScatterInfo) + +#define VXC_AtomicS(Dest, BaseAddr, Offsets, Data, AtomicSInfo) VXC_OP4(atomic_s, Dest, BaseAddr, Offsets, Data, AtomicSInfo) +#define VXC_AtomicS_b(Dest, BaseAddr, Offsets, Offsets_b, Data, AtomicSInfo) VXC_OP5(atomic_s_b, Dest, BaseAddr, Offsets, Offsets_b, Data, AtomicSInfo) + + + +#define VXC_ReadImage(Dest, Image, Coord, Offset, Info) VXC_OP4(img_load, Dest, Image, Coord, Offset, Info) +#define VXC_WriteImage(Image, Coord, Color, Info) VXC_OP4_NoDest(img_store, Image, Coord, Color, Info) + + +#define VXC_ReadImage2DArray(Dest, Image, Coord, Offset, Info) \ + do { \ + int8 desc; \ + _viv_asm(COPY, desc, Image, sizeof(desc)); \ + _viv_asm(CLAMP0MAX, (Coord).w, (Coord).z, desc.s5 - 1); \ + int baseAddr = (int)(Coord).w *desc.s4 + desc.s0; \ + _viv_asm(MOV, (Coord).w, baseAddr); \ + VXC_OP4(img_load_3d, Dest, Image, (Coord).xyww, Offset, Info); \ + } while (0) +#define VXC_WriteImage2DArray(Image, Coord, Color, Info) \ + do { \ + int8 desc; \ + _viv_asm(COPY, desc, Image, sizeof(desc)); \ + _viv_asm(CLAMP0MAX, (Coord).w, (Coord).z, desc.s5 - 1); \ + int baseAddr = (int)(Coord).w *(desc).s4 + desc.s0; \ + _viv_asm(MOV, (Coord).w, baseAddr); \ + VXC_OP4_NoDest(img_store_3d, Image, (Coord).xyww, Color, Info); \ + } while (0) + + +#define VXC_ReadImage3D(Dest, Image, Coord, Offset, Info) VXC_OP4(img_load_3d, Dest, Image, Coord, Offset, Info) +#define VXC_WriteImage3D(Image, Coord, Color, Info) VXC_OP4_NoDest(img_store_3d, Image, Coord, Color, Info) + +#define VXC_Vload2(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload2, Dest, Pointer, byteOffset); } while(0) +#define VXC_Vload4(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload4, Dest, Pointer, byteOffset); } while(0) +#define VXC_Vload8(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload8, Dest, Pointer, byteOffset); } while(0) +#define VXC_Vload16(Dest, Pointer, Offset) do { int byteOffset = ((int)sizeof((Dest)))*(Offset); VXC_OP2(vload16, Dest, Pointer, byteOffset); } while(0) + +#define VXC_Vstore2(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore2, Pointer, byteOffset, Data); } while(0) +#define VXC_Vstore4(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore4, Pointer, byteOffset, Data); } while(0) +#define VXC_Vstore8(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore8, Pointer, byteOffset, Data); } while(0) +#define VXC_Vstore16(Pointer, Offset, Data) do { int byteOffset = ((int)sizeof((Data)))*(Offset); VXC_OP3_NoDest(vstore16, Pointer, byteOffset, Data); } while(0) + + +#define VXC_IndexAdd(Dest, Src0, Src1, Src2, Info) VXC_OP4(index_add, Dest, Src0, Src1, Src2, Info) +#define VXC_VertMin3(Dest, Src0, Src1, Src2, Info) VXC_OP4(vert_min3, Dest, Src0, Src1, Src2, Info) +#define VXC_VertMax3(Dest, Src0, Src1, Src2, Info) VXC_OP4(vert_max3, Dest, Src0, Src1, Src2, Info) +#define VXC_VertMed3(Dest, Src0, Src1, Src2, Info) VXC_OP4(vert_med3, Dest, Src0, Src1, Src2, Info) +#define VXC_HorzMin3(Dest, Src0, Info) VXC_OP2(horz_min3, Dest, Src0, Info) +#define VXC_HorzMax3(Dest, Src0, Info) VXC_OP2(horz_max3, Dest, Src0, Info) +#define VXC_HorzMed3(Dest, Src0, Info) VXC_OP2(horz_med3, Dest, Src0, Info) + +#if (VX_VERSION == 2) +#define VXC_BiLinear(Dest, Src0, Src1, Src2, Info) \ + do { \ + int endBin = ((Info) & VXC_END_BIN_BITMASK) >> 8; \ + int roundMode = ((Info) & VXC_ROUNDING_MODE_BITMASK) >> 2; \ + int clamp = ((Info) & VXC_CLAMP_BITMASK) >> 22; \ + int mod1 = VXC_MODIFIER(0, endBin + 1, 0, roundMode, clamp); \ + int4 bitMask = { 0x00000000, 0x00000008, 0x00000010, 0x00000018}; \ + typeof (Dest) bi1; \ + uint4 bi2; \ + int bi3, bi4; \ + VXC_Lerp(bi1, Src0, Src1, (Src2).y, mod1); \ + _viv_asm(PARAM_CHAIN, bi3, bi1.x!, bitMask); \ + _viv_asm(PARAM_CHAIN, bi4, bi3, 8); \ + _viv_asm(INTRINSIC, bi2, OP_bit_extract, bi4); \ + VXC_Lerp(Dest, bi2!, bi2.y!, (Src2).x, Info); \ + } while (0) + +#define VXC_BitReplace(Dest, Src0, Src1, Src2, Info) +#define VXC_IAdd(Dest, Src0, Src1, Src2, Info) +#define VXC_MagPhase(Dest, Src0, Src1, Info) +#define VXC_SelectAdd(Dest, Src0, Src1, U512, Info) VXC_OP1(error, ERROR_SELECTADD_NOT_SUPPORTED) + +#define VXC_Filter_Box(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Guassian(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_SobelX(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_SobelY(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_ScharrX(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_ScharrY(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Max(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Min(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter_Median(Dest, Src0, Src1, Src2, Info) +#define VXC_Filter(Dest, Src0, Src1, Src2, Info) do { \ + int filter = (((Info) >> 16)&0x0F); \ + if (filter == VXC_FM_BOX) { VXC_Filter_Box(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Guassian) { VXC_Filter_Guassian(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_SobelX) { VXC_Filter_SobelX(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_SobelY) { VXC_Filter_SobelY(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_ScharrX) { VXC_Filter_ScharrX(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_ScharrY) { VXC_Filter_ScharrY(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Max) { VXC_Filter_Max(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Min) { VXC_Filter_Min(Dest, Src0, Src1, Src2, Info); } \ + if (filter == VXC_FM_Median) { VXC_Filter_Median(Dest, Src0, Src1, Src2, Info); } \ + } while (0) + +#else + +#define VXC_BiLinear(Dest, Src0, Src1, Src2, Info) VXC_OP4(bi_linear, Dest, Src0, Src1, Src2, Info) +#define VXC_BitReplace(Dest, Src0, Src1, Src2, Info) VXC_OP4(bit_replace, Dest, Src0, Src1, Src2, Info) +#define VXC_IAdd(Dest, Src0, Src1, Src2, Info) VXC_OP4(iadd, Dest, Src0, Src1, Src2, Info) +#define VXC_MagPhase(Dest, Src0, Src1, Info) VXC_OP3(mag_phase, Dest, Src0, Src1, Info) +#define VXC_SelectAdd(Dest, Src0, Src1, U512, Info) VXC_OP4(select_add, Dest, Src0, Src1, U512, Info) +#define VXC_Filter(Dest, Src0, Src1, Src2, Info) VXC_OP4(filter, Dest, Src0, Src1, Src2, Info) +#endif + +#else + +#ifdef __cplusplus +extern "c" { +#endif + +#define viv_vx_api_only 0 + +#if viv_vx_api_only +#define _RET0_ ; +#define _RET_ ; +#define _EXT_ extern +#else +#define _RET0_ { return (0); } +#define _RET_ { return ; } +#define _EXT_ +#endif + + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_char16 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_char8 a) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_short8 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_short4 a) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_ushort8 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastP_uc(vxc_half8 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastP_uc(vxc_half4 a) _RET0_ + + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_uchar16 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_short8 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_short4 a) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_ushort8 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_icastP_c(vxc_half8 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastP_c(vxc_half4 a) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_uchar16 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_char16 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_char8 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_short8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_short4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastP_us(vxc_half8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastP_us(vxc_half4 a) _RET0_ + + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_uchar16 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_char16 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_char8 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_ushort8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastP_s(vxc_half8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastP_s(vxc_half4 a) _RET0_ + + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_uchar16 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_char16 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_char8 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_ushort8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastP_h(vxc_short8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastP_h(vxc_short4 a) _RET0_ + + + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_icastD_uc(vxc_char16 a) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_char8 a) _RET0_ + +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_short8 a) _RET0_ +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_short4 a) _RET0_ + +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_ushort8 a) _RET0_ +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_uchar8 viv_intrinsic_vx_icastD_uc(vxc_half8 a) _RET0_ +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_half4 a) _RET0_ + +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_int4 a) _RET0_ +_EXT_ vxc_uchar2 viv_intrinsic_vx_icastD_uc(vxc_int2 a) _RET0_ + +_EXT_ vxc_uchar4 viv_intrinsic_vx_icastD_uc(vxc_uint4 a) _RET0_ +_EXT_ vxc_uchar2 viv_intrinsic_vx_icastD_uc(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_char16 viv_intrinsic_vx_icastD_c(vxc_uchar16 a) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_uchar8 a) _RET0_ + +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_short8 a) _RET0_ +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_short4 a) _RET0_ + +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_ushort8 a) _RET0_ +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_char8 viv_intrinsic_vx_icastD_c(vxc_half8 a) _RET0_ +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_half4 a) _RET0_ + +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_int4 a) _RET0_ +_EXT_ vxc_char2 viv_intrinsic_vx_icastD_c(vxc_int2 a) _RET0_ + +_EXT_ vxc_char4 viv_intrinsic_vx_icastD_c(vxc_uint4 a) _RET0_ +_EXT_ vxc_char2 viv_intrinsic_vx_icastD_c(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_uchar8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_uchar4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_char8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_char4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_short8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_short4 a) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vx_icastD_us(vxc_half8 a) _RET0_ +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_half4 a) _RET0_ + +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_int4 a) _RET0_ +_EXT_ vxc_ushort2 viv_intrinsic_vx_icastD_us(vxc_int2 a) _RET0_ + +_EXT_ vxc_ushort4 viv_intrinsic_vx_icastD_us(vxc_uint4 a) _RET0_ +_EXT_ vxc_ushort2 viv_intrinsic_vx_icastD_us(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_uchar8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_uchar4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_char8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_char4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_ushort8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_short8 viv_intrinsic_vx_icastD_s(vxc_half8 a) _RET0_ +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_half4 a) _RET0_ + +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_int4 a) _RET0_ +_EXT_ vxc_short2 viv_intrinsic_vx_icastD_s(vxc_int2 a) _RET0_ + +_EXT_ vxc_short4 viv_intrinsic_vx_icastD_s(vxc_uint4 a) _RET0_ +_EXT_ vxc_short2 viv_intrinsic_vx_icastD_s(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_uchar8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_uchar4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_char8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_char4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_ushort8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_ushort4 a) _RET0_ + +_EXT_ vxc_half8 viv_intrinsic_vx_icastD_h(vxc_short8 a) _RET0_ +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_short4 a) _RET0_ + +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_int4 a) _RET0_ +_EXT_ vxc_half2 viv_intrinsic_vx_icastD_h(vxc_int2 a) _RET0_ + +_EXT_ vxc_half4 viv_intrinsic_vx_icastD_h(vxc_uint4 a) _RET0_ +_EXT_ vxc_half2 viv_intrinsic_vx_icastD_h(vxc_uint2 a) _RET0_ + + +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_char4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_uchar4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_short4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_ushort4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_half4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_uint4 a) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vx_icastD_i(vxc_float4 a) _RET0_ + +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_char2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_uchar2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_short2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_ushort2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_half2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_uint2 a) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vx_icastD_i(vxc_float2 a) _RET0_ + + +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_char4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_uchar4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_short4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_ushort4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_half4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_int4 a) _RET0_ +_EXT_ vxc_uint4 viv_intrinsic_vx_icastD_ui(vxc_float4 a) _RET0_ + +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_char2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_uchar2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_short2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_ushort2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_half2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_int2 a) _RET0_ +_EXT_ vxc_uint2 viv_intrinsic_vx_icastD_ui(vxc_float2 a) _RET0_ + + +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_char4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_uchar4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_short4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_ushort4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_half4 a) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vx_icastD_f(vxc_int4 a) _RET0_ + +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_char2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_uchar2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_short2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_ushort2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_half2 a) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vx_icastD_f(vxc_int2 a) _RET0_ + + + +#define VXC_SWIZZLE_MASK8_ALL() 0xFFFF +#define VXC_SWIZZLE_MASK8(E0, E1, E2, E3, E4, E5, E6, E7) \ + (((E0) * 0x3) | ((E1) * (0x3 << 2)) | ((E2) * (0x3 << 4)) | \ + ((E3) * (0x3 << 6)) | ((E4) * (0x3 << 8)) | ((E5) * (0x3 << 10)) | \ + ((E6) * (0x3 << 12)) | ((E7) * (0x3 << 14)) ) + +#define VXC_SWIZZLE_MASK16_ALL() 0xFFFF +#define VXC_SWIZZLE_MASK16(E0, E1, E2, E3, E4, E5, E6, E7, E9, E10, E11, E12, E13, E14, E15) \ + (((E0) * 0x1) | ((E1) * (0x1 << 1)) | ((E2) * (0x1 << 2)) | \ + ((E3) * (0x1 << 1)) | ((E4) * (0x1 << 4)) | ((E5) * (0x1 << 5)) | \ + ((E6) * (0x1 << 6)) | ((E7) * (0x1 << 7)) | ((E8) * (0x1 << 8)) | \ + ((E9) * (0x1 << 9)) | ((E10) * (0x1 << 10)) | ((E11) * (0x1 << 11)) | \ + ((E12) * (0x1 << 12)) | ((E13) * (0x1 << 13)) | ((E14) * (0x1 << 14)) | \ + ((E15) * (0x1 << 15)) ) + +#define VXC_SWIZZLE8(S0, S1, S2, S3, S4, S5, S6, S7) \ + (uint)((S0) << 0 | (S1) << 4 | (S2) << 8 | (S3) << 12 | \ + (S4) << 16 | (S5) << 20 | (S6) << 24 | (S7) << 28 ) + + +_EXT_ vxc_char16 viv_intrinsic_vx_read_imagec (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_read_imageuc (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_read_images (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_read_imageus (image2d_t image, int2 coord) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vx_read_imageh (image2d_t image, int2 coord) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_read_imagec (image1d_t image, int coord) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_read_imageuc (image1d_t image, int coord) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_read_images (image1d_t image, int coord) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_read_imageus (image1d_t image, int coord) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vx_read_imageh (image1d_t image, int coord) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vx_read_imagec (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_read_imageuc (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_read_images (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_read_imageus (image1d_array_t image, int2 coord) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vx_read_imageh (image1d_array_t image, int2 coord) _RET0_ + + +_EXT_ void viv_intrinsic_vx_write_imagec (image2d_t image, int2 coord, vxc_char16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageuc (image2d_t image, int2 coord, vxc_uchar16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_images (image2d_t image, int2 coord, vxc_short8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageus (image2d_t image, int2 coord, vxc_ushort8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageh (image2d_t image, int2 coord, vxc_half8 color) _RET_ + +_EXT_ void viv_intrinsic_vx_write_imagec (image1d_t image, int coord, vxc_char16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageuc (image1d_t image, int coord, vxc_uchar16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_images (image1d_t image, int coord, vxc_short8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageus (image1d_t image, int coord, vxc_ushort8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageh (image1d_t image, int coord, vxc_half8 color) _RET_ + +_EXT_ void viv_intrinsic_vx_write_imagec (image1d_array_t image, int2 coord, vxc_char16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageuc (image1d_array_t image, int2 coord, vxc_uchar16 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_images (image1d_array_t image, int2 coord, vxc_short8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageus (image1d_array_t image, int2 coord, vxc_ushort8 color) _RET_ +_EXT_ void viv_intrinsic_vx_write_imageh (image1d_array_t image, int2 coord, vxc_half8 color) _RET_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_AbsDiff_uc(vxc_uchar16 a, vxc_uchar16 b) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_AbsDiff_c(vxc_char16 a, vxc_char16 b) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_AbsDiff_s(vxc_short8 a, vxc_short8 b) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_AbsDiff_us(vxc_ushort8 a, vxc_ushort8 b) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_IAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_IAdd_c(vxc_char16 a, vxc_char16 b, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_IAdd_s(vxc_short8 a, vxc_short8 b, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_IAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_IAccSq_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_IAccSq_c(vxc_char16 a, vxc_char16 b, uint Imm) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_IAccSq_s(vxc_short8 a, vxc_short8 b, uint Imm) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_IAccSq_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_Lerp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_float c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_Lerp_c(vxc_char16 a, vxc_char16 b, vxc_float c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_Lerp_s(vxc_short8 a, vxc_short8 b, vxc_float c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_Lerp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_float c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_Filter_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_filter_mode f) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_Filter_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_filter_mode f) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_Filter_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_filter_mode f) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_Filter_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_filter_mode f) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_MagPhase_uc(vxc_uchar16 a, vxc_uchar16 b) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_MagPhase_c(vxc_char16 a, vxc_char16 b) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_MagPhase_s(vxc_short8 a, vxc_short8 b) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_MagPhase_us(vxc_ushort8 a, vxc_ushort8 b) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_MulShift_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_MulShift_c(vxc_char16 a, vxc_char16 b, uint Imm) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_MulShift_s(vxc_short8 a, vxc_short8 b, uint Imm) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_MulShift_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm) _RET0_ + +/* Clamp: clamps up to 16 values to a min and.or max value + * + * Syntax: + * r = Clamp(a, b, c) ; + * r = ClampBoolean(a, b, c) ; // boolean mode + * Semantics: + * r[i] = clamp(a[i], b[i], c[i]) ; i E [0, elem(r) ) + * + * In boolean mode it will write a 0 in the result if the value + * is inside the specified min/max range, otherwise all 1s will + * be written to the result. + */ +_EXT_ vxc_uchar16 viv_intrinsic_vx_Clamp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_Clamp_c(vxc_char16 a, vxc_char16 b, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_Clamp_s(vxc_short8 a, vxc_short8 b, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_Clamp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vx_ClampBoolean_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_ClampBoolean_c(vxc_char16 a, vxc_char16 b, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_ClampBoolean_s(vxc_short8 a, vxc_short8 b, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_ClampBoolean_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_BiLinear_uc(vxc_uchar16 a, vxc_uchar16 b, float2 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_BiLinear_c(vxc_char16 a, vxc_char16 b, float2 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_BiLinear_s(vxc_short8 a, vxc_short8 b, float2 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_BiLinear_us(vxc_ushort8 a, vxc_ushort8 b, float2 c) _RET0_ + + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_SelectAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_512bits c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_SelectAdd_c(vxc_char16 a, vxc_char16 b, vxc_512bits c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_SelectAdd_s(vxc_short8 a, vxc_short8 b, vxc_512bits c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_SelectAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_512bits c) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vx_AtomicAdd_uc(vxc_uchar16 * a, vxc_int offset, vxc_uchar16 c) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vx_AtomicAdd_c(vxc_char16 * a, vxc_int offset, vxc_char16 c) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vx_AtomicAdd_s(vxc_short8 * a, vxc_int offset, vxc_short8 c) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vx_AtomicAdd_us(vxc_ushort8 * a, vxc_int offset, vxc_ushort8 c) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_BitExtract_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vx_BitExtract_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vx_BitReplace_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vx_BitReplace_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c) _RET0_ + + + + + + +_EXT_ vxc_char16 viv_intrinsic_vxmc_read_imagec (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_read_imageuc (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_read_images (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_read_imageus (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_read_imageh (image2d_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vxmc_read_imagec (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_read_imageuc (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_read_images (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_read_imageus (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_read_imageh (image1d_t image, int coord, int offsetX, vxc_modifier modifier) _RET0_ + +_EXT_ vxc_char16 viv_intrinsic_vxmc_read_imagec (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_read_imageuc (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_read_images (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_read_imageus (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_read_imageh (image1d_array_t image, int2 coord, int offsetXY, vxc_modifier modifier) _RET0_ + + +_EXT_ void viv_intrinsic_vxmc_write_imagec (image2d_t image, int2 coord, vxc_char16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageuc (image2d_t image, int2 coord, vxc_uchar16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_images (image2d_t image, int2 coord, vxc_short8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageus (image2d_t image, int2 coord, vxc_ushort8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageh (image2d_t image, int2 coord, vxc_half8 color, vxc_modifier modifier) _RET_ + +_EXT_ void viv_intrinsic_vxmc_write_imagec (image1d_t image, int coord, vxc_char16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageuc (image1d_t image, int coord, vxc_uchar16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_images (image1d_t image, int coord, vxc_short8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageus (image1d_t image, int coord, vxc_ushort8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageh (image1d_t image, int coord, vxc_half8 color, vxc_modifier modifier) _RET_ + +_EXT_ void viv_intrinsic_vxmc_write_imagec (image1d_array_t image, int2 coord, vxc_char16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageuc (image1d_array_t image, int2 coord, vxc_uchar16 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_images (image1d_array_t image, int2 coord, vxc_short8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageus (image1d_array_t image, int2 coord, vxc_ushort8 color, vxc_modifier modifier) _RET_ +_EXT_ void viv_intrinsic_vxmc_write_imageh (image1d_array_t image, int2 coord, vxc_half8 color, vxc_modifier modifier) _RET_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_AbsDiff_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_AbsDiff_c(vxc_char16 a, vxc_char16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_AbsDiff_s(vxc_short8 a, vxc_short8 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_AbsDiff_us(vxc_ushort8 a, vxc_ushort8 b, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_IAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_IAdd_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_IAdd_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_IAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_IAccSq_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_IAccSq_c(vxc_char16 a, vxc_char16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_IAccSq_s(vxc_short8 a, vxc_short8 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_IAccSq_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_Lerp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_float c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_Lerp_c(vxc_char16 a, vxc_char16 b, vxc_float c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_Lerp_s(vxc_short8 a, vxc_short8 b, vxc_float c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_Lerp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_float c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_Filter_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_Filter_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_Filter_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_Filter_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_MagPhase_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_MagPhase_c(vxc_char16 a, vxc_char16 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_MagPhase_s(vxc_short8 a, vxc_short8 b, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_MagPhase_us(vxc_ushort8 a, vxc_ushort8 b, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_MulShift_uc(vxc_uchar16 a, vxc_uchar16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_MulShift_c(vxc_char16 a, vxc_char16 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_MulShift_s(vxc_short8 a, vxc_short8 b, uint Imm, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_MulShift_us(vxc_ushort8 a, vxc_ushort8 b, uint Imm, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uint viv_intrinsic_vxmc_DP16x1(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int viv_intrinsic_vxmc_DP16x1(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_float viv_intrinsic_vxmc_DP16x1(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint2 viv_intrinsic_vxmc_DP8x2(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vxmc_DP8x2(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_float2 viv_intrinsic_vxmc_DP8x2(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint4 viv_intrinsic_vxmc_DP4x4(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vxmc_DP4x4(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_float4 viv_intrinsic_vxmc_DP4x4(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uchar8 viv_intrinsic_vxmc_DP2x8(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_char8 viv_intrinsic_vxmc_DP2x8(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_half8 viv_intrinsic_vxmc_DP2x8(vxc_half8 a, vxc_half8 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint viv_intrinsic_vxmc_DP32x1(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int viv_intrinsic_vxmc_DP32x1(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint2 viv_intrinsic_vxmc_DP16x2(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vxmc_DP16x2(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint4 viv_intrinsic_vxmc_DP8x4(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vxmc_DP8x4(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_DP4x8(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_DP4x8(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_DP2x16(vxc_uchar16 a, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_DP2x16(vxc_char16 a, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uint viv_intrinsic_vxmc_DP32x1_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int viv_intrinsic_vxmc_DP32x1_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint2 viv_intrinsic_vxmc_DP16x2_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int2 viv_intrinsic_vxmc_DP16x2_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uint4 viv_intrinsic_vxmc_DP8x4_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_int4 viv_intrinsic_vxmc_DP8x4_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_DP4x8_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_DP4x8_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_DP2x16_b(vxc_uchar16 a_hi, vxc_uchar16 a_lo, vxc_uchar16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_DP2x16_b(vxc_char16 a_hi, vxc_char16 a_lo, vxc_char16 b, vxc_modifier modifier, vxc_512bits u) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_Clamp_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_Clamp_c(vxc_char16 a, vxc_char16 b, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_Clamp_s(vxc_short8 a, vxc_short8 b, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_Clamp_us(vxc_ushort8 a, vxc_ushort8 b, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_BiLinear_uc(vxc_uchar16 a, vxc_uchar16 b, float2 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_BiLinear_c(vxc_char16 a, vxc_char16 b, float2 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_BiLinear_s(vxc_short8 a, vxc_short8 b, float2 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_BiLinear_us(vxc_ushort8 a, vxc_ushort8 b, float2 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_SelectAdd_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_512bits c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_SelectAdd_c(vxc_char16 a, vxc_char16 b, vxc_512bits c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_SelectAdd_s(vxc_short8 a, vxc_short8 b, vxc_512bits c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_SelectAdd_us(vxc_ushort8 a, vxc_ushort8 b, vxc_512bits c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_AtomicAdd_uc(vxc_uchar16 * a, vxc_int offset, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_char16 viv_intrinsic_vxmc_AtomicAdd_c(vxc_char16 * a, vxc_int offset, vxc_char16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_short8 viv_intrinsic_vxmc_AtomicAdd_s(vxc_short8 * a, vxc_int offset, vxc_short8 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_AtomicAdd_us(vxc_ushort8 * a, vxc_int offset, vxc_ushort8 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_BitExtract_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar8 viv_intrinsic_vxmc_BitExtract_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ + + +_EXT_ vxc_ushort8 viv_intrinsic_vxmc_BitReplace_us(vxc_ushort8 a, vxc_ushort8 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ +_EXT_ vxc_uchar16 viv_intrinsic_vxmc_BitReplace_uc(vxc_uchar16 a, vxc_uchar16 b, vxc_uchar16 c, vxc_modifier modifier) _RET0_ + + +vxc_char2 viv_intrinsic_vx_vload2(size_t Offset, char *Pointer) { + vxc_char2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_char2)); + return dest; +} + +vxc_char4 viv_intrinsic_vx_vload4(size_t Offset, char *Pointer) { + vxc_char4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_char4)); + return dest; +} + +vxc_char8 viv_intrinsic_vx_vload8(size_t Offset, char *Pointer) { + vxc_char8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_char8)); + return dest; +} + +vxc_char16 viv_intrinsic_vx_vload16(size_t Offset, char *Pointer) { + vxc_char16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_char16)); + return dest; +} + +vxc_uchar2 viv_intrinsic_vx_vload2(size_t Offset, uchar *Pointer) { + vxc_uchar2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_uchar2)); + return dest; +} + +vxc_uchar4 viv_intrinsic_vx_vload4(size_t Offset, uchar *Pointer) { + vxc_uchar4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_uchar4)); + return dest; +} + +vxc_uchar8 viv_intrinsic_vx_vload8(size_t Offset, uchar *Pointer) { + vxc_uchar8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_uchar8)); + return dest; +} + +vxc_uchar16 viv_intrinsic_vx_vload16(size_t Offset, uchar *Pointer) { + vxc_uchar16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_uchar16)); + return dest; +} + +vxc_short2 viv_intrinsic_vx_vload2(size_t Offset, short *Pointer) { + vxc_short2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_short2)); + return dest; +} + +vxc_short4 viv_intrinsic_vx_vload4(size_t Offset, short *Pointer) { + vxc_short4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_short4)); + return dest; +} + +vxc_short8 viv_intrinsic_vx_vload8(size_t Offset, short *Pointer) { + vxc_short8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_short8)); + return dest; +} + +vxc_short16 viv_intrinsic_vx_vload16(size_t Offset, short *Pointer) { + vxc_short16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_short16)); + return dest; +} + +vxc_ushort2 viv_intrinsic_vx_vload2(size_t Offset, ushort *Pointer) { + vxc_ushort2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_ushort2)); + return dest; +} + +vxc_ushort4 viv_intrinsic_vx_vload4(size_t Offset, ushort *Pointer) { + vxc_ushort4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_ushort4)); + return dest; +} + +vxc_ushort8 viv_intrinsic_vx_vload8(size_t Offset, ushort *Pointer) { + vxc_ushort8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_ushort8)); + return dest; +} + +vxc_ushort16 viv_intrinsic_vx_vload16(size_t Offset, ushort *Pointer) { + vxc_ushort16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_ushort16)); + return dest; +} + +vxc_half2 viv_intrinsic_vx_vload2(size_t Offset, half *Pointer) { + vxc_half2 dest; + VXC_OP2(vload2, dest, Pointer, Offset * sizeof(vxc_half2)); + return dest; +} + +vxc_half4 viv_intrinsic_vx_vload4(size_t Offset, half *Pointer) { + vxc_half4 dest; + VXC_OP2(vload4, dest, Pointer, Offset * sizeof(vxc_half4)); + return dest; +} + +vxc_half8 viv_intrinsic_vx_vload8(size_t Offset, half *Pointer) { + vxc_half8 dest; + VXC_OP2(vload8, dest, Pointer, Offset * sizeof(vxc_half8)); + return dest; +} + +vxc_half16 viv_intrinsic_vx_vload16(size_t Offset, half *Pointer) { + vxc_half16 dest; + VXC_OP2(vload16, dest, Pointer, Offset * sizeof(vxc_half16)); + return dest; +} + + +void viv_intrinsic_vx_vstore2(vxc_char2 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_char2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_char4 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_char4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_char8 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_char8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_char16 Data, size_t Offset, char * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_char16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_uchar2 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_uchar2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_uchar4 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_uchar4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_uchar8 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_uchar8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_uchar16 Data, size_t Offset, uchar * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_uchar16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_short2 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_short2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_short4 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_short4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_short8 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_short8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_short16 Data, size_t Offset, short * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_short16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_ushort2 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_ushort2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_ushort4 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_ushort4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_ushort8 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_ushort8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_ushort16 Data, size_t Offset, ushort * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_ushort16), Data); +} + +void viv_intrinsic_vx_vstore2(vxc_half2 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore2, Pointer, Offset * sizeof(vxc_half2), Data); +} + +void viv_intrinsic_vx_vstore4(vxc_half4 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore4, Pointer, Offset * sizeof(vxc_half4), Data); +} + +void viv_intrinsic_vx_vstore8(vxc_half8 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore8, Pointer, Offset * sizeof(vxc_half8), Data); +} + +void viv_intrinsic_vx_vstore16(vxc_half16 Data, size_t Offset, half * Pointer) { + VXC_OP3_NoDest(vstore16, Pointer, Offset * sizeof(vxc_half16), Data); +} + +#undef _RET0_ +#undef _RET_ +#undef _EXT_ + +#ifdef __cplusplus +} +#endif + +#endif +typedef struct +{ + size_t size; + global char* item; +} vx_array_char; + +typedef struct +{ + size_t size; + global unsigned char* item; +} vx_array_uchar; + +typedef struct +{ + size_t size; + global short* item; +} vx_array_short; + +typedef struct +{ + size_t size; + global unsigned short* item; +} vx_array_ushort; + +typedef struct +{ + size_t size; + global int* item; +} vx_array_int; + +typedef struct +{ + size_t size; + global unsigned int* item; +} vx_array_uint; + + +typedef struct +{ + size_t size; + global float * item; +} vx_array_float; + +typedef struct +{ + size_t size; + global unsigned char* item; +} vx_lut_uchar; + +typedef struct +{ + size_t size; + global unsigned short* item; +} vx_lut_ushort; + +typedef struct +{ + size_t columns; + size_t rows; + global short* matrix; + uint scale; +} vx_convolution; + +typedef struct +{ + size_t columns; + size_t rows; + global char* matrix; +} vx_matrix_char; + +typedef struct +{ + size_t columns; + size_t rows; + global unsigned char* matrix; +} vx_matrix_uchar; + +typedef struct +{ + size_t columns; + size_t rows; + global short* matrix; +} vx_matrix_short; + +typedef struct +{ + size_t columns; + size_t rows; + global unsigned short* matrix; +} vx_matrix_ushort; + +typedef struct +{ + size_t columns; + size_t rows; + global int* matrix; +} vx_matrix_int; + +typedef struct +{ + size_t columns; + size_t rows; + global unsigned int* matrix; +} vx_matrix_uint; + +typedef struct +{ + size_t columns; + size_t rows; + global float* matrix; +} vx_matrix_float; + +typedef struct +{ + int type; + uint value; + uint lower; + uint upper; + uint trueValue; + uint falseValue; +} vx_threshold; + +typedef struct { + int dst_width; + int dst_height; + global float* ptr; +} vx_remap; + +typedef struct +{ + int bins; + int rang; + int offset; + float window_r; + global int* ptr; +} vx_distribution; + +typedef struct _vxc_pyramid +{ + float scale; + uint width; + uint height; + uint format; + uint levelCount; + _viv_image2d_array_t imageArray; +} vxc_pyramid; + +#endif + +#endif diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libArchModelSw.so b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..c26c2bc Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libGAL.so b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libGAL.so new file mode 100644 index 0000000..4c5fdaa Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libGAL.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libNNArchPerf.so b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..816a76e Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..687ebe9 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libOpenVXU.so b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..8a1185c Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libVSC_Lite.so b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libVSC_Lite.so new file mode 100644 index 0000000..22d0cc3 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/libVSC_Lite.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/librknn_runtime.so b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..1264ce3 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64-mini/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/bin/rknn_server b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/bin/rknn_server new file mode 100644 index 0000000..fb5ab85 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/bin/rknn_server differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libArchModelSw.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..c26c2bc Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libCLC.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libCLC.so new file mode 100644 index 0000000..100c901 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libCLC.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libGAL.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libGAL.so new file mode 100644 index 0000000..87da367 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libGAL.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNArchPerf.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..816a76e Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNGPUBinary.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNGPUBinary.so new file mode 100644 index 0000000..8987d4d Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNGPUBinary.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNVXCBinary.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNVXCBinary.so new file mode 100644 index 0000000..0793f10 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libNNVXCBinary.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenCL.so.1.2 b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenCL.so.1.2 new file mode 100644 index 0000000..94ce057 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenCL.so.1.2 differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..23f5ae6 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenVXU.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..8a1185c Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOvx12VXCBinary.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOvx12VXCBinary.so new file mode 100644 index 0000000..fbdc57d Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libOvx12VXCBinary.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libVSC.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libVSC.so new file mode 100644 index 0000000..ed6e52a Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libVSC.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libneuralnetworks.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libneuralnetworks.so new file mode 100644 index 0000000..ea9768d Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/libneuralnetworks.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/librknn_runtime.so b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/librknn_runtime.so new file mode 100644 index 0000000..a207e3f Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-aarch64/usr/lib/librknn_runtime.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/bin/rknn_server b/libs/rklibs/rknpu/drivers/linux-armhf/usr/bin/rknn_server new file mode 100644 index 0000000..3731a46 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/bin/rknn_server differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libArchModelSw.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libArchModelSw.so new file mode 100644 index 0000000..267dfbb Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libArchModelSw.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libCLC.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libCLC.so new file mode 100644 index 0000000..642b34b Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libCLC.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libGAL.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libGAL.so new file mode 100644 index 0000000..109e22a Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libGAL.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNArchPerf.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNArchPerf.so new file mode 100644 index 0000000..e75cf4b Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNArchPerf.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNGPUBinary.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNGPUBinary.so new file mode 100644 index 0000000..609e6a2 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNGPUBinary.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNVXCBinary.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNVXCBinary.so new file mode 100644 index 0000000..f7c1460 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libNNVXCBinary.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenCL.so.1.2 b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenCL.so.1.2 new file mode 100644 index 0000000..73e5bfe Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenCL.so.1.2 differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenVX.so.1.2 b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenVX.so.1.2 new file mode 100644 index 0000000..e896ca1 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenVX.so.1.2 differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenVXU.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenVXU.so new file mode 100644 index 0000000..ccae677 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOpenVXU.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOvx12VXCBinary.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOvx12VXCBinary.so new file mode 100644 index 0000000..ac8f547 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libOvx12VXCBinary.so differ diff --git a/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libneuralnetworks.so b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libneuralnetworks.so new file mode 100644 index 0000000..2266108 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/linux-armhf/usr/lib/libneuralnetworks.so differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore.ko new file mode 100644 index 0000000..8ebbbaa Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore.ko differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore_fedora.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore_fedora.ko new file mode 100644 index 0000000..1a51915 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore_fedora.ko differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore_puma.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore_puma.ko new file mode 100644 index 0000000..4644e82 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore_puma.ko differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore_puma_tb.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore_puma_tb.ko new file mode 100644 index 0000000..1bebce6 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore_puma_tb.ko differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk1806.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk1806.ko new file mode 100644 index 0000000..f69273f Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk1806.ko differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk3399pro-npu-pcie.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk3399pro-npu-pcie.ko new file mode 100644 index 0000000..3c69e5e Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk3399pro-npu-pcie.ko differ diff --git a/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk3399pro-npu.ko b/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk3399pro-npu.ko new file mode 100644 index 0000000..f694c17 Binary files /dev/null and b/libs/rklibs/rknpu/drivers/npu_ko/galcore_rk3399pro-npu.ko differ diff --git a/libs/rklibs/rknpu/rknn/README.md b/libs/rklibs/rknpu/rknn/README.md new file mode 100644 index 0000000..4846874 --- /dev/null +++ b/libs/rklibs/rknpu/rknn/README.md @@ -0,0 +1,47 @@ +# Introduction +RKNN-Toolkit is a software development kit that provides users with model conversion, inference and performance evaluation on PC and Rockchip NPU platforms(RK1808/RK1806/RK3399Pro/RV1109/RV1126). +RKNN-Toolkit-Lite provides Python programming interfaces for Rockchip NPU platform to help users deploy RKNN models and accelerate the implementation of AI applications. +Note: +- For the deployment of the RKNN model, please refer to: + - RK1808/RK1806/RV1109/RV1126: https://github.com/rockchip-linux/rknpu + - RK3399Pro: https://github.com/airockchip/RK3399Pro_npu +- For RK3566/RK3568/RK3588/RK3588S/RV1103/RV1106, please refer to: + - https://github.com/rockchip-linux/rknn-toolkit2 + - https://github.com/rockchip-linux/rknpu2 +# Download +From version 1.3.0, some wheel packages are larger than 100MB, can not be uploaded directly. So, you need to go to the releases page to download. +You can download from releases page: https://github.com/rockchip-linux/rknn-toolkit/releases +- All wheel packages are in compressed file: [rknn-toolkit-v1.7.3-packages.tar.gz](https://github.com/rockchip-linux/rknn-toolkit/releases/download/v1.7.3/rknn-toolkit-v1.7.3-packages.tar.gz "rknn-toolkit-v1.7.3-packages.tar.gz") or [rknn-toolkit-v1.7.3-packages.zip](https://github.com/rockchip-linux/rknn-toolkit/releases/download/v1.7.3/rknn-toolkit-v1.7.3-packages.zip "rknn-toolkit-v1.7.3-packages.zip ") +- All examples, docs and platform-tools are in compressed file: [Source code(zip)](https://github.com/rockchip-linux/rknn-toolkit/archive/v1.7.3.zip "Source code(zip)") or [Source code(tar.gz)](https://github.com/rockchip-linux/rknn-toolkit/archive/v1.7.3.tar.gz "Source code(tar.gz)") +- You can also download all packages, docker image, examples, docs and platform-tools from baidu cloud: [rknn-toolkit](https://eyun.baidu.com/s/3bqgIr0N "RKNN-Toolkit"), fetch code: rknn +- You can download RKNN Toolkit Lite packages and examples from [rknn-toolkit-lite](rknn-toolkit-lite) +# Checksums +## MD5 +``` +7d672e911c26af34112bbfcfed86aef7 rknn_toolkit-1.7.3-cp35-cp35m-linux_aarch64.whl +e751a1c2782879dedbe3904448e07105 rknn_toolkit-1.7.3-cp36-cp36m-linux_x86_64.whl +20db420731b4976b15b0fbddef9984e3 rknn_toolkit-1.7.3-cp36-cp36m-macosx_10_15_x86_64.whl +1ebba11b78766cd320a470bb397578cb rknn_toolkit-1.7.3-cp36-cp36m-win_amd64.whl +0cdd9c288a748bcef84848fd5dc12d80 rknn_toolkit-1.7.3-cp37-cp37m-linux_aarch64.whl +f06e1f88864c5a4759e4258cb536c38d rknn_toolkit-1.7.3-cp37-cp37m-macosx_10_15_x86_64.whl +8e5c63a241b809b78ca65d226344d0eb rknn_toolkit-1.7.3-cp38-cp38-linux_x86_64.whl + +a24f157407e16bc255fc387404eb2030 rknn-toolkit-v1.7.3-packages.tar.gz +8dbeeecb06b9201b9f464909ad8d9544 rknn-toolkit-v1.7.3-packages.zip + +4b50d8966908e80567843c3623e33d46 rknn_toolkit_lite-1.7.3-cp36-cp36m-linux_aarch64.whl +dc669361506646104e823d569055b849 rknn_toolkit_lite-1.7.3-cp36-cp36m-linux_x86_64.whl +15a26aad303769f8f2f38f5888529a57 rknn_toolkit_lite-1.7.3-cp36-cp36m-macosx_10_15_x86_64.whl +cd490d13dc1e40e24ab76100a9b82ced rknn_toolkit_lite-1.7.3-cp36-cp36m-win_amd64.whl +17f10789ec42fdc687c3c02ef6dc1141 rknn_toolkit_lite-1.7.3-cp37-cp37m-linux_aarch64.whl +030a09ed522620aa6dfb4ccac578518b rknn_toolkit_lite-1.7.3-cp37-cp37m-linux_armv7l.whl +079aaf9c2c39b2c2a6858688ed733973 rknn_toolkit_lite-1.7.3-cp37-cp37m-macosx_10_15_x86_64.whl +b1b32a4e8803dd9303ab1a72ae8deccf rknn_toolkit_lite-1.7.3-cp38-cp38-linux_x86_64.whl +``` +# Feedback and Community Support +- QQ Group Chat: 1025468710 (full, please join group 2) +- QQ Group Chat2: 547021958 +

+ + +
diff --git a/libs/rklibs/rknpu/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_CN.pdf b/libs/rklibs/rknpu/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_CN.pdf new file mode 100644 index 0000000..9e80b4b --- /dev/null +++ b/libs/rklibs/rknpu/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_CN.pdf @@ -0,0 +1,1290 @@ + 瑞芯微电子股份有限公司 + +密级状态:绝密( ) 秘密( ) 内部( ) 公开(√ ) + +Rockchip User Guide RKNN API + + (技术部,图形计算平台中心) + +文件状态: 当前版本: 1.7.3 + HPC +[ ] 正在修改 作 者: 2022-8-13 + 熊伟 +[√] 正式发布 完成日期: 2022-8-13 + + 审 核: + + 完成日期: + + 瑞芯微电子股份有限公司 + Rockchips Semiconductor Co . , Ltd + + (版本所有,翻版必究) + 瑞芯微电子股份有限公司 + + 更新记录 + + 版本 修改人 修改日期 修改说明 核定人 +v0.9.6 杨华聪 +V0.9.7 杨华聪 2018-12-20 初始版本 熊伟 +V0.9.8 杨华聪 熊伟 + 2019-01-16 1) 添加 RKNN SDK 开发流程说明 卓鸿添 +V1.3.3 卓鸿添 2019-06-05 2) 添加 RKNN Python API 说明 卓鸿添 +V1.4.0 卓鸿添 2020-06-02 1) 添加 RKNN API 库的说明 卓鸿添 + 2) 更新 rknn example 和 rknn-toolkit 路 +v1.6.0 洪启飞 卓鸿添 + 径 +v1.6.1 洪启飞 3) 修正文档中错误 卓鸿添 +v1.7.0 洪启飞 1)支持 RV1126/RV1109 +v1.7.1 洪启飞 2)删除 python 支持说明,关于 python +v1.7.3 洪启飞 支持见 RKNN Toolkit Lite 相关文档。 + + 2020-09-10 1)增加错误码 + + 2021-1-15 1)增加 Matmul 高级 API 说明 + 2)增加一组设置输入和一组获取输出 + 的接口 + 3)增加做量化和反量化的算法说明 + 4)增加 NPU 驱动说明 + 5)增加 FAQ + + 2021-4-26 1)更新 Matmul 高级 API 说明 + + 2021-8-5 1)更新版本号 卓鸿添 + 2021-12-2 1)rknn_inputs_set 接口单通道输入性能 卓鸿添 + 2022-8-13 优化 卓鸿添 + 2)已知 Bug 修复 + 1)更新版本号 + + 1 + 瑞芯微电子股份有限公司 + +1 主要功能说明 ................................................................................................................................................... 5 +2 硬件平台 ........................................................................................................................................................... 5 +3 使用说明 ........................................................................................................................................................... 5 + + 3.1 RKNN SDK 开发流程 ............................................................................................................................... 5 + 3.2 RKNN C API ...............................................................................................................................................6 + + 3.2.1 RKNN API 库 ...................................................................................................................................... 6 + 3.2.2 EXAMPLE 使用说明 ..........................................................................................................................6 + 3.2.3 API 流程说明 ...................................................................................................................................... 7 + + 3.2.3.1 API 内部处理流程 ..........................................................................................................................................9 + 3.2.3.2 量化和反量化 ..............................................................................................................................................10 + 3.2.3.3 零拷贝 .......................................................................................................................................................... 11 + 3.2.4 API 详细说明 .................................................................................................................................... 12 + 3.2.4.1 rknn_init ........................................................................................................................................................ 12 + 3.2.4.2 rknn_destroy ..................................................................................................................................................13 + 3.2.4.3 rknn_query .................................................................................................................................................... 13 + 3.2.4.4 rknn_inputs_set ............................................................................................................................................. 16 + 3.2.4.5 rknn_inputs_map ...........................................................................................................................................16 + 3.2.4.6 rknn_inputs_sync ...........................................................................................................................................18 + 3.2.4.7 rknn_inputs_unmap .......................................................................................................................................18 + 3.2.4.8 rknn_run ........................................................................................................................................................ 19 + 3.2.4.9 rknn_outputs_get ...........................................................................................................................................19 + 3.2.4.10 rknn_outputs_release .................................................................................................................................. 20 + 3.2.4.11 rknn_outputs_map ....................................................................................................................................... 21 + 3.2.4.12 rknn_outputs_sync .......................................................................................................................................21 + + 2 + 瑞芯微电子股份有限公司 + 3.2.4.13 rknn_outputs_unmap ...................................................................................................................................22 + 3.2.5 RKNN 数据结构定义 ....................................................................................................................... 22 + 3.2.5.1 rknn_input_output_num ................................................................................................................................ 22 + 3.2.5.2 rknn_tensor_attr ............................................................................................................................................23 + 3.2.5.3 rknn_input ..................................................................................................................................................... 24 + 3.2.5.4 rknn_tensor_mem ..........................................................................................................................................24 + 3.2.5.5 rknn_output ................................................................................................................................................... 25 + 3.2.5.6 rknn_perf_detail ............................................................................................................................................ 25 + 3.2.5.7 rknn_sdk_version .......................................................................................................................................... 25 + 3.2.6 RKNN 返回值错误码 ....................................................................................................................... 26 +4 高级 API 使用说明 ........................................................................................................................................ 27 + 4.1 MATMUL 算子库 ........................................................................................................................................27 + 4.1.1 简介 ...................................................................................................................................................27 + 4.1.2 数据结构定义 ...................................................................................................................................27 + 4.1.3 详细 API 说明 .................................................................................................................................. 28 + 4.1.3.1 rknn_matmul_load ........................................................................................................................................ 28 + 4.1.3.2 rknn_matmul_run ..........................................................................................................................................28 + 4.1.3.3 rknn_matmul_unload .................................................................................................................................... 29 + 4.1.4 实现限制 ...........................................................................................................................................29 + 4.1.4.1 维度限制 ......................................................................................................................................................29 + 4.1.4.2 输入数据类型限制 ......................................................................................................................................30 + 4.1.5 基准测试 ...........................................................................................................................................30 +5 NPU 驱动说明 .................................................................................................................................................31 + 5.1.1 NPU 驱动目录说明 ...........................................................................................................................31 + 5.1.2 NPU full driver 与 mini driver 的区别 ..............................................................................................31 + + 3 + 瑞芯微电子股份有限公司 +6 FAQ ..................................................................................................................................................................32 + + 6.1.1 输入输出数据格式问题 ...................................................................................................................32 + 6.1.2 输入输出接口使用问题 ...................................................................................................................33 + 6.1.3 API 调用流程问题 ............................................................................................................................ 33 + 6.1.4 性能问题 ...........................................................................................................................................34 + + 4 + 瑞芯微电子股份有限公司 + +1 主要功能说明 + + RKNN SDK 为 RK1808 等 带 有 NPU 的 平 台 提 供 编 程 接 口 , 能 够 帮 助 用 户 部 署 使 用 +RKNN-Toolkit 导出的 RKNN 模型,加速 AI 应用的落地。 + +2 硬件平台 + +本文档适用如下硬件平台: + 1) RK1808、RK1806 + 2) RV1126、RV1109 + +注意:本文档不适用 RK3399Pro +下面的说明以 RK1808 为例,也同时适用上述其他平台。 + +3 使用说明 + +3.1 RKNN SDK 开发流程 + + 在使用 RKNN SDK 之前,用户首先需要使用 RKNN-Toolkit 工具将用户的模型转换为 RKNN + +模型,用户可以在 https://github.com/rockchip-linux/rknn-toolkit 获取工具的完整 + +安装包及使用文档。 + 成功转换生成 RKNN 模型之后,用户可以先通过 RKNN-Toolkit 连接 RK1808 等开发板进行联 + +机调试,确保模型的精度性能符合要求。 + 得到 RKNN 模型文件之后,用户可以选择使用 C 或 Python 接口在 RK1808 等平台开发应用, + +后续章节将说明如何在 RK1808 等平台上基于 RKNN SDK 进行开发。 + + 5 + 瑞芯微电子股份有限公司 + +3.2 RKNN C API + +3.2.1 RKNN API 库 + + RKNN SDK 所提供的库和头文件位于/external/rknpu/rknn/ +rknn_api/librknn_api 目录下,开发者可以在自己应用中引用即可开发应用。 + + 需要注意的是,RK1808 和 RK3399Pro 平台的 RKNN API 是兼容的,两者开发的应用程序 +可以很方便地移植。但是使用过程中需要注意要区分两个平台的 librknn_api.so,如果开发者使 +用 RK3399Pro 的 librknn_api.so 将无法在 RK1808 平台上运行。开发者可以使用以下方法来区分 +librknn_api.so 的平台: + + $ strings librknn_api.so |grep version + librknn_api version 1.7.3 (cf7f05f build: 2022-08-13 10:59:33) + +3.2.2 EXAMPLE 使用说明 + + SDK 提供了 Linux 平台的 MobileNet 图像分类、MobileNet SSD 目标检测以及 YoloV3 Tiny + 目标检测 Demo。这些 Demo 能够为客户基于 RKNN SDK 开发自己的 AI 应用提供参考。Demo + 代码位于/external/rknpu/rknn/rknn_api/examples 目录。下面以 rknn_mobilenet_demo 为例 + 来讲解如何快速上手运行。 + + 1) 编译 Demo + + cd examples/rknn_mobilenet_demo + # 修改 build.sh 中的 GCC_COMPILER,指向目标平台的编译器 + ./build.sh + 2) 部署到 RK1808 设备 + + adb push install/rknn_mobilenet_demo /userdata/ + + 3) 运行 Demo + + 6 + 瑞芯微电子股份有限公司 + + adb shell + cd /userdata/rknn_mobilenet_demo/ + ./rknn_mobilenet_demo model/mobilenet_v1_rk180x.rknn + model/dog_224x224.jpg #for RK1808 + ./rknn_mobilenet_demo model/mobilenet_v1_rv1109_rv1126.rknn + model/dog_224x224.jpg #for RV1109/RV1126 + +3.2.3 API 流程说明 + + 从 RKNN API V1.6.0 版本开始,新增加了一组设置输入的函数: +  rknn_inputs_map +  rknn_inputs_sync +  rknn_inputs_unmap + 以及一组获取输出的函数: +  rknn_outputs_map +  rknn_outputs_sync +  rknn_outputs_unmap + 在设置输入时,用户可以使用 rknn_inputs_set 或者 rknn_inputs_map 系列函数。获取推理 + 的输出时,使用 rknn_outputs_get 或者 rknn_outputs_map 系列函数。特定场景下,使用 map 系 + 列接口可以减少内存拷贝的次数,提高完整推理的速度。 + rknn_inputs_map 系列接口和 rknn_inputs_set 接口的调用流程不同,rknn_outputs_map 系 + 列接口和 rknn_outputs_get 接口的调用流程也不同。两个系列 API 调用流程差异如图 3-1 所示, + 其中(a)为 set/get 系列接口调用流程,(b)为 map 系列接口调用流程。 + + 7 + 瑞芯微电子股份有限公司 + + 图 3-1 使用 set/get 系列(a)和 map 系列(b)接口流程差异 + 设置输入和获取输出接口没有绑定关系,因此可以混合使用 set/get 系列接口和 map 系列 +接口。如图 3-2(c),用户可以使用 rknn_inputs_map 系列接口设置输入,再通过 rknn_outputs_get +接 口 获 取 输 出 , 或 者 如 图 3-2 ( d ) 通 过 rknn_inputs_set 系 列 接 口 设 置 输 入 , 再 使 用 +rknn_outputs_map 接口获取输出。 + + 8 + 瑞芯微电子股份有限公司 + + 图 3-2 混合使用 set/get 系列和 map 系列接口的调用流程 +3.2.3.1 API 内部处理流程 + + 在推理 RKNN 模型时,原始数据要经过输入处理、NPU 运行模型、输出处理三大流程。 + 在典型的图片推理场景中,假设输入数据 data 是 3 通道的图片且为 NHWC 排布格式,运行时 + (Runtime)对数据处 理的流程如图 3-3 所示。在 API 层面上,rknn_inputs_set 接口(当 + pass_through=0 时,详见 rknn_input 结构体)包含了颜色通道交换、归一化、量化、NHWC 转 + 换成 NCHW 的过程,rknn_outputs_get 接口(当 want_float=1 时,详见 rknn_output 结构体) + + 9 + 瑞芯微电子股份有限公司 + +包含了反量化的过程。 + + 图 3-3 完整的图片数据处理流程 + 实际上,对于某些 RKNN 模型,输入处理的流程没有全部执行,例如,当输入数据不是 + 3 通道图像时或者用 rknn-toolkit 导出模型的 config 函数配置为 reorder=”0 1 2”时,没有颜色通 + 道转换流程。当 RKNN 模型输入 tensor 的属性是 NHWC 布局时,没有 NHWC 转换成 NCHW + 的流程。rknn_inputs_se(t 当 pass_through=1 时)和 rknn_inputs_map 不包含任何输入处理流程, + rknn_outputs_get(当 want_float=0 时)和 rknn_outputs_map 不包含任何输出处理流程。此时, + 虽然两组 API 都不包含对应的处理流程,不过,使用 set/get 系列接口会比 map 系列接口多出 + 数据拷贝过程。 + +3.2.3.2 量化和反量化 + + 当使用 rknn_inputs_set(pass_through=1)和 rknn_inputs_map 时,表明在 NPU 推理之前 + 的流程要用户处理。rknn_outputs_map 获取输出后,用户也要做反量化得到 32 位浮点结果。 + + 量化和反量化用到的量化方式、量化数据类型以及量化参数,可以通过 rknn_query 接口 + 查询。目前,RK1808/RK3399Pro/RV1109/RV1126 的 NPU 有非对称量化和动态定点量化两种 + 量化方式,每种量化方式指定相应的量化数据类型。总共有以下四种数据类型和量化方式组 + 合: + +  uint8(非对称量化) +  int8(动态定点) +  int16(动态定点) +  float16(无) + 通常,归一化后的数据用 32 位浮点数保存,32 位浮点转换成 16 位浮点数请参考 IEEE-754 + 标准。假设归一化后的 32 位浮点数据是 D ,下面介绍量化流程: + + 10 + 瑞芯微电子股份有限公司 + +1)float32 转 uint8 +假设输入 tensor 的非对称量化参数是 Sq , ZP ,数据 D 量化过程表示为下式: + + Dq  round (clamp(D / Sq  ZP,0,255)) (3-1) + +上式中,clamp 表示将数值限制在某个范围。round 表示做舍入处理。 +2)float32 转 int8 +假设输入 tensor 的动态定点量化参数是 fl,数据 D 量化过程表示为下式: + + Dq  round (clamp(D * 2 fl ,-128,127)) (3-2) + +3)float32 转 int16 +假设输入 tensor 的动态定点量化参数是 fl,数据 D 量化过程表示为下式: + + Dq  round (clamp(D * 2 fl ,-32768,32767)) (3-3) + +反量化流程是量化的逆过程,可以根据上述量化公式反推出反量化公式,这里不做赘述。 + +3.2.3.3 零拷贝 + + 在特定的条件下,可以把输入数据拷贝次数减少到零,即零拷贝。比如,当 RKNN 模型 +是非对称量化,量化数据类型是 uint8,3 通道的均值是相同的整数同时缩放因子相同的情况 +下,归一化和量化可以省略。证明如下: + +假设输入图像数据是 D f ,量化参数是 Sq , ZP 。 M i 表示第 i 通道的均值, Si 表示第 i + +通道的归一化因子。则第 i 通道归一化后的数据 Di 如下式子: + + Di  (D f  M i ) / Si (3-4) + +数据 Di 量化过程表示为下式: (3-5) + Dq  clamp(Di / Sq  ZP,0,255) + +上述两个式子合并后,可以得出 + + Dq  clamp((D f  Mi ) /(Si * Sq)  ZP) (3-6) +假设量化图片矫正集数据范围包含 0 到 255 的整数值,当 M1  M 2  M 3 ,S1  S2  S3 时, + + 11 + 瑞芯微电子股份有限公司 + +归一化数值范围表示如下: (3-7) + Dmin  (0 - M i ) / Si  M i / Si (3-8) + Dmax  (255 - M i ) / Si + + 因此,量化参数计算如下: + + Sq  (Dmax  Dmin ) / 255  1/Si (3-9) + + ZP  (0  Dmin ) / Sq  M i (3-10) + +把式(3-9)和式(3-10)代入式(3-6),可以得出 D f  Dq ,即符合零拷贝的条件下:3 + +通道的均值是相同的整数同时归一化的缩放因子相同,输入 uint8 数据等于量化后的 uint8 数 +据。 + + 输入零拷贝能降低 CPU 负载,提高整体的推理速度。针对 RGB 或 BGR 输入数据,实现 +输入零拷贝的步骤如下: + + 1)三个通道的均值是相同的整数同时归一化的缩放因子相同。 + 2)在 rknn-toolkit 的 config 函数中,设置 force_builtin_perm=True,导出 NHWC 输入的 +RKNN 模型。 + 3)使用 rknn_inputs_map 接口,获取输入 tensor 内存地址信息。 + 4)往内存地址填充输入数据,比如调用 RGA 缩放函数,目标地址使用 rknn_inputs_map +获取的物理地址。 + 5)调用 rknn_inputs_sync 接口。 + 6)调用 rknn_run 接口。 + 7)调用获取输出接口。 + +3.2.4 API 详细说明 + +3.2.4.1 rknn_init + + rknn_init 初始化函数将创建 rknn_context 对象、加载 RKNN 模型以及根据 flag 执行特定 +的初始化行为。 + + 12 + 瑞芯微电子股份有限公司 + +API rknn_init +功能 初始化 rknn +参数 rknn_context *context:rknn_context 指针。函数调用之后,context 将会被赋值。 + void *model:RKNN 模型的二进制数据。 + uint32_t size:模型大小。 + uint32_t flag:特定的初始化标志。目前 RK1808 平台仅支持以下标志: + + RKNN_FLAG_COLLECT_PERF_MASK:打开性能收集调试开关,打开之后能够通过 + rknn_query 接口查询网络每层运行时间。需要注意,该标志被设置后 rknn_run 的 + 运行时间将会变长。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + rknn_context ctx; + int ret = rknn_init(&ctx, model_data, model_data_size, 0); + +3.2.4.2 rknn_destroy + + rknn_destroy 函数将释放传入的 rknn_context 及其相关资源。 + +API rknn_destroy + +功能 销毁 rknn_context 对象及其相关资源。 + +参数 rknn_context context:要销毁的 rknn_context 对象。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + int ret = rknn_destroy (ctx); + +3.2.4.3 rknn_query + rknn_query 函数能够查询获取到模型输入输出、运行时间以及 SDK 版本等信息。 + + 13 + 瑞芯微电子股份有限公司 + +API rknn_query + +功能 查询模型与 SDK 的相关信息。 + +参数 rknn_context context:rknn_context 对象。 + + rknn_query_cmd cmd:查询命令。 + + void* info:存放返回结果的结构体变量。 + + uint32_t size:info 对应的结构体变量的大小。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 当前 SDK 支持的查询命令如下表所示: + + 查询命令 返回结果结构体 功能 + +RKNN_QUERY_IN_OUT_NUM rknn_input_output_num 查询输入输出 Tensor 个数 + +RKNN_QUERY_INPUT_ATTR rknn_tensor_attr 查询输入 Tensor 属性 + +RKNN_QUERY_OUTPUT_ATTR rknn_tensor_attr 查询输出 Tensor 属性 + +RKNN_QUERY_PERF_DETAIL rknn_perf_detail 查询网络各层运行时间 + +RKNN_QUERY_SDK_VERSION rknn_sdk_version 查询 SDK 版本 + + 接下来的将依次详解各个查询命令如何使用。 + +1) 查询输入输出 Tensor 个数 + 传入 RKNN_QUERY_IN_OUT_NUM 命令可以查询模型输入输出 Tensor 的个数。其中需 + +要先创建 rknn_input_output_num 结构体对象。 + 示例代码如下: + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, + + sizeof(io_num)); + printf("model input num: %d, output num: %d\n", io_num.n_input, + + io_num.n_output); + +2) 查询输入 Tensor 属性 + 传入 RKNN_QUERY_INPUT_ATTR 命令可以查询模型输入 Tensor 的属性。其中需要先创 + +建 rknn_tensor_attr 结构体对象。 + + 14 + 瑞芯微电子股份有限公司 + +示例代码如下: + +rknn_tensor_attr input_attrs[io_num.n_input]; +memset(input_attrs, 0, sizeof(input_attrs)); +for (int i = 0; i < io_num.n_input; i++) { + + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +3) 查询输出 Tensor 属性 + 传入 RKNN_QUERY_OUTPUT_ATTR 命令可以查询模型输出 Tensor 的属性。其中需要先 + +创建 rknn_tensor_attr 结构体对象。 + 示例代码如下: + +rknn_tensor_attr output_attrs[io_num.n_output]; +memset(output_attrs, 0, sizeof(output_attrs)); +for (int i = 0; i < io_num.n_output; i++) { + + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +4) 查询网络各层运行时间 + 如果在 rknn_init 函数调用时有设置 RKNN_FLAG_COLLECT_PERF_MASK 标志,那么 + +在执行 rknn_run 完成之后,可以传入 RKNN_QUERY_PERF_DETAIL 命令来查询网络每层运 +行时间。其中需要先创建 rknn_perf_detail 结构体对象。 + + 示例代码如下: + +rknn_perf_detail perf_detail; +ret = rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, &perf_detail, + + sizeof(rknn_perf_detail)); +printf("%s", perf_detail.perf_data); + + 注意,用户不需要释放 rknn_perf_detail 中的 perf_data,SDK 会自动管理该 Buffer 内存。 +5) 查询 SDK 版本 + + 传入 RKNN_QUERY_SDK_VERSION 命令可以查询 RKNN SDK 的版本信息。其中需要 +先创建 rknn_sdk_version 结构体对象。 + + 15 + 瑞芯微电子股份有限公司 + + 示例代码如下: + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + + sizeof(rknn_sdk_version)); + printf("sdk api version: %s\n", version.api_version); + printf("driver version: %s\n", version.drv_version); + +3.2.4.4 rknn_inputs_set + + 通过 rknn_inputs_set 函数可以设置模型的输入数据。该函数能够支持多个输入,其中每 + + 个输入是 rknn_input 结构体对象,在传入之前用户需要设置该对象。 + +API rknn_inputs_set + +功能 设置模型输入数据。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + rknn_input inputs[]:输入数据数组,数组每个元素是 rknn_input 结构体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = img_width*img_height*img_channels; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].buf = in_data; + + ret = rknn_inputs_set(ctx, 1, inputs); + +3.2.4.5 rknn_inputs_map + rknn_inputs_map 函数用于获取模型输入 tensor 初始化后的存储状态,存储状态包括虚拟 + + 16 + 瑞芯微电子股份有限公司 + + 地址,物理地址,fd,存储空间大小。它需要和 rknn_inputs_sync 接口(见 rknn_inputs_sync + 函数)配合使用,在模型初始化后,用户通过返回的的内存位置设置输入数据,并且在推理 + + 前调用 rknn_inputs_sync 函数。存储状态使用 rknn_tensor_mem 结构体表示。输入参数 mem 是 + + rknn_tensor_mem 结构体数组。 + 目前 ,在 RK1808/RV1109/RV1126 芯片 上,返回 的 fd 是 -1。当 返回的物 理地址 值是 + + 0xffffffffffffffff(2 的 64 次幂-1),表示无法获取正确的物理地址,而虚拟地址仍然有效。如 + 果有多个模型输入 tensor 的存储空间较大,用户可以在挂载驱动时,适当增加模型输入和输 + 出存储空间或者扩增固件中的 CMA 内存空间。以 RV1109_RV1126 为例,配置驱动存储空间, + 可以参考如下修改: + + 把/etc/init.d/S60NPU_init 文件这一行: + insmod /lib/modules/galcore.ko contiguousSize=0x400000 gpuProfiler=1 + 改成 + insmod /lib/modules/galcore.ko contiguousSize=0x600000 gpuProfiler=1 + 然后重启生效。此配置应该大于用户模型输入和输出总大小,但不超过固件中可用的 + + CMA 空间大小。 + +API rknn_inputs_map + +功能 读取输入存储状态信息。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结 + 构体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + 17 + 瑞芯微电子股份有限公司 + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + +3.2.4.6 rknn_inputs_sync + + rknn_inputs_sync 函数将 CPU 缓存写回内存,让设备能获取正确的数据。 + +API rknn_inputs_sync + +功能 同步输入数据。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + + ret = rknn_inputs_sync(ctx, 1, mem); + +3.2.4.7 rknn_inputs_unmap + + rknn_inputs_unmap 函数将清除 rknn_inputs_map 函数获取的输入 tensor 的存储位置信息和 + + 标志。 + +API rknn_inputs_unmap + +功能 清除 rknn_inputs_map 函数获取的输入 tensor 的存储位置信息和标志。 + +参数 rknn_context context:rknn_contex 对象。 + + uint32_t n_inputs:输入数据个数。 + + 18 + 瑞芯微电子股份有限公司 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + ret = rknn_inputs_sync(ctx, 1, mem); + ret = rknn_run(ctx, NULL); + + ret = rknn_inputs_unmap(ctx, 1, mem); + +3.2.4.8 rknn_run + + rknn_run 函数将执行一次模型推理,调用之前需要先通过 rknn_inputs_set 函数设置输入数 + + 据。 + +API rknn_run + +功能 执行一次模型推理。 + +参数 rknn_context context:rknn_context 对象。 + + rknn_run_extend* extend:保留扩展,当前没有使用,传入 NULL 即可。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + ret = rknn_run(ctx, NULL); + +3.2.4.9 rknn_outputs_get + + rknn_outputs_get 函数可以获取模型推理的输出数据。该函数能够一次获取多个输出数据。 + 其 中 每 个 输 出 是 rknn_output 结 构 体 对 象 , 在 函 数 调 用 之 前 需 要 依 次 创 建 并 设 置 每 个 + rknn_output 对象。 + + 对于输出数据的 buffer 存放可以采用两种方式:一种是用户自行申请和释放,此时 + + 19 + 瑞芯微电子股份有限公司 + + rknn_output 对象的 is_prealloc 需要设置为 1,并且将 buf 指针指向用户申请的 buffer;另一种 + 是由 rknn 来进行分配,此时 rknn_output 对象的 is_prealloc 设置为 0 即可,函数执行之后 buf + 将指向输出数据。 + +API rknn_outputs_get + +功能 获取模型推理输出。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + rknn_output outputs[]:输出数据的数组,其中数组每个元素为 rknn_output 结构体对 + 象,代表模型的一个输出。 + + rknn_output_extend* extend:保留扩展,当前没有使用,传入 NULL 即可 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下: + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) { + + outputs[i].want_float = 1; + } + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + +3.2.4.10 rknn_outputs_release + + rknn_outputs_release 函数将释放 rknn_outputs_get 函数得到的输出的相关资源。 + +API rknn_outputs_release + +功能 释放 rknn_output 对象。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + rknn_output outputs[]:要销毁的 rknn_output 数组。 + +返回值 int 错误码(见 rknn 返回值错误码) + + 示例代码如下 + + 20 + 瑞芯微电子股份有限公司 + + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + +3.2.4.11 rknn_outputs_map + + rknn_outputs_map 函 数 获 取 模 型 初 始 化 后 输 出 tensor 的 存 储 状 态 。 需 要 和 + + rknn_outputs_sync 函 数 ( 见 rknn_outputs_sync 函 数 ) 配 合 使 用 , 在 模 型 初 始 化 后 调 用 + rknn_outputs_map 接口,接着每次推理完调用 rknn_outputs_sync 接口。如果用户需要 32 位浮 + 点类型的数据,需要根据量化方式和量化的数据类型做反量化。 + +API rknn_outputs_map + +功能 读取输出存储状态信息。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_map(ctx, 1, mem); + +3.2.4.12 rknn_outputs_sync + + 当使用 rknn_outputs_map 接口映射完模型运行时模型输出 tensor 存储状态信息后,为确 + + 保缓存一致性,使用 rknn_outputs_sync 函数让 CPU 获取推理完最新的数据。 + +API rknn_outputs_sync + +功能 推理完,同步最新的输出数据。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + + 21 + 瑞芯微电子股份有限公司 + + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_sync(ctx, io_num.n_output, mem); + +3.2.4.13 rknn_outputs_unmap + + rknn_outputs_unmap 函数将清除 rknn_outputs_map 函数获取的输出 tensor 的存储状态。 + +API rknn_outputs_unmap + +功能 清除 rknn_outputs_map 函数获取的输出 tensor 的存储状态。 + +参数 rknn_context context:rknn_context 对象。 + + uint32_t n_outputs:输出数据个数。 + rknn_tensor_mem mem[]:存储状态信息数组,数组每个元素是 rknn_tensor_mem 结构 + + 体对象。 + +返回值 int 错误码(见 rknn 返回值错误码) + +示例代码如下: + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_unmap(ctx, io_num.n_output,mem); + +3.2.5 RKNN 数据结构定义 + +3.2.5.1 rknn_input_output_num + 结构体 rknn_input_output_num 表示输入输出 Tensor 个数,其结构体成员变量如下表所示: + + 22 + 成员变量 数据类型 瑞芯微电子股份有限公司 + n_input uint32_t +n_output uint32_t 含义 + + 输入 Tensor 个数 + 输出 Tensor 个数 + +3.2.5.2 rknn_tensor_attr + +结构体 rknn_tensor_attr 表示模型的 Tensor 的属性,结构体的定义如下表所示: + +成员变量 数据类型 含义 + +index uint32_t 表示输入输出 Tensor 的索引位置。 + +n_dims uint32_t Tensor 维度个数。 + + dims uint32_t[] Tensor 各维度值。 + name char[] Tensor 名称。 +n_elems Tensor 数据元素个数。 + uint32_t + +size uint32_t Tensor 数据所占内存大小。 + +fmt rknn_tensor_format Tensor 维度的格式,有以下格式: + + RKNN_TENSOR_NCHW + RKNN_TENSOR_NHWC + +type rknn_tensor_type Tensor 数据类型,有以下数据类型: + + RKNN_TENSOR_FLOAT32 + RKNN_TENSOR_FLOAT16 + RKNN_TENSOR_INT8 + RKNN_TENSOR_UINT8 + +qnt_type RKNN_TENSOR_INT16 + rknn_tensor_qnt_type Tensor 量化类型,有以下的量化类型: + + RKNN_TENSOR_QNT_NONE:未量化; + RKNN_TENSOR_QNT_DFP:动态定点量化; + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC : 非 + + 23 + fl int8_t 瑞芯微电子股份有限公司 + zp uint32_t + 对称量化。 +scale float RKNN_TENSOR_QNT_DFP 量化类型的参数。 + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC 量 化 + 类型的参数。 + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC 量 化 + 类型的参数。 + +3.2.5.3 rknn_input + + 结构体 rknn_input 表示模型的一个数据输入,用来作为参数传入给 rknn_inputs_set 函数。 +结构体的定义如下表所示: + +成员变量 数据类型 含义 + +index uint32_t 该输入的索引位置。 + +buf void* 输入数据 Buffer 的指针。 + +size uint32_t 输入数据 Buffer 所占内存大小。 + +pass_through uint8_t 设置为 1 时会将 buf 存放的输入数据直接设置给 + 模型的输入节点,不做任何预处理。 + +type rknn_tensor_type 输入数据的类型。 + +fmt rknn_tensor_format 输入数据的格式。 + +3.2.5.4 rknn_tensor_mem + +结构体 rknn_tensor_mem 表示 tensor 初始化后的存储状态信息,用来作为参数传入给 + +rknn_inputs_map 系列和 rknn_outputs_map 系列函数。结构体的定义如下表所示: + +成员变量 数据类型 含义 + +logical_addr void* 该输入的虚拟地址。 + +physical_addr uint64_t 该输入的物理地址。 + +fd int32_t 该输入的 fd。 + + 24 + size uint32_t 瑞芯微电子股份有限公司 + handle uint32_t + priv_data 该输入 tensor 占用的内存大小。 +reserved_flag void* 该输入的 handle。 + uint64_t 保留的数据。 + 保留的标志位。 + +3.2.5.5 rknn_output + +结构体 rknn_output 表示模型的一个数据输出,用来作为参数传入给 rknn_outputs_get 函 + +数,在函数执行后,结构体对象将会被赋值。结构体的定义如下表所示: + +成员变量 数据类型 含义 + +want_float uint8_t 标识是否需要将输出数据转为 float 类型输出。 + +is_prealloc uint8_t 标识存放输出数据的 Buffer 是否是预分配。 + +index uint32_t 该输出的索引位置。 + +buf void* 输出数据 Buffer 的指针。 + +size uint32_t 输出数据 Buffer 所占内存大小。 + +3.2.5.6 rknn_perf_detail + +结构体 rknn_perf_detail 表示模型的性能详情,结构体的定义如下表所示: + +成员变量 数据类型 含义 + +perf_data char* 性能详情包含网络每层运行时间,能够直接打印 + + 出来查看。 + +data_len uint64_t 存放性能详情的字符串数组的长度。 + +3.2.5.7 rknn_sdk_version + +结构体 rknn_sdk_version 用来表示 RKNN SDK 的版本信息,结构体的定义如下: + + 25 + 成员变量 数据类型 瑞芯微电子股份有限公司 + api_version char[] + drv_version char[] 含义 + SDK 的版本信息。 + SDK 所基于的驱动版本信息。 + +3.2.6 RKNN 返回值错误码 + +RKNN API 函数的返回值错误码定义如下表所示 + + 错误码 错误详情 + +RKNN_SUCC(0) 执行成功 + +RKNN_ERR_FAIL(-1) 执行出错 + +RKNN_ERR_TIMEOUT(-2) 执行超时 + +RKNN_ERR_DEVICE_UNAVAILABLE(-3) NPU 设备不可用 + +RKNN_ERR_MALLOC_FAIL(-4) 内存分配失败 + +RKNN_ERR_PARAM_INVALID(-5) 传入参数错误 + +RKNN_ERR_MODEL_INVALID(-6) 传入的 RKNN 模型无效 + +RKNN_ERR_CTX_INVALID(-7) 传入的 rknn_context 无效 +RKNN_ERR_INPUT_INVALID(-8) 传入的 rknn_input 对象无效 +RKNN_ERR_OUTPUT_INVALID(-9) 传入的 rknn_output 对象无效 + +RKNN_ERR_DEVICE_UNMATCH(-10) 版本不匹配 + +RKNN_ERR_INCOMPATILE_PRE_COMPILE_M RKNN 模型使用 pre_compile 模式,但是和当前驱动不 + +ODEL(-11) 兼容 + +RKNN_ERR_INCOMPATILE_OPTIMIZATION_L RKNN 模型设置了优化等级的选项,但是和当前驱动 + +EVEL_VERSION(-12) 不兼容 + +RKNN_ERR_TARGET_PLATFORM_UNMATCH RKNN 模型和当前平台不兼容,一般是将 RK1808 的平 + +(-13) 台的 RKNN 模型放到了 RV1109/RV1126 上。 + +RKNN_ERR_NON_PRE_COMPILED_MODEL_ RKNN 模型不是 pre_compile 模式,在 mini-driver 上无 + +ON_MINI_DRIVER(-14) 法执行 + + 26 + 瑞芯微电子股份有限公司 + +4 高级 API 使用说明 + +4.1 Matmul 算子库 + +4.1.1 简介 + + 高级 API 旨在利用 NPU 高算力特性,执行特定的数学运算,提供简洁的接口调用,达到 +计算加速的效果。其中,Matmul 算子库是一个定点数矩阵乘法的加速库。该操作定义如下: + + C  AT * B + 这里: + A,B 和 C 是 2 维矩阵 + A 是一个 K*M 的矩阵, + B 是一个 K*N 的矩阵 + C 是一个 M*N 的矩阵 + +4.1.2 数据结构定义 + + rknn_matmul_handle_t 表示用于执行 Matmul 算子操作的句柄,它包含了运行时环境的上 +下文和输入 buffer 的信息。结构体的定义如下表所示: + +成员变量 数据类型 含义 + + A void* 运算时第一个矩阵 buffer 的指针。 + + B void* 运算时第二个矩阵 buffer 的指针。 + +M int32_t A 矩阵的低维度元素个数。 + + K int32_t A 和 B 矩阵的高维度元素个数。 + + N int32_t B 矩阵的低维度元素个数。 + +in_dtype rknn_tensor_type 输入数据的类型。 + +rknn_ctx rknn_context 运行时的上下文对象。 + + 27 + 瑞芯微电子股份有限公司 + +4.1.3 详细 API 说明 + +4.1.3.1 rknn_matmul_load + + rknn_matmul_load 加载函数将加载用户创建的输入 buffer,返回 rknn_matmul_handle_t 类 + 型对象。Matmul 算子 API 不负责管理输入 buffer 的生命周期,用户要确保输入 buffer 在 Matmul + + 算子 API 调用内有效。 + +API rknn_matmul_load + +功能 初始化和设置输入 buffer 指针。 + +参数 void *a:用户创建的第一个矩阵 buffer 的指针,只支持输入是 8-bit 无符号整型或 8-bit + 有符号整型的一维数组指针。 + + void *b:用户创建的第二个矩阵 buffer 的指针,只支持输入是 8-bit 无符号整型或 8-bit + 有符号整型的一维数组指针。 + + int32_t M:A 矩阵的低维度元素个数。 + + int32_t K:A 和 B 矩阵的高维度元素个数。 + int32_t N:B 矩阵的低维度元素个数。 + + rknn_tensor_type dtype:用户指定的输入数据类型,只支持 RKNN_TENSOR_INT8 或 + RKNN_TENSOR_UINT8 类型 + +返回值 rknn_matmul_handle_t 对象。 + + 示例代码如下: + + rknn_tensor_type dtype = RKNN_TENSOR_INT8; + int8_t x[256*1] = {0}; + int8_t y[256*4096] = {0}; + rknn_matmul_handle_t handle= rknn_matmul_load(x,y,1,256,4096,dtype); + +4.1.3.2 rknn_matmul_run + + 在 rknn_matmul_load 被调用后和执行 rknn_matmul_run 前,输入 buffer 的数据由外部更新, + 不用重新调用 rknn_matmul_load。 + + 28 + 瑞芯微电子股份有限公司 + +API rknn_matmul_run + +功能 执行 Matmul 操作。 + +参数 rknn_matmul_handle_t matmul_handle:由 rknn_matmul_load 接口返回的句柄。 + + float *c:用户创建的矩阵浮点 buffer 的指针,用于获取输出。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + ... + float out_fp32_buf[4096] = {0}; + rknn_matmul_run(handle,out_fp32_buf); + +4.1.3.3 rknn_matmul_unload + +API rknn_matmul_unload + +功能 销毁 Matmul 算子运行时上下文。 + +参数 rknn_matmul_handle_t matmul_handle:由 rknn_matmul_load 接口返回的句柄。 + +返回值 int 错误码(见 rknn 返回值错误码)。 + + 示例代码如下: + + rknn_matmul_unload(handle); + +4.1.4 实现限制 + + Matmul 算子库是基于 NPU 的硬件架构实现,为了达到精度和速度的平衡,有一些限制如下。 + +4.1.4.1 维度限制 + + 按照上述操作描述,该库实现了 M=1 的矩阵乘法。具体而言,Matmul 算子输入 A 必须是 Kx1 +形状的 buffer,即用户必须创建一块包含 K 个 8-bit 无符号整型或者 8-bit 有符号整型元素的数据。 +当算子库运行在 Mini driver 上,K 的值只能设置为 128 或 256 或 512,N 固定为 4096,而在 Full driver +上运行,没有此限制,但建议 K 的值为 128,256,512,1024,2048,N 取 2 的偶数次幂,建议 N + + 29 + 瑞芯微电子股份有限公司 + +不大于 4096。 + +4.1.4.2 输入数据类型限制 + + 只支持 8-bit 无符号整型和 8-bit 有符号整型两种输入。 + +4.1.5 基准测试 + + 输入的两个矩阵使用随机数情况下,在 RV1109-EVB 板子上实测结果如表-1 所示。速度是 + +rknn_matmul_run 接口调用循环 100 次后的平均时间,平均相对误差是 NPU 和 CPU 上执行相同算 + +法的结果的误差值,具体公式是: + + (abs(R1  R2 ) / R2 ) / N + + k + + 其中, + + R1 是 Matmul 算子库输出向量,包含 N 个元素。 + R2 是 CPU 输出向量,包含 N 个元素。 + + 表-1 Matmul 算子库速度/精度结果(RV1109,int8) + +K N 速度(ms) 平均相对误差 + +128 1024 1.0 0.00034 + +256 1024 1.6 0.00032 + +512 2024 3.0 -0.00015 + +1024 1024 5.4 0.00047 + +128 4096 3.0 0.00051 + +256 4096 5.6 0.00024 + +512 4096 10.7 0.00024 + +1024 4096 20.9 0.00051 + +注意,速度可能因为 NPU 驱动版本不同而有些许差异。误差值则根据每次测试的随机数不同也可 +能有些许差异。 + + 30 + 瑞芯微电子股份有限公司 + +5 NPU 驱动说明 + +5.1.1 NPU 驱动目录说明 + + NPU 的驱动在$SDK/external/rknpu/drivers/目录下或者 + +https://github.com/rockchip-linux/rknpu/tree/master/drivers + +其中的编译、安装规则参考$SDK/buildroot/package/rockchip/rknpu/rknpu.mk +drivers/ +├── common +├── linux-aarch64 (for RK1808 npu full driver) +├── linux-aarch64-mini (for RK1808 npu mini driver) +├── linux-armhf (for RK1806 npu full driver) +├── linux-armhf-mini(for RK1806 npu mini driver) +├── linux-armhf-puma (for RV1126/RV1109 npu full driver) +├── linux-armhf-puma-mini(for RV1126/RV1109 npu mini driver) +├── npu_ko (NPU kernel driver) + +5.1.2 NPU full driver 与 mini driver 的区别 + + 主要包含以下几点: + 1)Mini driver 只支持预编译的 rknn 模型,如果跑非预编译模型,会出现 +RKNN_ERR_MODEL_INVALID 的错误,从 1.6.0 开始,会返回 +RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER 的错误; + 2)Full driver 支持 RKNN Toolkit 的联机调试功能,mini driver 不支持; + 3)Mini driver 库大小比 full driver 小很多,以 RV1109/RV1126 1.6.0 驱动为例,full driver 大 +小为 87MB,mini driver 大小为 7.1MB,可以有效的节省 flash 大小。 + 4)Mini driver 库运行时占用的内存比 full driver 小。 + + 31 + 瑞芯微电子股份有限公司 + +6 FAQ + +6.1.1 输入输出数据格式问题 + +6.1.1.1 三通道图片数据输入,采用 RGB 还是 BGR 排布? + + 建议用户输入数据统一使用 RGB 排布。在导出 RKNN 模型时,config 函数的 reorder_channel +参数,有以下两种可能: + + 1)如果原始模型使用 BGR 图片训练,reorder_channel='2 1 0'。 + 2)如果原始模型使用 RGB 图片训练,reorder_channel='0 1 2'。 + +6.1.1.2 rknn_input 结构体该设置的 RKNN_TENSOR_NHWC 还是 RKNN_TENSOR_NCHW? + 两种设置耗时为何不同? + + rknn_input 结构体根据用户自己的数据格式而定,C API 内部会自动转换成 NPU 需要的格式。 +耗时不同的原因是不同输入格式计算量不同,优化方式也不同。 + +6.1.1.3 没量化的 RKNN 模型,输出 rknn_tensor_attr 里面 size 和 rknn_outputs_get 接口返 + 回的 rknn_output 的 size 为何不同? + + 没量化 RKNN 模型,NPU 内部输出数据类型是 float16,大小是元素数量*2 字节。当用户设 +置 want_float=1 时,想要的是 float32 数据,float16 会转换成 float32,大小是元素数量*4 字节。 + +6.1.1.4 rknn_output.index 是用户输入还是驱动返回? + +驱动返回。 + +6.1.1.5 rknn_tensor_attr 中的 dims 数组为何会出现 0? + +0 表示该维度无效。rknn_tensor_attr 中的 n_dims 表示 dims 数组的有效维度数量。 + + 32 + 瑞芯微电子股份有限公司 + +6.1.1.6 rknn_tensor_attr 中的 dims 数组顺序与 rknn_toolkit 的获取的 numpy 的顺序相反? + 是。C API 中的数组排布跟 python 相反,比如 rknn-toolkit 的 run()接口获得 numpy 输出形 + +状是[1,255,20,20],C API 中 dims 数组是{20,20,255,1}。 + +6.1.2 输入输出接口使用问题 + +6.1.2.1 pass_through 用法以及使用 rknn_inputs_map 接口时,如何预处理数据? + 请参考 https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples 下的 + +rknn_pass_through_demo 示例。 +6.1.2.2 使用 rknn_inputs_map 或者 rknn_outputs_map 获取的物理地址为什么会无效?如何 + + 获取有效的物理地址? + + 输入/输出无法分配到物理连续的内存,可能的原因有: + 1)输入/输出的占用空间过大,超过了总的物理连续的内存大小(默认是 4MB)。 + 2)系统中没有足够的物理连续的内存可用。 + 3)导出 RKNN 模型时,config 函数添加如下参数:output_optimize=1。 + 用户可以尝试重启系统,或者让 NPU 驱动挂载时配置更大的连续地址空间,配置方法参考 +rknn_inputs_map 接口说明。 + +6.1.3 API 调用流程问题 + +6.1.3.1 rknn_init 成功后,模型文件占用内存是否可以释放? + 可以。 + +6.1.3.2 rknn_output.is_prealloc=1 时,rknn_outputs_release 是否需要调用? + 需要。 + + 33 + 瑞芯微电子股份有限公司 + +6.1.4 性能问题 + +6.1.4.1 rknn_init 耗时过长? + + 使用预编译模型。使用方法参考 https://github.com/rockchip-linux/rknn-toolkit/tree/master/doc 下 +User Guide 文档的相关章节。 + +6.1.4.2 rknn_inputs_set 接口耗时过长? + + 可能原因是数据量大或格式转换耗时长。如果是格式转换耗时长,用户可以尝试 pass_through +用法自己做转换。转换方式请参考 +https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples 下的 +rknn_pass_through_demo 示例。或者尝试在导出 RKNN 模型时,config 函数添加如下参数: +output_optimize=1。 + +6.1.4.3 rknn_outputs_get 接口耗时过长? + + 可能原因是数据量大或转换格式耗时长。如果是转换格式耗时长,用户可以尝试设置 +want_float=0, 再 自 己 做 转 换 。 或 者 尝 试 在 导 出 RKNN 模 型 时 , config 函 数 添 加 如 下 参 数 : +output_optimize=1。 + + 34 + diff --git a/libs/rklibs/rknpu/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_EN.pdf b/libs/rklibs/rknpu/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_EN.pdf new file mode 100644 index 0000000..1fe93f2 --- /dev/null +++ b/libs/rklibs/rknpu/rknn/doc/Rockchip_User_Guide_RKNN_API_V1.7.3_EN.pdf @@ -0,0 +1,1473 @@ + www.rock-chips.com + +Classification Level: Top Secret( ) Secret( ) Internal( ) Public(√) + +Rockchip User Guide RKNN API + +(Technology Department, Graphic Compute Platform Center) + +Mark: Version: 1.7.3 + +[ ] Changing Author: HPC + +[√] Released Completed Date: 13/Aug/2022 + + Reviewer: Vincent + + Reviewed Date: 213/Aug/2022 + + 瑞芯微电子股份有限公司 + Rockchip Electronics Co., Ltd. + + (Copyright Reserved) + www.rock-chips.com + + Revision History + +Version Modifier Date Modify Description Reviewer +v0.9.7 Yang Huacong +V0.9.8 Yang Huacong 25/Jan/2019 Initial version Zhuo + 5/Jun/2019 Hongtian +V1.3.3 Randall 2/June/2020 1. Add RKNN API Library description +V1.4.0 Randall 2. Update rknn example and rknn-toolkit Zhuo + Hongtian +v1.6.0 Chifred Hong path + 3. Fix some mistake Randall +v1.6.1 Chifred Hong 1. Support RV1109/RV1126 +v1.7.0 Chifred Hong 2. Remove the python support +v1.7.1 Chifred Hong instructions, see the RKNN Toolkit Lite +v1.7.3 Chifred Hong related documentation for python support. + + 10/Sep/2020 1. Add some error code Randall + + 15/Jan/2021 1. Add Matmul advanced API Randall + 2. Add a set of API for setting inputs and + a set of API for obtaining outputs + 3. Add quantization and dequantization + algorithm description + 4. Add NPU driver description + 5. Add FAQ + + 26/Apr/2021 1. Update Matmul advanced API Randall + + 5/Aug/2021 1. Update Version number Randall + Randall + 2/Dec/2021 1. Single-channel input performance Randall + optimization + 2. Fix bug + + 13/Aug/2022 1. Update Version number + + 1 + www.rock-chips.com + +1 OVERVIEW ..................................................................................................................................................... 5 +2 SUPPORTED HARDWARE PLATFORMS ................................................................................................. 5 +3 INSTRUCTIONS ..............................................................................................................................................5 + + 3.1 RKNN SDK DEVELOPMENT PROCESS ........................................................................................................ 5 + 3.2 RKNN C API ...............................................................................................................................................6 + + 3.2.1 RKNN API Library .............................................................................................................................. 6 + 3.2.2 EXAMPLES .........................................................................................................................................6 + 3.2.3 API process description ........................................................................................................................7 + + 3.2.3.1 API internal processing flow ........................................................................................................................... 9 + 3.2.3.2 Quantification and dequantization ................................................................................................................10 + 3.2.3.3 Zero copy .......................................................................................................................................................12 + 3.2.4 API Reference .................................................................................................................................... 13 + 3.2.4.1 rknn_init ........................................................................................................................................................ 13 + 3.2.4.2 rknn_destroy ..................................................................................................................................................14 + 3.2.4.3 rknn_query .................................................................................................................................................... 15 + 3.2.4.4 rknn_inputs_set ............................................................................................................................................. 17 + 3.2.4.5 rknn_inputs_map ...........................................................................................................................................18 + 3.2.4.6 rknn_inputs_sync ...........................................................................................................................................19 + 3.2.4.7 rknn_inputs_unmap .......................................................................................................................................20 + 3.2.4.8 rknn_run ........................................................................................................................................................ 21 + 3.2.4.9 rknn_outputs_get ...........................................................................................................................................21 + 3.2.4.10 rknn_outputs_release .................................................................................................................................. 22 + 3.2.4.11 rknn_outputs_map ....................................................................................................................................... 23 + 3.2.4.12 rknn_outputs_sync .......................................................................................................................................23 + + 2 + www.rock-chips.com + 3.2.4.13 rknn_outputs_unmap ...................................................................................................................................24 + 3.2.5 RKNN DataStruct Define .................................................................................................................. 25 + 3.2.5.1 rknn_input_output_num ................................................................................................................................ 25 + 3.2.5.2 rknn_tensor_attr ............................................................................................................................................25 + 3.2.5.3 rknn_input ..................................................................................................................................................... 26 + 3.2.5.4 rknn_tensor_mem ..........................................................................................................................................27 + 3.2.5.5 rknn_output ................................................................................................................................................... 27 + 3.2.5.6 rknn_perf_detail ............................................................................................................................................ 28 + 3.2.5.7 rknn_sdk_version .......................................................................................................................................... 28 + 3.2.6 RKNN Error Code ............................................................................................................................. 28 +4 ADVANCED API INSTRUCTIONS .............................................................................................................29 + 4.1 MATMUL OPERATOR LIBRARY ....................................................................................................................29 + 4.1.1 Introduction ........................................................................................................................................29 + 4.1.2 Data structure definition .....................................................................................................................30 + 4.1.3 Detailed API description .................................................................................................................... 31 + 4.1.3.1 rknn_matmul_load ........................................................................................................................................ 31 + 4.1.3.2 rknn_matmul_run ..........................................................................................................................................31 + 4.1.3.3 rknn_matmul_unload .................................................................................................................................... 32 + 4.1.4 Implementation restrictions ................................................................................................................32 + 4.1.4.1 Dimensional restrictions ............................................................................................................................... 33 + 4.1.4.2 Input data type restriction ............................................................................................................................. 33 + 4.1.5 Benchmark ......................................................................................................................................... 33 +5 NPU DRIVER DESCRIPTION .................................................................................................................... 35 + 5.1.1 Directory structure description ...........................................................................................................35 + 5.1.2 The difference between NPU full driver and mini driver .................................................................. 35 + + 3 + www.rock-chips.com +6 FAQ ..................................................................................................................................................................36 + + 6.1.1 Input and output data format issues ................................................................................................... 36 + 6.1.2 Input and output interface usage problems ........................................................................................ 37 + 6.1.3 API call process issues ....................................................................................................................... 38 + 6.1.4 Performance issues .............................................................................................................................38 + + 4 + www.rock-chips.com + +1 Overview + + RKNN SDK provides a programming interface for platforms with NPU such as RK1808, which can +help users deploy RKNN models exported using RKNN-Toolkit. + +2 Supported hardware platforms + +This document applies to the following hardware platforms: + 1) RK1808, RK1806 + 2) RV1126, RV1109 + +The following description takes RK1808 as an example and also applies to other platforms mentioned +above. + +3 Instructions +3.1 RKNN SDK Development Process + + Before using the RKNN SDK, users first need to use the RKNN-Toolkit tool to convert the + user's model to the RKNN model. The user can obtain the tool's complete installation package and + documentation at https://github.com/rockchip-linux/rknn-toolkit . + + After successful conversion to the RKNN model, users can first connect to the RK1808 device + via RKNN-Toolkit for online debugging to ensure that the accuracy and performance of the model + meet the requirements. + + After getting the RKNN model file, users can choose using C or Python interface to develop + the application. The following chapters will explain how to develop application based on the RKNN + SDK on RK1808 platform. + + 5 + www.rock-chips.com + +3.2 RKNN C API + +3.2.1 RKNN API Library + + The libraries and header files provided by the RKNN SDK are located at +/external/rknpu/rknn/rknn_api/librknn_api directory, developers can use to develop +applications. + + It should be noted that the RKNN API of the RK1808 and RK3399Pro platforms is compatible, +and applications developed by both can be easily ported. However, you need to pay attention to +distinguish between the two platforms librknn_api.so, if the developer uses RK3399Pro's +librknn_api.so will not be able to run on the RK1808 platform. Developers can use the following +methods to distinguish the platform of librknn_api.so. + + $ strings librknn_api.so |grep version + librknn_api version 1.7.3 (cf7f05f build: 2022-08-13 10:59:33) + +3.2.2 EXAMPLES + + The SDK provides MobileNet image classification, MobileNet SSD object detection, and Yolo + v3 object detection demos. These demos provide reference for developer to develop applications + based on the RKNN SDK. The emo code is located in the + /external/rknpu/rknn/rknn_api/examples directory. Let's take rknn_mobilenet_demo as an + example to explain how to get started quickly. + + 1) Compile Demo Source Code + + cd examples/rknn_mobilenet_demo + # modify `GCC_COMPILER` on `build.sh` for target platform, then execute + ./build.sh + 2) Deploy to the RK1808 device + + adb push install/rknn_mobilenet_demo /userdata/ + + 3) Run Demo + + 6 + www.rock-chips.com + + adb shell + cd /userdata/rknn_mobilenet_demo/ + ./rknn_mobilenet_demo model/mobilenet_v1_rk180x.rknn + model/dog_224x224.jpg # RK180x + ./rknn_mobilenet_demo model/mobilenet_v1_rv1109_rv1126.rknn + model/dog_224x224.jpg # RV1109/RV1126 + +3.2.3 API process description + + From RKNN API V1.6.0, a new set of functions for setting input have been added: +  rknn_inputs_map +  rknn_inputs_sync +  rknn_inputs_unmap + And a set of functions to get the output: +  rknn_outputs_map +  rknn_outputs_sync +  rknn_outputs_unmap + When setting input, users can use rknn_inputs_set or rknn_inputs_map series of functions. + When obtaining the output of inference, use the rknn_outputs_get or rknn_outputs_map series of + functions. In certain scenarios, using the map series of API can reduce the number of memory copies + and improve the speed of complete inference. + The calling process of the rknn_inputs_map series interface and the rknn_inputs_set interface is + different, and the calling process of the rknn_outputs_map series interface and the rknn_outputs_get + interface are also different. The difference between the two series of API call flow is shown in Figure + 3-1, where (a) is the set/get series interface call flow, (b) is the map series interface call flow. + + 7 + www.rock-chips.com + + Figure 3-1 callling process difference between set/get series (a) and map series (b) + There is no binding relationship between the set input and get output API, so set/get series +interfaces and map series interfaces can be mixed. As shown in Figure 3-2(c), the user can use the +rknn_inputs_map series interface to set the input, and then get the output through the +rknn_outputs_get interface, or as Figure 3-2(d) set the input through the rknn_inputs_set series +interface, and then use the rknn_outputs_map interface to get the output. + + 8 + www.rock-chips.com + + Figure 3-2 The calling process of mixed use of set/get series and map series interfaces +3.2.3.1 API internal processing flow + + During inference of RKNN model, the original data has to go through three processes: input + processing, NPU running model, and output processing. In a typical picture inference scenario, + assuming that the input data is a 3-channel picture and is in the NHWC layout format, the data + processing flow at runtime is shown in Figure 3-3. At the API level, the rknn_inputs_set interface + (when pass_through=0, see the rknn_input structure for details) includes the process of swapping + + 9 + www.rock-chips.com + + color channel, normalization, quantization, and conversion of NHWC to NCHW. The + rknn_outputs_get interface (when want_float=1, see rknn_output structure) contains the process of + dequantization. + + Figure 3-3 Normal mode image data processing flow + In fact, for some RKNN models, the Runtime input processing flow is not all executed. For + example, when the input data is not a 3-channel image or the config function of the rknn-toolkit is + configured as reorder=”0 1 2” when exporting model, there is no color channel swapping process. + When the attribute of RKNN model input tensor is NHWC layout, there is no process for converting + NHWC to NCHW. rknn_inputs_set (when pass_through=1) and rknn_inputs_map do not contain any + input processing flow, rknn_outputs_get (when want_float=0) and rknn_outputs_map do not contain + any output processing flow. At this time, although the two sets of APIs do not include the + corresponding processing flow, using the set/get series interface will have more data copying process + than the map series interface. + +3.2.3.2 Quantification and dequantization + + When using rknn_inputs_set (pass_through=1) and rknn_inputs_map, it indicates that the + process before NPU inference needs to be processed by the user. After rknn_outputs_map gets the + output, the user also needs to dequantize output to get the 32-bit floating point result. + + The quantization method, quantization data type and quantization parameters used in + quantization and dequantization can be queried through the rknn_query interface. Currently, the NPU + of RK1808/RK3399Pro/RV1109/RV1126 has two quantization methods: asymmetric quantization + and dynamic fixed-point quantization. Each quantization method specifies the corresponding + quantized data type. There are a total of four combinations of data types and quantification methods: + + 10 + www.rock-chips.com + +  uint8 (asymmetric quantization) +  int8 (dynamic fixed-point) +  int16 (dynamic fixed-point) +  float16 (none) + Normally, the normalized data is stored in 32-bit floating point data. For conversion of 32-bit +floating point data to 16-bit floating point data, please refer to the IEEE-754 standard. Assuming that +the normalized 32-bit floating point data is D , the following describes the quantization process: + 1) float32 to uint8 + +Assuming that the asymmetric quantization parameter of the input tensor is Sq , ZP , the data + +quantization process is expressed as the following formula: + + Dq  round (clamp(D / Sq  ZP,0,255)) (3-1) + +In the above formula, clamp means to limit the value to a certain range. round means rounding + +processing. + +2) float32 to int8 + +Assuming that the dynamic fixed-point quantization parameter of the input tensor is fl, the data + +quantization process is expressed as the following formula: + + Dq  round (clamp(D * 2 fl ,-128,127)) (3-2) + + 3) float32 to int16 + Assuming that the dynamic fixed-point quantization parameter of the input tensor is fl, the data +quantization process is expressed as the following formula: + + Dq  round (clamp(D * 2 fl ,-32768,32767)) (3-3) + + The dequantization process is the inverse process of quantization, and the inverse quantization +formula can be deduced according to the above quantization formula, which will not be repeated +here. + + 11 + www.rock-chips.com + +3.2.3.3 Zero copy + + In specific case, the number of input data copies can be reduced to 0, that is, zero copy. For +example, when the RKNN model is asymmetric quantization, the quantization data type is uint8, the +mean value of the 3 channels is the same integer and the scaling factor is the same, the normalization +and quantization can be omitted. The proof is as follows: + +Assume that the input image data is D f , and the quantization parameter is Sq , ZP . M i + +Represents the mean value of the i-th channel, and Si represents the normalization factor of the i-th +channel. Then the normalized data of the i-th channel is as follows: + + Di  (D f  M i ) / Si (3-4) + +The data quantification process is expressed as the following formula: + + Dq  clamp(Di / Sq  ZP,0,255) (3-5) + +After combining the above two formulas, we can get (3-6) + Dq  clamp((D f  Mi ) /(Si * Sq)  ZP) + +Assuming that the data range of the calibration data set contains integer values from 0 to 255, + +when M1  M 2  M3 , S1  S2  S3 , the normalized value range is expressed as follows: + + Dmin  (0 - M i ) / Si  M i / Si (3-7) + + Dmax  (255 - M i ) / Si (3-8) + +So, the quantization parameter is calculated as follows: + + Sq  (Dmax  Dmin ) / 255  1/Si (3-9) + + ZP  (0  Dmin ) / Sq  M i (3-10) + + Substituting formula (3-9) and formula (3-10) into formula (3-6), it can be concluded that under +the condition of zero-copy: the mean value of the 3 channels is the same integer and the normalized +scaling factor is the same , The input uint8 data is equal to the quantized uint8 data. + + Input zero copy can reduce the CPU load and improve the speed of complete inference. For + + 12 + www.rock-chips.com + + RGB or BGR input data, the steps to achieve input zero-copy are as follows: + 1) The mean value of the three channels is the same integer and the normalized scaling factor is + + the same. + 2) In the config function of rknn-toolkit, set force_builtin_perm=True to export the RKNN + + model input by NHWC. + 3) Use the rknn_inputs_map interface to obtain the input tensor memory address information. + 4) Fill the memory address with input data, such as calling the RGA resize function, the target + + address uses the physical address obtained by rknn_inputs_map. + 5) Call the rknn_inputs_sync interface. + 6) Call the rknn_run interface. + 7) Call the get output interface. + +3.2.4 API Reference + +3.2.4.1 rknn_init + The rknn_init function will create a rknn_context object, load the RKNN model, and perform + + specific initialization behavior based on the flag. + + 13 + www.rock-chips.com + +API rknn_init + +Description Initialize rknn + +Parameters rknn_context *context:Pointer to rknn_context object. After the function is called, the + + context object will be assigned. + + void *model:Binary data for the RKNN model. + + uint32_t size:Model size + + uint32_t flag:A specific initialization flag. Currently supports following flags: + + RKNN_FLAG_COLLECT_PERF_MASK : Open the performance collection debugging + + switch. After opening, you can query the running time of each layer of the network + + through the rknn_query interface. Note that the running time of rknn_run will be + + longer after this flag is set. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_context ctx; + int ret = rknn_init(&ctx, model_data, model_data_size, 0); + +3.2.4.2 rknn_destroy + + The rknn_destroy function will release the rknn_context object and its associated resources. + +API rknn_destroy + +Description Destroy the rknn_context object and its related resources. + +Parameters rknn_context context: The rknn_context object to be destroyed. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + 14 + www.rock-chips.com + + int ret = rknn_destroy (ctx); + +3.2.4.3 rknn_query + + The rknn_query function can query the information of model input and output tensor attribute, + + performance information and SDK version etc. + +API rknn_query + +Description Query the information about the model and the SDK. + +Parameters rknn_context context: The object of rknn_contex. + + rknn_query_cmd cmd: Query command. + + void* info: Structure object that stores the result of the query. + + uint32_t size: the size of the info Structure object. + +Return int: Error code (See RKNN Error Code). + + Currently, the SDK supports the following query commands: + + Query command Return result structure Function + +RKNN_QUERY_IN_OUT_NUM rknn_input_output_num Query the number of input and output + + Tensor. + +RKNN_QUERY_INPUT_ATTR rknn_tensor_attr Query input Tensor attribute. + +RKNN_QUERY_OUTPUT_ATTR rknn_tensor_attr Query output Tensor attribute. + +RKNN_QUERY_PERF_DETAIL rknn_perf_detail Query the running time of each layer + + of the network. + +RKNN_QUERY_SDK_VERSION rknn_sdk_version Query the SDK version. + + Next we will explain each query command in detail. + + 1) Query the number of input and output Tensor + + The RKNN_QUERY_IN_OUT_NUM command can be used to query the number of model + + input and output Tensor. You need to create the rknn_input_output_num structure object first. + + 15 + www.rock-chips.com + +Sample Code: + +rknn_input_output_num io_num; +ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, + + sizeof(io_num)); +printf("model input num: %d, output num: %d\n", io_num.n_input, + + io_num.n_output); + +2) Query input Tensor attribute + The RKNN_QUERY_INPUT_ATTR command can be used to query the attribute of the model + +input Tensor. You need to create the rknn_tensor_attr structure object first. +Sample Code: + +rknn_tensor_attr input_attrs[io_num.n_input]; +memset(input_attrs, 0, sizeof(input_attrs)); +for (int i = 0; i < io_num.n_input; i++) { + + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +3) Query output Tensor attribute + The RKNN_QUERY_OUTPUT_ATTR command can be used to query the attribute of the model + +output Tensor. You need to create the rknn_tensor_attr structure object first. + Sample Code: + +rknn_tensor_attr output_attrs[io_num.n_output]; +memset(output_attrs, 0, sizeof(output_attrs)); +for (int i = 0; i < io_num.n_output; i++) { + + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + + sizeof(rknn_tensor_attr)); +} + +4) Query the running time of each layer of the network + If the RKNN_FLAG_COLLECT_PERF_MASK flag has been set on the rknn_init function, the + +RKNN_QUERY_PERF_DETAIL command can be passed to query the runtime of each layer of the +network after rknn_run function execution completed. You need to create the rknn_perf_detail + + 16 + www.rock-chips.com + + structure object first. + Sample Code: + + rknn_perf_detail perf_detail; + ret = rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, &perf_detail, + + sizeof(rknn_perf_detail)); + printf("%s", perf_detail.perf_data); + + It should be noted that rknn_perf_detail.perf_data does not need to be released. The SDK will + automatically manage this buffer memory. + 5) Query the SDK version + + The RKNN_QUERY_SDK_VERSION command can be used to query the version information + of the RKNN SDK. You need to create the rknn_sdk_version structure object first. + Sample Code: + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + + sizeof(rknn_sdk_version)); + printf("sdk api version: %s\n", version.api_version); + printf("driver version: %s\n", version.drv_version); + +3.2.4.4 rknn_inputs_set + + The input data of the model can be set by the rknn_inputs_set function. This function can + + support multiple inputs, each one is a rknn_input structure object. Developers needs to set these + + object field before passing in. + +API rknn_inputs_set + +Description Set the model input data. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs. + + rknn_input inputs[]: Array of rknn_input. + +Return int: Error code (See RKNN Error Code). + + 17 + www.rock-chips.com + +Sample Code: + +rknn_input inputs[1]; +memset(inputs, 0, sizeof(inputs)); +inputs[0].index = 0; +inputs[0].type = RKNN_TENSOR_UINT8; +inputs[0].size = img_width*img_height*img_channels; +inputs[0].fmt = RKNN_TENSOR_NHWC; +inputs[0].buf = in_data; + +ret = rknn_inputs_set(ctx, 1, inputs); + +3.2.4.5 rknn_inputs_map + + The rknn_inputs_map function is used to obtain the storage state of the model input tensor after + initialization. The storage state includes virtual address, physical address, fd, and storage space size. + It needs to be used in conjunction with the rknn_inputs_sync interface (see the rknn_inputs_sync + function). After the model is initialized, the user sets the input data through the returned memory + location, and calls the rknn_inputs_sync function before inference. The storage state is represented + by the rknn_tensor_mem structure. The input parameter mem is an array of rknn_tensor_mem + structure. + + Currently, on the RK1808/RV1109/RV1126 chip, the returned fd is -1. When the returned + physical address value is 0xffffffffffffffff (2 to the 64th power-1), it means that the correct physical + address cannot be obtained, and the virtual address is still valid. If there are multiple model input + tensors with large storage space, users can appropriately increase the model input and output storage + space when mounting the driver or increase the CMA memory space in the firmware. Take + RV1109_RV1126 as an example to configure drive storage space, you can refer to the following + modifications: + + Find this line in the /etc/init.d/S60NPU_init file: + insmod /lib/modules/galcore.ko contiguousSize=0x400000 gpuProfiler=1 + Change to + + 18 + www.rock-chips.com + + insmod /lib/modules/galcore.ko contiguousSize=0x600000 gpuProfiler=1 + Then restart to take effect. This configuration should be larger than the total size of user model + + input and output, but not more than the available CMA space in the firmware. + +API rknn_inputs_map + +Description Read input storage status information. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_input inputs[1]; + rknn_tensor_mem mem[1]; + memset(inputs, 0, sizeof(inputs)); + + //set input info returned by rknn_query + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = img_width*img_height*img_channels; + inputs[0].fmt = RKNN_TENSOR_NHWC; + + ret = rknn_inputs_map(ctx, 1, inputs, mem); + +3.2.4.6 rknn_inputs_sync + + The rknn_inputs_sync function writes the CPU cache back to the memory so that the device + + can obtain the correct data. + +API rknn_inputs_sync + +Description Synchronize input data. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs. + + 19 + www.rock-chips.com + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + + ret = rknn_inputs_sync(ctx, 1, mem); + +3.2.4.7 rknn_inputs_unmap + + The rknn_inputs_unmap function will clear the storage location information and flags of the + + input tensor obtained by the rknn_inputs_map function. + +API rknn_inputs_unmap + +Description Clear the storage location information and flags of the input tensor obtained by the + + rknn_inputs_map function. + +Parameter rknn_context context: The object of rknn_contex. + + uint32_t n_inputs: Number of inputs + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + 20 + www.rock-chips.com + + rknn_tensor_mem mem[1]; + ret = rknn_inputs_map(ctx, 1, mem); + ret = rknn_inputs_sync(ctx, 1, mem); + ret = rknn_run(ctx, NULL); + + ret = rknn_inputs_unmap(ctx, 1, inputs, mem); + +3.2.4.8 rknn_run + + The rknn_run function will perform a model reasoning. The input data need to be set by the + + rknn_inputs_set function before rknn_run is called. + +API rknn_run + +Description Perform a model reasoning. + +Parameter rknn_context context: The object of rknn_contex. + + rknn_run_extend* extend: Reserved for extension, currently not used, you can pass + + NULL. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + ret = rknn_run(ctx, NULL); + +3.2.4.9 rknn_outputs_get + + The rknn_outputs_get function can get the output data of the model reasoning. This function + can get multiple output data. Each of these outputs is a rknn_output structure object, which needs to + be created and set in turn before the function is called. + + There are two ways to store buffers for output data: + 1) Developer allocate and release buffers themselves. At this time, the + + rknn_output.is_prealloc needs to be set to 1, and the rknn_output.buf points to users’ + allocated buffer; + + 21 + www.rock-chips.com + + 2) The other is allocated by SDK. At this time, the rknn_output .is_prealloc needs to be set to + + 0. After the function is executed, rknn_output.buf will be created and store the output data. + +API rknn_outputs_get + +Description Get model inference output data. + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_output outputs[]: Array of rknn_output. + + rknn_run_extend* extend: Reserved for extension, currently not used, you can pass + + NULL. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + for (int i = 0; i < io_num.n_output; i++) { + + outputs[i].want_float = 1; + } + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + +3.2.4.10 rknn_outputs_release + + The rknn_outputs_release function will release the relevant resources of the rknn_output object. + +API rknn_outputs_release + +Description Release the rknn_output object + +Parameter rknn_context context: rknn_context object + + uint32_t n_outputs: Number of output. + + rknn_output outputs[]: rknn_output array to be release. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + 22 + www.rock-chips.com + + ret = rknn_outputs_release(ctx, io_num.n_output, outputs); + +3.2.4.11 rknn_outputs_map + + The rknn_outputs_map function obtains the storage state of the output tensor after the model is + + initialized. It needs to be used in conjunction with the rknn_outputs_sync function (see + + rknn_outputs_sync function). After the model is initialized, the rknn_outputs_map interface is called, + + and then the rknn_outputs_sync interface is called after each inference. If users need 32-bit floating + + point data, they need to perform dequantization according to the quantization method and quantized + + data type. + +API rknn_outputs_map + +Description Read output storage status information. + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + + Sample Code: + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_map(ctx, 1, mem); + +3.2.4.12 rknn_outputs_sync + + After the rknn_outputs_map interface is used to map the model output tensor storage state + information when the model is running, in order to ensure cache consistency, the rknn_outputs_sync + function is used to let the CPU obtain the latest data after the inference. + + 23 + www.rock-chips.com + +API rknn_outputs_sync + +Description After inference, synchronize the latest output data + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + +Sample Code: + + rknn_tensor_mem mem[1]; + + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_sync(ctx, io_num.n_output, mem); + +3.2.4.13 rknn_outputs_unmap + + The rknn_outputs_unmap function will clear the storage status of the output tensor obtained by + + the rknn_outputs_map function. + +API rknn_outputs_unmap + +Description Clear the storage state of the output tensor obtained by the rknn_outputs_map + + function. + +Parameter rknn_context context: The object of rknn_context. + + uint32_t n_outputs: Number of output. + + rknn_tensor_mem mem[]: Array of storage status information. Each element of the + + array is an rknn_tensor_mem structure object. + +Return int: Error code (See RKNN Error Code). + +Sample Code: + + 24 + www.rock-chips.com + + rknn_tensor_mem mem[1]; + ret = rknn_outputs_unmap(ctx, io_num.n_output,mem); + +3.2.5 RKNN DataStruct Define + +3.2.5.1 rknn_input_output_num + + The structure rknn_input_output_num represents the number of input and output Tensor , The + following table shows the definition: + + Field Type Meaning +n_input uint32_t The number of input tensor +n_output uint32_t The number of output tensor + +3.2.5.2 rknn_tensor_attr + +The structure rknn_tensor_attr represents the attribute of the model's Tensor. The following table + +shows the definition: + +Field Type Meaning + +index uint32_t Indicates the index position of the input and output + + Tensor. + +n_dims uint32_t The number of Tensor dimensions. + +dims uint32_t[] Values for each dimension. + +name char[] Tensor name. + +n_elems uint32_t The number of Tensor data elements. + +size uint32_t The memory size of Tensor data. + +fmt rknn_tensor_format The format of Tensor dimension, has the following + + format: + + 25 + www.rock-chips.com + + RKNN_TENSOR_NCHW + + RKNN_TENSOR_NHWC + + type rknn_tensor_type Tensor data type, has the following data types: + +qnt_type RKNN_TENSOR_FLOAT32 + + fl RKNN_TENSOR_FLOAT16 + zp + scale RKNN_TENSOR_INT8 + + RKNN_TENSOR_UINT8 + + RKNN_TENSOR_INT16 + + rknn_tensor_qnt_type Tensor Quantization Type, has the following types + + of quantization: + + RKNN_TENSOR_QNT_NONE: Not quantified; + + RKNN_TENSOR_QNT_DFP: Dynamic fixed point + + quantization; + + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: + + Asymmetric quantification. + + int8_t RKNN_TENSOR_QNT_DFP quantization parameter + + uint32_t RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC + + quantization parameter. + + float RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC + + quantization parameter. + +3.2.5.3 rknn_input + + The structure rknn_input represents a data input to the model, used as a parameter to the +rknn_inputs_set function. The following table shows the definition: + + 26 + www.rock-chips.com + + Field Type Meaning + index + buf uint32_t The index position of this input. + size +pass_through void* Point to the input data Buffer. + + type uint32_t The memory size of the input data Buffer. + fmt + uint8_t When set to 1, buf will be directly set to the input + + node of the model without any pre-processing. + + rknn_tensor_type The type of input data. + + rknn_tensor_format The format of input data. + +3.2.5.4 rknn_tensor_mem + +The structure rknn_tensor_mem represents the storage state information after tensor + +initialization, which is used as a parameter to pass in to the rknn_inputs_map series and + +rknn_outputs_map series functions. The definition of the structure is shown in the following table: + +Field Type Meaning + +logical_addr void* The virtual address of this input. + +physical_addr uint64_t The physical address of this input. + +fd int32_t The fd of this input. + +size uint32_t The memory size of the input tensor. + +handle uint32_t The handle of this input. + +priv_data void* Reserved data. + +reserved_flag int32_t Reserved flag. + +3.2.5.5 rknn_output + + The structure rknn_output represents a data output of the model, used as a parameter to the +rknn_outputs_get function. The following table shows the definition: + + 27 + Field Type www.rock-chips.com +want_float uint8_t + Meaning +is_prealloc uint8_t Indicates if the output data needs to be converted + to float type. + index uint32_t Indicates whether the Buffer that stores the output + buf void* data is pre-allocated. + size The index position of this output. + uint32_t Pointer which point to the output data Buffer. + Output data Buffer memory size. + +3.2.5.6 rknn_perf_detail + +The structure rknn_perf_detail represents the performance details of the model. The following + +table shows the definition: + +Field Type Meaning + +perf_data char* Performance details include the run time of each + + layer of the network. + +data_len uint64_t The Length of perf_data. + +3.2.5.7 rknn_sdk_version + +The structure rknn_sdk_version is used to indicate the version information of the RKNN SDK. + +The following table shows the definition: + +Field Type Meaning + +api_version char[] SDK API Version information. + +drv_version char[] Driver version information. + +3.2.6 RKNN Error Code + +The return code of the RKNN API function is defined as shown in the following table. + + 28 + www.rock-chips.com + + Error Code Message + +RKNN_SUCC(0) Execution is successful + +RKNN_ERR_FAIL (-1) Execution error + +RKNN_ERR_TIMEOUT (-2) Execution timeout + +RKNN_ERR_DEVICE_UNAVAILABLE (-3) NPU device is unavailable + +RKNN_ERR_MALLOC_FAIL (-4) Memory allocation is failed + +RKNN_ERR_PARAM_INVALID (-5) Parameter error + +RKNN_ERR_MODEL_INVALID (-6) RKNN model is invalid + +RKNN_ERR_CTX_INVALID (-7) rknn_context is invalid + +RKNN_ERR_INPUT_INVALID (-8) rknn_input object is invalid + +RKNN_ERR_OUTPUT_INVALID (-9) rknn_output object is invalid + +RKNN_ERR_DEVICE_UNMATCH (-10) Version does not match + +RKNN_ERR_INCOMPATILE_PRE_COMPILE_ This RKNN model use pre_compile mode, but not + +MODEL (-11) compatible with current driver. + +RKNN_ERR_INCOMPATILE_OPTIMIZATION_ This RKNN model use optimization level mode, but not + +LEVEL_VERSION (-12) compatible with current driver. + +RKNN_ERR_TARGET_PLATFORM_UNMATC This RKNN model don’t compatible with current + +H (-13) platform. + +RKNN_ERR_NON_PRE_COMPILED_MODEL The RKNN model is not in pre_compile mode and cannot + +_ON_MINI_DRIVER(-14) be executed on mini-driver. + +4 Advanced API instructions +4.1 Matmul Operator library + +4.1.1 Introduction + + The high-level API is designed to use the high computing power of the NPU to perform specific + + 29 + www.rock-chips.com + +mathematical operations, provide a simple interface call, and achieve the effect of computing acceleration. +Among them, the Matmul operator library is an acceleration library for fixed-point matrix multiplication. +The operation is defined as follows: + + C  AT * B + Here: + A, B and C are 2-dimensional matrices + A is an K*M matrix + B is a K*N matrix + C is an M*N matrix + +4.1.2 Data structure definition + +rknn_matmul_handle_t represents the handle used to perform the operation of the Matmul + +operator, which contains the context of the runtime environment and the information of the input + +buffer. The definition of the structure is shown in the following table: + +Field Type Meaning + +A void* The pointer of the first matrix buffer during + + operation. + +B void* The pointer of the second matrix buffer during + + operation. + +M int32_t The low rank dimension of A matrix. + +K int32_t The high rank dimension of A and B matrix. + +N int32_t The low rank dimension of B matrix. + +in_dtype rknn_tensor_type The type of input data. + +rknn_ctx rknn_context The context object at runtime. + + 30 + www.rock-chips.com + +4.1.3 Detailed API description + +4.1.3.1 rknn_matmul_load + + The rknn_matmul_load loading function will load the input buffer created by the user and + + return an object of type rknn_matmul_handle_t.The Matmul operator API is not responsible for + + managing the life cycle of the input buffer, and the user must ensure that the input buffer is valid + + within the Matmul operator API call. + +API rknn_matmul_load + +Description Initialize and set the input buffer pointer. + +Parameter void *a:The pointer of the first matrix buffer created by the user can only support the + + input of 8-bit unsigned integer or 8-bit signed integer one-dimensional array pointer. + + void *b:The pointer of the second matrix buffer created by the user only supports the + + input of 8-bit unsigned integer or 8-bit signed integer one-dimensional array pointer. + + int32_t M: The low rank dimension of A matrix. + + int32_t K: The high rank dimension of A and B matrix. + + int32_t N: The low rank dimension of B matrix. + + rknn_tensor_type dtype : The input data type specified by the user, only supports + + RKNN_TENSOR_INT8 or RKNN_TENSOR_UINT8 type + +Return rknn_matmul_handle_t object。 + + The sample code is as follows: + + rknn_tensor_type dtype = RKNN_TENSOR_INT8; + int8_t x[256*1] = {0}; + int8_t y[256*4096] = {0}; + rknn_matmul_handle_t handle= rknn_matmul_load(x,y,1,256,4096,dtype); + +4.1.3.2 rknn_matmul_run + After rknn_matmul_load is called and before rknn_matmul_run is executed, the data in the + + 31 + www.rock-chips.com + + input buffer is updated externally, without calling rknn_matmul_load again. + +API rknn_matmul_run + +Description Perform Matmul operations. + +Parameter rknn_matmul_handle_t matmul_handle: The handle returned by the + + rknn_matmul_load interface. + + float *c: The pointer to the matrix floating-point buffer created by the user, used to + + obtain the output. + +Return int: Error code (See RKNN Error Code). + + The sample code is as follows: + + ... + float out_fp32_buf[4096] = {0}; + rknn_matmul_run(handle,out_fp32_buf); + +4.1.3.3 rknn_matmul_unload + +API rknn_matmul_unload + +Description Destroy the Matmul operator runtime context. + +Parameter rknn_matmul_handle_t matmul_handle: The handle returned by the + + rknn_matmul_load interface. + +Return int: Error code (See RKNN Error Code). + + The sample code is as follows: + + ... + rknn_matmul_unload(handle); + +4.1.4 Implementation restrictions + + The Matmul operator library is implemented based on the hardware architecture of the NPU. In +order to achieve a balance between accuracy and speed, there are some restrictions as follows. + + 32 + www.rock-chips.com + +4.1.4.1 Dimensional restrictions + + According to the above operation description, the library realizes the matrix multiplication of M=1. +Specifically, the input A of the Matmul operator must be a Kx1 buffer(row-major), that is, the user must +create a piece of data that contains K 8-bit unsigned integers or 8-bit signed integers. When the operator +library is running on the Mini driver, the value of K can only be set to 128 or 256 or 512, and N is fixed +at 4096. When running on the Full driver, there is no such limit, but the recommended number of K is +128, 256, 512, 1024, 2048, N is an even power of 2. It is recommended that N should not be greater than +4096. + +4.1.4.2 Input data type restriction + + Only supports 8-bit unsigned integer and 8-bit signed integer input. + +4.1.5 Benchmark + + When the two input matrices use random numbers, the measured results on the RV1109-EVB board +are shown in Table-1. The speed is the average time after the rknn_matmul_run interface is called 100 +times. The average relative error is the error value of the result of executing the same algorithm on the +NPU and CPU. The specific formula is: + + (abs(R1  R2 ) / R2 ) / N + + k + + among them, + R1 is the output vector of the Matmul operator library, containing N elements. + R2 is the CPU output vector, containing N elements. + + 33 + www.rock-chips.com + + Table-1 Speed/accuracy results of Matmul operator library (RV1109, int8) + +K N Speed(ms) Average relative error + +128 1024 1.0 0.00034 + +256 1024 1.6 0.00032 + +512 2024 3.0 -0.00015 + +1024 1024 5.4 0.00047 + +128 4096 3.0 0.00051 + +256 4096 5.6 0.00024 + +512 4096 10.7 0.00024 + +1024 4096 20.9 0.00051 + +Note: the speed may vary slightly due to different NPU driver versions. The error value may varies + +slightly according to the random number of each test. + + 34 + www.rock-chips.com + +5 NPU driver description + +5.1.1 Directory structure description + + The NPU driver is in the $SDK/external/rknpu/drivers/ directory or +https://github.com/rockchip-linux/rknpu/tree/master/drivers +The compilation and installation rules refer to $SDK/buildroot/package/rockchip/rknpu/rknpu.mk +drivers/ +├── common +├── linux-aarch64 (for RK1808 npu full driver) +├── linux-aarch64-mini (for RK1808 npu mini driver) +├── linux-armhf (for RK1806 npu full driver) +├── linux-armhf-mini(for RK1806 npu mini driver) +├── linux-armhf-puma (for RV1126/RV1109 npu full driver) +├── linux-armhf-puma-mini(for RV1126/RV1109 npu mini driver) +├── npu_ko (NPU kernel driver) + +5.1.2 The difference between NPU full driver and mini driver + + Include the following points: + 1) Mini driver only supports pre-compiled rknn model. If you run non-pre-compiled model, +RKNN_ERR_MODEL_INVALID error will appear. Starting from 1.6.0, it will return +RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER error; + 2) The full driver supports the online debugging function of RKNN Toolkit, but the mini driver does +not; + 3) Mini driver library size is much smaller than full driver. Taking RV1109/RV1126 1.6.0 driver as +an example, full driver size is 87MB, mini driver size is 7.1MB, which can effectively save flash size. + 4) Mini driver library occupies less memory than full driver when running. + + 35 + www.rock-chips.com + +6 FAQ + +6.1.1 Input and output data format issues + +6.1.1.1 How to choose from RGB or BGR format when given three-channel image data + input? + + It is recommended that using RGB format input data uniformly. When exporting the RKNN model, +there are two possibilities for the reorder_channel parameter of the config function: + + 1) If the original model is trained using BGR images, reorder_channel = '2 1 0'. + 2) If the original model is trained using RGB images, reorder_channel = '0 1 2'. + +6.1.1.2 Which RKNN_TENSOR_NHWC or RKNN_TENSOR_NCHW should be set in the + rknn_input structure? Why are the two settings time-consuming different? + + The rknn_input structure is determined according to the user's own data format, and the C API will +automatically convert it into the format required by the NPU. + + The reason for the different time-consuming is that different input formats have different calculation +amounts and different optimization methods. + +6.1.1.3 For the non-quantized RKNN model, why is the size in the output + rknn_tensor_attr different from the size of the rknn_output returned by the + rknn_outputs_get interface? + + The non-quantized RKNN model, the internal output data type of the NPU is float16, and the size is +the number of elements * 2 bytes. When the user sets want_float = 1, what they want is float32 data, +float16 will be converted to float32, and the size is the number of elements * 4 bytes. + + 36 + www.rock-chips.com + +6.1.1.4 Is rknn_output.index set by user or return by driver? + +Return by driver. + +6.1.1.5 Why does the dims array of the rknn_tensor_attr have 0? + +0 means the size is invalid. n_dims in rknn_tensor_attr represents the number of valid size in dims. + +6.1.1.6 The order of dims in rknn_tensor_attr is opposite to the order of numpy obtained + by rknn_toolkit? + + The layout of arrays in C API is the opposite of python. For example, the numpy output shape +obtained by the run() interface of rknn-toolkit is [1,255,20,20], and the dims array in C API is +{20,20,255,1}. + +6.1.2 Input and output interface usage problems + +6.1.2.1 How to preprocess the data when using pass_through and using + rknn_inputs_map interface? + + Please refer to the rknn_pass_through_demo example under +https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples. + +6.1.2.2 Why is the physical address obtained using rknn_inputs_map or + rknn_outputs_map invalid? How to obtain a valid physical address? + + Input/output cannot be allocated to physically contiguous memory. The possible reasons are: + 1) The input/output size is too large, exceeding the total physically contiguous memory size (the +default is 4MB). + 2) There is not enough physically contiguous memory available in the system. + 3) When exporting the RKNN model, add the following parameter to the config function: + + 37 + www.rock-chips.com + +output_optimize=1. + Users can try to restart the system, or configure a larger physically continuous memory space when + +the NPU driver is mounted. For the configuration method, refer to the rknn_inputs_map interface +description. + +6.1.3 API call process issues + +6.1.3.1 After rknn_init succeeds, can the memory occupied by the model file be + released? + + Yes,you can. + +6.1.3.2 When rknn_output.is_prealloc=1, does rknn_outputs_release need to be called? + + Yes, it needs. + +6.1.4 Performance issues + +6.1.4.1 rknn_init takes too long? + + Use pre-compiled models. Refer to the relevant chapters of the User Guide document under +https://github.com/rockchip-linux/rknn-toolkit/tree/master/doc for usage. + +6.1.4.2 rknn_inputs_set takes too long? + + The possible reason is the large amount of data or the time-consuming format conversion. If it takes +a long time to convert the format, users can try the pass_through usage to do the conversion themselves. +For conversion method, please refer to rknn_pass_through_demo example under +https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples, or try to export the RKNN +model by adding the following parameter to the config function: output_optimize=1. + + 38 + www.rock-chips.com + +6.1.4.3 rknn_outputs_get takes too long? + + The possible reason is the large amount of data or the time-consuming format conversion. If it takes +a long time to convert the format, users can try to set want_float=0 and do the conversion themselves, or +try to export the RKNN model by adding the following parameter to the config function: +output_optimize=1. + + 39 + diff --git a/libs/rklibs/rknpu/rknn/include/rknn_runtime.h b/libs/rklibs/rknpu/rknn/include/rknn_runtime.h new file mode 100644 index 0000000..a114c1f --- /dev/null +++ b/libs/rklibs/rknpu/rknn/include/rknn_runtime.h @@ -0,0 +1,504 @@ +/**************************************************************************** +* +* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved. +* +* The material in this file is confidential and contains trade secrets +* of Rockchip Corporation. This is proprietary information owned by +* Rockchip Corporation. No part of this work may be disclosed, +* reproduced, copied, transmitted, or used in any way for any purpose, +* without the express written permission of Rockchip Corporation. +* +*****************************************************************************/ + + +#ifndef _RKNN_RUNTIME_H +#define _RKNN_RUNTIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + Definition of extended flag for rknn_init. +*/ +/* set high priority context. */ +#define RKNN_FLAG_PRIOR_HIGH 0x00000000 + +/* set medium priority context */ +#define RKNN_FLAG_PRIOR_MEDIUM 0x00000001 + +/* set low priority context. */ +#define RKNN_FLAG_PRIOR_LOW 0x00000002 + +/* asynchronous mode. + when enable, rknn_outputs_get will not block for too long because it directly retrieves the result of + the previous frame which can increase the frame rate on single-threaded mode, but at the cost of + rknn_outputs_get not retrieves the result of the current frame. + in multi-threaded mode you do not need to turn this mode on. */ +#define RKNN_FLAG_ASYNC_MASK 0x00000004 + +/* collect performance mode. + when enable, you can get detailed performance reports via rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, ...), + but it will reduce the frame rate. */ +#define RKNN_FLAG_COLLECT_PERF_MASK 0x00000008 + +/* + save pre-compile model. +*/ +#define RKNN_FLAG_PRECOMPILE_MASK 0x00000020 + +/* + Error code returned by the RKNN API. +*/ +#define RKNN_SUCC 0 /* execute succeed. */ +#define RKNN_ERR_FAIL -1 /* execute failed. */ +#define RKNN_ERR_TIMEOUT -2 /* execute timeout. */ +#define RKNN_ERR_DEVICE_UNAVAILABLE -3 /* device is unavailable. */ +#define RKNN_ERR_MALLOC_FAIL -4 /* memory malloc fail. */ +#define RKNN_ERR_PARAM_INVALID -5 /* parameter is invalid. */ +#define RKNN_ERR_MODEL_INVALID -6 /* model is invalid. */ +#define RKNN_ERR_CTX_INVALID -7 /* context is invalid. */ +#define RKNN_ERR_INPUT_INVALID -8 /* input is invalid. */ +#define RKNN_ERR_OUTPUT_INVALID -9 /* output is invalid. */ +#define RKNN_ERR_DEVICE_UNMATCH -10 /* the device is unmatch, please update rknn sdk + and npu driver/firmware. */ +#define RKNN_ERR_INCOMPATILE_PRE_COMPILE_MODEL -11 /* This RKNN model use pre_compile mode, but not compatible with current driver. */ +//add by chifred: for reporting optimization version bug info +#define RKNN_ERR_INCOMPATILE_OPTIMIZATION_LEVEL_VERSION -12 /* This RKNN model set optimization level, but not compatible with current driver. */ +#define RKNN_ERR_TARGET_PLATFORM_UNMATCH -13 /* This RKNN model set target platform, but not compatible with current platform. */ +//chifred add end +#define RKNN_ERR_NON_PRE_COMPILED_MODEL_ON_MINI_DRIVER -14 /* This RKNN model is not a pre-compiled model, but the npu driver is mini driver. */ + +/* + Definition for tensor +*/ +#define RKNN_MAX_DIMS 16 /* maximum dimension of tensor. */ +#define RKNN_MAX_NAME_LEN 256 /* maximum name lenth of tensor. */ + + + +#ifdef __arm__ +typedef uint32_t rknn_context; +#else +typedef uint64_t rknn_context; +#endif + +/* + The query command for rknn_query +*/ +typedef enum _rknn_query_cmd { + RKNN_QUERY_IN_OUT_NUM = 0, /* query the number of input & output tensor. */ + RKNN_QUERY_INPUT_ATTR, /* query the attribute of input tensor. */ + RKNN_QUERY_OUTPUT_ATTR, /* query the attribute of output tensor. */ + RKNN_QUERY_PERF_DETAIL, /* query the detail performance, need set + RKNN_FLAG_COLLECT_PERF_MASK when call rknn_init. */ + RKNN_QUERY_PERF_RUN, /* query the time of run. */ + RKNN_QUERY_SDK_VERSION, /* query the sdk & driver version */ + RKNN_QUERY_PRE_COMPILE, /* query the pre compile model */ + + RKNN_QUERY_CMD_MAX +} rknn_query_cmd; + +/* + the tensor data type. +*/ +typedef enum _rknn_tensor_type { + RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */ + RKNN_TENSOR_FLOAT16, /* data type is float16. */ + RKNN_TENSOR_INT8, /* data type is int8. */ + RKNN_TENSOR_UINT8, /* data type is uint8. */ + RKNN_TENSOR_INT16, /* data type is int16. */ + + RKNN_TENSOR_TYPE_MAX +} rknn_tensor_type; + +/* + the quantitative type. +*/ +typedef enum _rknn_tensor_qnt_type { + RKNN_TENSOR_QNT_NONE = 0, /* none. */ + RKNN_TENSOR_QNT_DFP, /* dynamic fixed point. */ + RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC, /* asymmetric affine. */ + + RKNN_TENSOR_QNT_MAX +} rknn_tensor_qnt_type; + +/* + the tensor data format. +*/ +typedef enum _rknn_tensor_format { + RKNN_TENSOR_NCHW = 0, /* data format is NCHW. */ + RKNN_TENSOR_NHWC, /* data format is NHWC. */ + + RKNN_TENSOR_FORMAT_MAX +} rknn_tensor_format; + +/* + the information for RKNN_QUERY_IN_OUT_NUM. +*/ +typedef struct _rknn_input_output_num { + uint32_t n_input; /* the number of input. */ + uint32_t n_output; /* the number of output. */ +} rknn_input_output_num; + +/* + the information for RKNN_QUERY_INPUT_ATTR / RKNN_QUERY_OUTPUT_ATTR. +*/ +typedef struct _rknn_tensor_attr { + uint32_t index; /* input parameter, the index of input/output tensor, + need set before call rknn_query. */ + + uint32_t n_dims; /* the number of dimensions. */ + uint32_t dims[RKNN_MAX_DIMS]; /* the dimensions array. */ + char name[RKNN_MAX_NAME_LEN]; /* the name of tensor. */ + + uint32_t n_elems; /* the number of elements. */ + uint32_t size; /* the bytes size of tensor. */ + + rknn_tensor_format fmt; /* the data format of tensor. */ + rknn_tensor_type type; /* the data type of tensor. */ + rknn_tensor_qnt_type qnt_type; /* the quantitative type of tensor. */ + int8_t fl; /* fractional length for RKNN_TENSOR_QNT_DFP. */ + uint32_t zp; /* zero point for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ + float scale; /* scale for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */ +} rknn_tensor_attr; + +/* + the information for RKNN_QUERY_PERF_DETAIL. +*/ +typedef struct _rknn_perf_detail { + char* perf_data; /* the string pointer of perf detail. don't need free it by user. */ + uint64_t data_len; /* the string length. */ +} rknn_perf_detail; + +/* + the information for RKNN_QUERY_PERF_RUN. +*/ +typedef struct _rknn_perf_run { + int64_t run_duration; /* real inference time (us) */ +} rknn_perf_run; + +/* + the information for RKNN_QUERY_SDK_VERSION. +*/ +typedef struct _rknn_sdk_version { + char api_version[256]; /* the version of rknn api. */ + char drv_version[256]; /* the version of rknn driver. */ +} rknn_sdk_version; + +/* + The flags of rknn_tensor_mem. +*/ +typedef enum _rknn_tensor_mem_flags { + RKNN_TENSOR_MEMORY_FLAGS_UNKNOWN = 0, + RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE = 1, /*Used to mark in rknn_destroy_mem() whether it is necessary to release the "mem" pointer itself. + If the flag RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE is set, rknn_destroy_mem() will call free(mem).*/ + +} rknn_tensor_mem_flags; + + +/* + the memory information of tensor. +*/ +typedef struct _rknn_tensor_memory { + void* logical_addr; /* the virtual address of tensor buffer. */ + uint64_t physical_addr; /* the physical address of tensor buffer. */ + int32_t fd; /* the fd of tensor buffer. */ + uint32_t size; /* the size of tensor buffer. */ + uint32_t handle; /* the handle tensor buffer. */ + void * priv_data; /* the data which is reserved. */ + uint64_t reserved_flag; /* the flag which is reserved. */ +} rknn_tensor_mem; + +/* + the input information for rknn_input_set. +*/ +typedef struct _rknn_input { + uint32_t index; /* the input index. */ + void* buf; /* the input buf for index. */ + uint32_t size; /* the size of input buf. */ + uint8_t pass_through; /* pass through mode. + if TRUE, the buf data is passed directly to the input node of the rknn model + without any conversion. the following variables do not need to be set. + if FALSE, the buf data is converted into an input consistent with the model + according to the following type and fmt. so the following variables + need to be set.*/ + rknn_tensor_type type; /* the data type of input buf. */ + rknn_tensor_format fmt; /* the data format of input buf. + currently the internal input format of NPU is NCHW by default. + so entering NCHW data can avoid the format conversion in the driver. */ +} rknn_input; + +/* + the output information for rknn_outputs_get. +*/ +typedef struct _rknn_output { + uint8_t want_float; /* want transfer output data to float */ + uint8_t is_prealloc; /* whether buf is pre-allocated. + if true, the following variables need to be set. + if false, The following variables do not need to be set. */ + uint32_t index; /* the output index. */ + void* buf; /* the output buf for index. + when is_prealloc = FALSE and rknn_outputs_release called, + this buf pointer will be free and don't use it anymore. */ + uint32_t size; /* the size of output buf. */ +} rknn_output; + +/* + the extend information for rknn_run. +*/ +typedef struct _rknn_run_extend { + uint64_t frame_id; /* output parameter, indicate current frame id of run. */ +} rknn_run_extend; + +/* + the extend information for rknn_outputs_get. +*/ +typedef struct _rknn_output_extend { + uint64_t frame_id; /* output parameter, indicate the frame id of outputs, corresponds to + struct rknn_run_extend.frame_id.*/ +} rknn_output_extend; + +/* + the information for RKNN_QUERY_RKNN_PRECOMPILE. +*/ +typedef struct _rknn_precompile { + void* model_data; /* the pointer of precompile model. don't need free it by user. */ + uint32_t data_len; /* the model length. */ +} rknn_precompile; + + +/* rknn_init + + initial the context and load the rknn model. + + input: + rknn_context* context the pointer of context handle. + void* model pointer to the rknn model. + uint32_t size the size of rknn model. + uint32_t flag extend flag, see the define of RKNN_FLAG_XXX_XXX. + return: + int error code. +*/ +int rknn_init(rknn_context* context, void* model, uint32_t size, uint32_t flag); + + +/* rknn_destroy + + unload the rknn model and destroy the context. + + input: + rknn_context context the handle of context. + return: + int error code. +*/ +int rknn_destroy(rknn_context context); + + +/* rknn_query + + query the information about model or others. see rknn_query_cmd. + + input: + rknn_context context the handle of context. + rknn_query_cmd cmd the command of query. + void* info the buffer point of information. + uint32_t size the size of information. + return: + int error code. +*/ +int rknn_query(rknn_context context, rknn_query_cmd cmd, void* info, uint32_t size); + + +/* rknn_inputs_set + + set inputs information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_input inputs[] the arrays of inputs information, see rknn_input. + return: + int error code +*/ +int rknn_inputs_set(rknn_context context, uint32_t n_inputs, rknn_input inputs[]); + + +/* rknn_inputs_map + + map inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_map(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_sync + + synchronize inputs tensor buffer by input index of rknn model. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_sync(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_inputs_unmap + + unmap inputs tensor memory information by input index of rknn model. + inputs information see rknn_input. + + input: + rknn_context context the handle of context. + uint32_t n_inputs the number of inputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_inputs_unmap(rknn_context context, uint32_t n_inputs, rknn_tensor_mem mem[]); + + +/* rknn_run + + run the model to execute inference. + + input: + rknn_context context the handle of context. + rknn_run_extend* extend the extend information of run. + return: + int error code. +*/ +int rknn_run(rknn_context context, rknn_run_extend* extend); + + +/* rknn_outputs_get + + wait the inference to finish and get the outputs. + this function will block until inference finish. + the results will set to outputs[]. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_output outputs[] the arrays of output, see rknn_output. + rknn_output_extend* the extend information of output. + return: + int error code. +*/ +int rknn_outputs_get(rknn_context context, uint32_t n_outputs, rknn_output outputs[], rknn_output_extend* extend); + + +/* rknn_outputs_release + + release the outputs that get by rknn_outputs_get. + after called, the rknn_output[x].buf get from rknn_outputs_get will + also be free when rknn_output[x].is_prealloc = FALSE. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_output outputs[] the arrays of output. + return: + int error code +*/ +int rknn_outputs_release(rknn_context context, uint32_t n_ouputs, rknn_output outputs[]); + + +/* rknn_outputs_map + + map the model output tensors memory information. + The difference between this function and "rknn_outputs_get" is + that it directly maps the model output tensor memory location to the user. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_map(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_sync + + synchronize the output tensors buffer to ensure cache cohenrency, wait the inference to finish. + + input: + rknn_context context the handle of context. + uint32_t n_outputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code. +*/ +int rknn_outputs_sync(rknn_context context, uint32_t n_outputs, rknn_tensor_mem mem[]); + +/* rknn_outputs_unmap + + unmap the outputs memory information that get by rknn_outputs_map. + + input: + rknn_context context the handle of context. + uint32_t n_ouputs the number of outputs. + rknn_tensor_mem mem[] the array of tensor memory information + return: + int error code +*/ +int rknn_outputs_unmap(rknn_context context, uint32_t n_ouputs, rknn_tensor_mem mem[]); + +/* rknn_create_mem (memory allocated inside) + + Create tensor memory. This API require libdrm support! + + input: + rknn_context ctx the handle of context. + uint64_t size the size of tensor buffer. + return: + rknn_tensor_mem the pointer of tensor memory information. +*/ +rknn_tensor_mem* rknn_create_mem(rknn_context ctx, uint64_t size); + +/* rknn_destroy_mem (support allocate inside and outside) + + destroy tensor memory. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the pointer of tensor memory information. + return: + int error code +*/ +int rknn_destroy_mem(rknn_context ctx, rknn_tensor_mem *mem); + + + +/* rknn_set_io_mem + + set the input and output tensors buffer. + + input: + rknn_context ctx the handle of context. + rknn_tensor_mem *mem the array of tensor memory information. + rknn_tensor_attr *attr the attribute of input or output tensor buffer. + return: + int error code. +*/ +int rknn_set_io_mem(rknn_context ctx, rknn_tensor_mem *mem, rknn_tensor_attr *attr); + +#ifdef __cplusplus +} //extern "C" +#endif + +#endif //_RKNN_RUNTIME_H diff --git a/libs/rklibs/rknpu/rknn/python/rknn/README b/libs/rklibs/rknpu/rknn/python/rknn/README new file mode 100644 index 0000000..7beab1a --- /dev/null +++ b/libs/rklibs/rknpu/rknn/python/rknn/README @@ -0,0 +1 @@ +# RKNN \ No newline at end of file diff --git a/libs/rklibs/rknpu/rknn/python/rknn/VERSION b/libs/rklibs/rknpu/rknn/python/rknn/VERSION new file mode 100644 index 0000000..d612b7f --- /dev/null +++ b/libs/rklibs/rknpu/rknn/python/rknn/VERSION @@ -0,0 +1 @@ +0.9.7.1 diff --git a/libs/rklibs/rknpu/rknn/python/rknn/__init__.py b/libs/rklibs/rknpu/rknn/python/rknn/__init__.py new file mode 100644 index 0000000..cfc82a4 --- /dev/null +++ b/libs/rklibs/rknpu/rknn/python/rknn/__init__.py @@ -0,0 +1 @@ +from rknn import api diff --git a/libs/rklibs/rknpu/rknn/python/rknn1808-1.2.0-cp36-cp36m-linux_aarch64.whl b/libs/rklibs/rknpu/rknn/python/rknn1808-1.2.0-cp36-cp36m-linux_aarch64.whl new file mode 100644 index 0000000..0b99684 Binary files /dev/null and b/libs/rklibs/rknpu/rknn/python/rknn1808-1.2.0-cp36-cp36m-linux_aarch64.whl differ diff --git a/libs/rklibs/rknpu/rknn/python/rknn1808-1.2.0-cp37-cp37m-linux_aarch64.whl b/libs/rklibs/rknpu/rknn/python/rknn1808-1.2.0-cp37-cp37m-linux_aarch64.whl new file mode 100644 index 0000000..08ca084 Binary files /dev/null and b/libs/rklibs/rknpu/rknn/python/rknn1808-1.2.0-cp37-cp37m-linux_aarch64.whl differ diff --git a/libs/rklibs/rknpu/rknn/rknn_utils/CMakeLists.txt b/libs/rklibs/rknpu/rknn/rknn_utils/CMakeLists.txt new file mode 100644 index 0000000..ab0d1f6 --- /dev/null +++ b/libs/rklibs/rknpu/rknn/rknn_utils/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.4.1) + +project(rknn_matmul_sample_linux) + +set(CMAKE_SYSTEM_NAME Linux) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +if (CMAKE_C_COMPILER MATCHES "aarch64") + set(LIB_ARCH lib64) +else() + set(LIB_ARCH lib) +endif() + +# rknn_utils +include_directories(${CMAKE_SOURCE_DIR}/librknn_utils/include) +set(RKNN_UTILS_LIB ${CMAKE_CURRENT_SOURCE_DIR}/librknn_utils/${LIB_ARCH}/librknn_utils.so) + +add_executable(rknn_matmul_sample + example/matmul_sample.c +) + +target_link_libraries(rknn_matmul_sample + "-Wl,--allow-shlib-undefined" ${RKNN_UTILS_LIB} +) + +# install target +set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_matmul_sample) +install(TARGETS rknn_matmul_sample DESTINATION bin) +install(PROGRAMS ${RKNN_UTILS_LIB} DESTINATION lib) diff --git a/libs/rklibs/rknpu/rknn/rknn_utils/README.md b/libs/rklibs/rknpu/rknn/rknn_utils/README.md new file mode 100644 index 0000000..bdac65e --- /dev/null +++ b/libs/rklibs/rknpu/rknn/rknn_utils/README.md @@ -0,0 +1,28 @@ +## build + +modify `GCC_COMPILER` on `build.sh` for target platform, then execute + +``` +./build.sh +``` + +## install + +connect device and push build output into `/userdata` + +``` +adb push install/rknn_matmul_sample/lib/librknn_utils.so /usr/lib +adb push install/rknn_matmul_sample /userdata/ +``` + +## run + +``` +adb shell +cd /userdata/rknn_matmul_sample/ +``` + +- /rk180x/rv1109/rv1126 +``` +./bin/rknn_matmul_sample 256 4096 +``` diff --git a/libs/rklibs/rknpu/rknn/rknn_utils/build.sh b/libs/rklibs/rknpu/rknn/rknn_utils/build.sh new file mode 100644 index 0000000..75a9a87 --- /dev/null +++ b/libs/rklibs/rknpu/rknn/rknn_utils/build.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -e + +# for rk1808 aarch64 +# GCC_COMPILER=${RK1808_TOOL_CHAIN}/bin/aarch64-linux-gnu + + +# for rk1806 armhf +# GCC_COMPILER=~/opts/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf + +# for rv1109/rv1126 armhf +GCC_COMPILER=${RV1109_TOOL_CHAIN}/bin/arm-linux-gnueabihf + +ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd ) + +# build rockx +BUILD_DIR=${ROOT_PWD}/build + +if [[ ! -d "${BUILD_DIR}" ]]; then + mkdir -p ${BUILD_DIR} +fi + +cd ${BUILD_DIR} +cmake .. \ + -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \ + -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++ +make -j4 +make install +cd - \ No newline at end of file diff --git a/libs/rklibs/rknpu/rknn/rknn_utils/example/matmul_sample.c b/libs/rklibs/rknpu/rknn/rknn_utils/example/matmul_sample.c new file mode 100644 index 0000000..7fec91a --- /dev/null +++ b/libs/rklibs/rknpu/rknn/rknn_utils/example/matmul_sample.c @@ -0,0 +1,182 @@ +/** + * Example: Calculate (x-y)^2 using Matmul API + */ + + +/*------------------------------------------- + Includes +-------------------------------------------*/ +#include +#include +#include +#include +#include +#include + +#include +#include "rknn_matmul_api.h" + +/*------------------------------------------- + Macros and Variables +-------------------------------------------*/ + +double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); } + +static int gen_random_data(int8_t *x,int len,int min_value,int max_value) { + srand((unsigned int)time(NULL)); + int range = max_value-min_value+1; + for (int i = 0;i \n",argv[0]); + return -1; + } + int DIM = atoi(argv[1]); + int BATCH_SIZE = atoi(argv[2]); + rknn_tensor_type dtype = RKNN_TENSOR_INT8; + int8_t *x = (int8_t *)malloc(DIM*sizeof(int8_t)); + int8_t *y = (int8_t *)malloc(DIM*BATCH_SIZE*sizeof(int8_t)); + float out_fp32_buf[BATCH_SIZE]; + float res_cpu[BATCH_SIZE]; + int x_size = DIM; + int y_size = DIM*BATCH_SIZE; + int test_count = 100; + float err; + struct timeval start_time, stop_time; + + int *square_y = (int *)malloc(BATCH_SIZE*sizeof(int)); + + //generate random test data + gen_random_data(x,x_size,-128,127); + gen_random_data(y,y_size,-128,127); + pre_process_y(y,DIM,BATCH_SIZE,square_y); + + rknn_matmul_handle_t handle= rknn_matmul_load(x,y, 1, DIM, BATCH_SIZE, dtype); + if (handle == NULL) + { + fprintf(stderr, "rknn_matmul_load failed.\n"); + return -1; + } + + //loop for matmul + gettimeofday(&start_time, NULL); + for(int i = 0;i + +int drm_init(drm_context *drm_ctx) +{ + static const char *card = "/dev/dri/card0"; + int flag = O_RDWR; + int drm_fd = -1; + + drm_fd = open(card, flag); + if (drm_fd < 0) + { + printf("failed to open %s\n", card); + return -1; + } + + drm_ctx->drm_handle = dlopen("libdrm.so", RTLD_LAZY); + + if (!drm_ctx->drm_handle) + { + printf("failed to dlopen libdrm.so\n"); + printf("dlopen error: %s\n", dlerror()); + drm_deinit(drm_ctx, drm_fd); + return -1; + } + + drm_ctx->io_func = (FUNC_DRM_IOCTL)dlsym(drm_ctx->drm_handle, "drmIoctl"); + if (drm_ctx->io_func == NULL) + { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + drm_deinit(drm_ctx, drm_fd); + printf("failed to dlsym drmIoctl\n"); + return -1; + } + return drm_fd; +} + +void drm_deinit(drm_context *drm_ctx, int drm_fd) +{ + if (drm_ctx->drm_handle) + { + dlclose(drm_ctx->drm_handle); + drm_ctx->drm_handle = NULL; + } + if (drm_fd > 0) + { + close(drm_fd); + } +} + +void *drm_buf_alloc(drm_context *drm_ctx, int drm_fd, int TexWidth, int TexHeight, int bpp, int *fd, unsigned int *handle, size_t *actual_size) +{ + // printf("0: Width %d, Heigh %d, bpp %d\n", TexWidth, TexHeight, bpp); + // printf("fd: %d, handle: %d, size: %d\n", fd, handle, actual_size); + int ret; + if (drm_ctx == NULL) + { + printf("drm context is unvalid\n"); + return NULL; + } + char *map = NULL; + + // printf("Width %d, Heigh %d\n", TexWidth, TexHeight); + + void *vir_addr = NULL; + struct drm_prime_handle fd_args; + struct drm_mode_map_dumb mmap_arg; + struct drm_mode_destroy_dumb destory_arg; + + struct drm_mode_create_dumb alloc_arg; + + memset(&alloc_arg, 0, sizeof(alloc_arg)); + alloc_arg.bpp = bpp; + alloc_arg.width = TexWidth; + alloc_arg.height = TexHeight; + // alloc_arg.flags = ROCKCHIP_BO_CONTIG; + + // printf("2: Width %d, Heigh %d\n", TexWidth, TexHeight); + + //获取handle和size + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); + // printf("3: Width %d, Heigh %d\n", TexWidth, TexHeight); + if (ret) + { + printf("failed to create dumb buffer: %s\n", strerror(errno)); + return NULL; + } + // printf("4: Width %d, Heigh %d\n", TexWidth, TexHeight); + if (handle != NULL) + { + *handle = alloc_arg.handle; + } + // printf("5: Width %d, Heigh %d\n", TexWidth, TexHeight); + if (actual_size != NULL) + { + *actual_size = alloc_arg.size; + } + // printf("create width=%u, height=%u, bpp=%u, size=%lu dumb buffer\n",alloc_arg.width,alloc_arg.height,alloc_arg.bpp,alloc_arg.size); + // printf("out handle= %d\n",alloc_arg.handle); + + //获取fd + memset(&fd_args, 0, sizeof(fd_args)); + fd_args.fd = -1; + fd_args.handle = alloc_arg.handle; + ; + fd_args.flags = 0; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &fd_args); + if (ret) + { + printf("rk-debug handle_to_fd failed ret=%d,err=%s, handle=%x \n", ret, strerror(errno), fd_args.handle); + return NULL; + } + // printf("out fd = %d, drm fd: %d\n",fd_args.fd,drm_fd); + if (fd != NULL) + { + *fd = fd_args.fd; + } + + //获取虚拟地址 + memset(&mmap_arg, 0, sizeof(mmap_arg)); + mmap_arg.handle = alloc_arg.handle; + + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); + if (ret) + { + printf("failed to create map dumb: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + vir_addr = map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, mmap_arg.offset); + if (map == MAP_FAILED) + { + printf("failed to mmap buffer: %s\n", strerror(errno)); + vir_addr = NULL; + goto destory_dumb; + } + // printf("alloc map=%x \n",map); + return vir_addr; +destory_dumb: + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = alloc_arg.handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d\n", ret); + return vir_addr; +} + +int drm_buf_destroy(drm_context *drm_ctx, int drm_fd, int buf_fd, int handle, void *drm_buf, size_t size) +{ + int ret = -1; + if (drm_buf == NULL) + { + printf("drm buffer is NULL\n"); + return -1; + } + + munmap(drm_buf, size); + + struct drm_mode_destroy_dumb destory_arg; + memset(&destory_arg, 0, sizeof(destory_arg)); + destory_arg.handle = handle; + ret = drm_ctx->io_func(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg); + if (ret) + printf("failed to destory dumb %d, error=%s\n", ret, strerror(errno)); + if (buf_fd > 0) + { + close(buf_fd); + } + + return ret; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2bf8321 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,1605 @@ +#include "main.h" +//4 13 22 31 +#include +#include +#include +#include"yolov5_detect.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define MSS_TEST_BUS_NAME "yolov5test" +static jbus_hdl_t g_jbus; +static jmss_hdl_t g_jmss; +static jisp_hdl_t g_jisp; +static bool g_running; +jmss_stm_t *g_stm[5]; + + +//配置文件路径 +#define modelpath_fire "/demo/bin/fire2025_pre.rknn" +#define DOWNLOAD_VERSION_PATH "/tmp/version" +#define ID_FILE_PWD "/etc/DeviceID" +std::string DeviceID; +std::string response_controllerId = "NULL"; + +static char labels_fire[2][20] = {"fire", "any"}; +static char labels_smog[2][20] = {"smog", "any"}; + +#define SERIAL_PORT_INFRARED_SENSOR "/dev/ttyS4" +#define SERIAL_PORT_SOLENOID "/dev/ttyS5" +#define BAUD_RATE B115200 +static int serialPortInfraredSensor; +static int serialPortSolenoid; +float temperature_img[24][32]; +unsigned char buffer[1544]; +std::mutex mtx; + +using namespace cv; +using namespace std; +using json = nlohmann::json; //使用nlohmann JSON 库 + + +// 定义GPIO控制引脚(假设GPIO2_B0已经设置好) +#define RELAY_GPIO_PIN 72 // 使用GPIO 72控制继电器 + +// 控制继电器复位的函数 +void reset_relay() { + // 导出GPIO引脚 + system("echo 72 > /sys/class/gpio/export"); + + // 设置GPIO引脚为输出 + system("echo out > /sys/class/gpio/gpio72/direction"); + + // 复位继电器 + system("echo 1 > /sys/class/gpio/gpio72/value"); // 设置高电平 + usleep(1000000); // 等待1秒 + system("echo 0 > /sys/class/gpio/gpio72/value"); // 设置低电平 + std::cout << "Relay reset!" << std::endl; +} + +constexpr auto CONFIG_FILE = "/demo/bin/jh.json"; +constexpr auto ID_FILE_PATH = "/etc/DeviceID"; +constexpr auto LICENSE_FILE = "/demo/bin/activation.lic"; +constexpr auto ENCRYPT_KEY = "YourSecretKey1234567890ABCDEF"; // 24字节密钥 + +// RSA公钥(PEM格式) +const string PUBLIC_KEY = R"( +-----BEGIN PUBLIC KEY----- +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK+UGGqE+K12kd5F5AH3GQNMSKZjq0l+IBTzF28YWTz+CWtRQ78eKOp3/fXZ8UBRQc2/nRT9Us24l9kPGJ2qUYECAwEAAQ== +-----END PUBLIC KEY----- +)"; + +// 全局变量 +mutex activation_mutex; + +class Config { + public: + static string getActivationUrl() { // 静态成员函数,用于获取激活 URL + try { + ifstream file(CONFIG_FILE); + if (!file) { + throw runtime_error("Config file not found"); + } + + json config = json::parse(file); // 使用 nlohmann JSON 库解析配置文件 + if (config.contains("activation_url")) { + string url = config["activation_url"]; + cout << "[INFO] Using activation URL from JSON config: " << url << endl; + return url; + } + throw runtime_error("activation_url not found in config"); + + } catch (const exception& e) { + cerr << "[WARN] " << e.what() << " - Using default URL" << endl; //捕获异常 + return "http://183.238.1.242:8889/api/label/security"; + } + } + }; + + class CryptoUtil { + public: + + // 异或加密(保留原有逻辑) + static string xorEncrypt(const string& plaintext) { + string encrypted = xorCipher(plaintext, ENCRYPT_KEY); + return base64Encode(encrypted); + } + + static string xorDecrypt(const string& ciphertext) { + string decoded = base64Decode(ciphertext); + return xorCipher(decoded, ENCRYPT_KEY); + } + + // URL编码 + static string urlEncode(const string& value) { + ostringstream escaped; + escaped.fill('0'); + escaped << hex; + for (char c : value) { + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + escaped << c; + } else { + escaped << '%' << setw(2) << int((unsigned char)c); + } + } + return escaped.str(); + } + + // 新增RSA加密 + static string rsaEncrypt(const string& plaintext) { + RSA* rsa = nullptr; + BIO* bio = nullptr; + string result; + + try { + bio = BIO_new_mem_buf(PUBLIC_KEY.c_str(), -1); + // rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr); + rsa = PEM_read_bio_RSA_PUBKEY(bio, nullptr, nullptr, nullptr); + if (!rsa) throw runtime_error("PEM_read_bio_RSA_PUBKEY failed"); + + int keySize = RSA_size(rsa); + vector encrypted(keySize); + + int len = RSA_public_encrypt( + plaintext.size(), + reinterpret_cast(plaintext.c_str()), + encrypted.data(), + rsa, + RSA_PKCS1_PADDING + ); + + if (len == -1) throw runtime_error("RSA encryption failed"); + + result = base64Encode(encrypted.data(), len); + + } catch (const exception& e) { + cerr << "[ERROR] RSA encryption failed: " << e.what() << endl; + ERR_print_errors_fp(stderr); + result.clear(); + } + + if (rsa) RSA_free(rsa); + if (bio) BIO_free_all(bio); + return result; + } + + private: + static string xorCipher(const string& data, const string& key) { + string result; + for (size_t i = 0; i < data.size(); ++i) { + result += data[i] ^ key[i % key.size()]; + } + return result; + } + + static string base64Encode(const string& data) { + BIO *bio, *b64; + BUF_MEM *bufferPtr; + + b64 = BIO_new(BIO_f_base64()); + bio = BIO_new(BIO_s_mem()); + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + BIO_write(bio, data.c_str(), data.size()); + BIO_flush(bio); + BIO_get_mem_ptr(bio, &bufferPtr); + BIO_set_close(bio, BIO_NOCLOSE); + BIO_free_all(bio); + + return string(bufferPtr->data, bufferPtr->length); + } + + static string base64Encode(const unsigned char* data, size_t len) { + BIO* b64 = BIO_new(BIO_f_base64()); + BIO* mem = BIO_new(BIO_s_mem()); + BIO_push(b64, mem); + + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_write(b64, data, len); + BIO_flush(b64); + + char* ptr; + long size = BIO_get_mem_data(mem, &ptr); + string result(ptr, size); + + BIO_free_all(b64); + return result; + } + + static string base64Decode(const string& encoded) { + BIO *bio, *b64; + char* buffer = (char*)malloc(encoded.size()); + memset(buffer, 0, encoded.size()); + + bio = BIO_new_mem_buf(encoded.c_str(), -1); + b64 = BIO_new(BIO_f_base64()); + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); + int len = BIO_read(bio, buffer, encoded.size()); + BIO_free_all(bio); + + string result(buffer, len); + free(buffer); + return result; + } + }; + + class NetworkUtil { + public: + static int postRequest(const string& url, const string& postData, string& response) { + CURL* curl = curl_easy_init(); + if (!curl) return CURLE_FAILED_INIT; + + // 使用RSA加密数据 + string encryptedData = CryptoUtil::rsaEncrypt(postData); + string urlEncodedData = CryptoUtil::urlEncode(encryptedData); + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, "Accept: application/json"); // 修改Content-Type + + const string full_url = url + "?id=" + urlEncodedData; + // cout << "[DEBUG] full_url: " << full_url << endl; + + curl_easy_setopt(curl, CURLOPT_URL, full_url.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); + + CURLcode res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + curl_slist_free_all(headers); + return res; + } + + private: + static size_t writeCallback(void* contents, size_t size, size_t nmemb, string* output) { + output->append(static_cast(contents), size * nmemb); + return size * nmemb; + } + }; + + class ActivationManager { + public: + explicit ActivationManager(string device_id, string mac) + : device_id_(move(device_id)), mac_(move(mac)) {} + + bool checkActivation() { + lock_guard lock(activation_mutex); + + if (checkLocalLicense()) { + cout << "[INFO] Device already activated" << endl; + return true; + } + return processOnlineActivation(); + } + + + // 内部函数实现:checkLocalLicense, validateLicense, processOnlineActivation, generateLicenseFile + private: + string device_id_; + string mac_; + + bool checkLocalLicense() { + ifstream file(LICENSE_FILE, ios::binary); + if (!file) return false; + + string encrypted((istreambuf_iterator(file)), istreambuf_iterator()); + return validateLicense(encrypted); + } + + bool validateLicense(const string& encrypted) { + // 修改为使用xorDecrypt + string decrypted = CryptoUtil::xorDecrypt(encrypted); + auto pos = decrypted.find('|'); + if (pos == string::npos) return false; + + string storedId = decrypted.substr(0, pos); + string storedMac = decrypted.substr(pos + 1); + + return (storedId == device_id_ && storedMac == mac_); + } + + bool processOnlineActivation(int retry_count = 3) { + string activation_url = Config::getActivationUrl(); + const string post_data = device_id_ + "|" + mac_; + cout << "[DEBUG] Original POST data: " << post_data << endl; + + for (int i = 0; i < retry_count; ++i) { + string response; + int curl_code = NetworkUtil::postRequest(activation_url, post_data, response); + + if (curl_code != CURLE_OK) { + cerr << "[ERROR] Network error: " << curl_easy_strerror((CURLcode)curl_code) << endl; + continue; + } + + try { + auto json_resp = json::parse(response); + int code = json_resp["code"].get(); + + if (code == 0) { + generateLicenseFile(device_id_ + "|" + mac_); + return true; + } + + if (code == 60001 || code == 60002) { + cerr << "[FATAL] Activation failed: " << json_resp["msg"].get() << endl; + return false; + } + + if (code == 60003 && i < retry_count - 1) { + cout << "[WARN] Retrying... (" << i+1 << "/" << retry_count << ")" << endl; + this_thread::sleep_for(chrono::seconds(1)); + continue; + } + return false; + + } catch (const json::exception& e) { + cerr << "[ERROR] JSON parse error: " << e.what() << endl; + } + } + return false; + } + + void generateLicenseFile(const string& data) { + ofstream file(LICENSE_FILE, ios::binary); + // 修改为使用xorEncrypt + string encrypted = CryptoUtil::xorEncrypt(data); + file.write(encrypted.data(), encrypted.size()); + cout << "[INFO] License file created successfully" << endl; + } + }; + + string getDeviceID() { + ifstream file(ID_FILE_PATH); + string id; + if (!getline(file, id)) throw runtime_error("Failed to read device ID"); + return id; + } + + string getMACAddress() { + const vector interfaces = {"eth0", "eth1", "enp0s3", "wlan0"}; + + for (const auto& iface : interfaces) { + ifstream file("/sys/class/net/" + iface + "/address"); + if (file) { + string mac; + getline(file, mac); + mac.erase(remove(mac.begin(), mac.end(), '\n'), mac.end()); + + if (mac.length() == 17 && count(mac.begin(), mac.end(), ':') == 5) { + return mac; + } + } + } + throw runtime_error("No valid MAC address found"); + } + + +int ALARM_TEMPERATURE; +int WARN_TEMPERATURE; +int MOVE_THRESHOLD; +int IGNORE_TEMPERATURE; +double Confidence_Threshold; + +vector> last_result; +auto last_result_time = std::chrono::high_resolution_clock::now();// 时间 +vector> now_result; + +int width = 1920, height = 1080; +JMediaRawFrameType_e type = JMEDIA_RAWFRAMETYPE_NV12; + +//查看是否重合 +bool check_whether_in_last_result(vector> &last_result, vector &now, std::ofstream& temperature_log){ + if(last_result.empty()) return true; + //查找now的框坐标是否有出现在上一帧 + for(auto i : last_result){ + //范围5像素以内都视为不变 + if(abs(i[0] - now[0]) <= MOVE_THRESHOLD && abs(i[1] - now[1]) <= MOVE_THRESHOLD && abs(i[2] - now[2]) <= MOVE_THRESHOLD && abs(i[3] - now[3]) <= MOVE_THRESHOLD){ + temperature_log << "有重复 此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" + << " 上帧结果:(" << i[0] << " " << i[1] << ") (" << i[2] << " " << i[3] << ")"<< endl; + cout << "上帧结果:(" << i[0] << " " << i[1] << ") (" << i[2] << " " << i[3] << ")" << endl; + cout << "此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" << endl; + return true; + } + } + cout << "无重复 此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" << endl; + temperature_log << "无重复 此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" << endl; + return false; +} + + +//获取当前系统时间 +string Get_Time(int input){ + // 获取当前系统时间 + std::time_t alarmTime_std = std::time(nullptr); + // 将时间转换为本地时间 + std::tm* localTime = std::localtime(&alarmTime_std); + // 从本地时间结构中获取时间信息 + int year = localTime->tm_year + 1900; + int month = localTime->tm_mon + 1; + int day = localTime->tm_mday; + int hour = localTime->tm_hour; + int minute = localTime->tm_min; + int second = localTime->tm_sec; + // 拼接成字符串 + std::ostringstream oss_alarmTime; + if(input == 1){ + oss_alarmTime << std::setfill('0') + << std::setw(4) << year << "-" + << std::setw(2) << month << "-" + << std::setw(2) << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << minute << ":" + << std::setw(2) << second; + }else{ + oss_alarmTime << std::setfill('0') + << std::setw(2) << month << "_" + << std::setw(2) << day << "_" + << std::setw(2) << hour << "_" + << std::setw(2) << minute << "_" + << std::setw(2) << second; + } + // 获取拼接后的字符串 + std::string formattedTime = oss_alarmTime.str(); + return formattedTime; +} + + + +double calibration(double x){ + double p1 = -9.052e-08; + double p2 = 8.313e-05; + double p3 = -0.02813; + double p4 = 4.16; + double p5 = -219.7; + double res = p1*pow(x, 4) + p2*pow(x, 3) + p3*pow(x, 2) + p4*x + p5; + return res; +} + + +size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* response){ + size_t totalSize = size * nmemb; + response->append(static_cast(contents), totalSize); + return totalSize; +} + + +bool _geturlFromfile(const char * filepath,char *url, int maxLength) { + if (filepath == NULL) { + printf("getrulVersion is error, url == null.\n"); + return false; + } + FILE *fp = fopen(filepath, "r"); + if (fp == NULL) { + printf("open %s failed, error is %s.\n", filepath, strerror(errno)); + return false; + } + int iffind=0; + char *line = NULL; + size_t len = 0; + size_t read; + while ((read = getline(&line, &len, fp)) != -1) { + if (read == 0 || line[0] == '#') { + continue; + } + char *pline = strstr(line, "upload_url"); + if (pline != NULL && (pline = strstr(pline, "=")) != NULL) { + pline++; //过滤掉等于号 + //过滤掉空格 + while (*pline == ' ') { + pline++; + } + int pline_len = strlen(pline) - 1; + int version_len = (pline_len > maxLength ? maxLength:pline_len); + memcpy(url, pline, version_len); + printf("upload_url = %s\n", url); + iffind=1; + break; + } + } + if (iffind == 0 ){ + printf("Can not find upload_url\n"); + return false; + } + free(line); + fclose(fp); + return true; +} + + +// 函数用于读取文件并将其内容保存在全局字符串变量中 +void readFileAndStoreInGlobal(const std::string& filename) { + std::ifstream file(filename); + if (file.is_open()) { + std::getline(file, DeviceID); // 读取一行并存储在全局字符串中 + file.close(); + } else { + std::cerr << "无法打开文件: " << filename << std::endl; + } +} + + +std::string generateIndices(char result[4],Alarm* Alarm) { + std::string indices; + for (int i = 0; i < 4; ++i) { + if (result[i] == '1') { + // 将索引值加入到生成的字符串中 + indices += std::to_string(i + 1); + Alarm->ifalarm = 1; + } + } + return indices; +} + + +void checkCoverage(int x1, int x2, int width, char result[4]) { + // 初始化为 '1',表示覆盖 + // std::string resultString1(result, 4); + // std::cout << "result in: " << resultString1 << std::endl; + char result_now[4]={'1','1','1','1'}; + int line1 = width*7/32; + int line2 = width/2; + int line3 = width*25/32; + if(x1>line1){ + result_now[3] = '0'; + } + if(x1>line2){ + result_now[3] = '0'; + result_now[2] = '0'; + } + if(x1>line3){ + result_now[3] = '0'; + result_now[2] = '0'; + result_now[1] = '0'; + } + + if(x2& data) { + uint16_t sum = 0; + for (size_t i = 0; i < data.size()-1; ++i) { + sum += data[i]; + } + return static_cast(sum & 0xFF); +} + + +void printVector(const std::vector& vec) { + std::cout << "Vector contents: "; + for (const auto& byte : vec) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte) << " "; + } + std::cout << std::dec << std::endl; // 切换回十进制 +} + + +int main(int argc, char **argv) +{ + std::string folderPath = "/demo/pic"; //保存图片的文件夹路径 + if(!createDirectory(folderPath)){ + return 0; + } + + // 打开 JSON 文件 + cout << "解析JSON文件" << endl; + std::ifstream file("/demo/bin/config.json"); + + // 检查文件是否成功打开 + if (!file.is_open()) { + std::cerr << "Failed to open json file" << std::endl; + return 1; + } + + try { + // 解析 JSON 文件 + json Config_jsonData; + file >> Config_jsonData; + // 关闭文件 + + // 赋值给全局常量 + WARN_TEMPERATURE = Config_jsonData["WARN_TEMPERATURE"]; + ALARM_TEMPERATURE = Config_jsonData["ALARM_TEMPERATURE"]; + MOVE_THRESHOLD = Config_jsonData["MOVE_THRESHOLD"]; + IGNORE_TEMPERATURE = Config_jsonData["IGNORE_TEMPERATURE"]; + Confidence_Threshold = Config_jsonData["Confidence_Threshold"]; + } + catch (const json::parse_error& e) + { + std::cerr << "JSON parsing error: " << e.what() << std::endl; + return 0; + } + file.close(); + + cout << "配置 WARN_TEMPERATURE :" << WARN_TEMPERATURE << endl; + cout << "配置 ALARM_TEMPERATURE :" << ALARM_TEMPERATURE << endl; + cout << "配置 MOVE_THRESHOLD :" << MOVE_THRESHOLD << endl; + cout << "配置 IGNORE_TEMPERATURE :" << IGNORE_TEMPERATURE << endl; + cout << "配置 Confidence_Threshold :" << Confidence_Threshold << endl; + cout << SERIAL_PORT_INFRARED_SENSOR <=2?(JMediaRawFrameType_e)atoi(argv[1]):JMEDIA_RAWFRAMETYPE_NV12; + int width = 1920, height = 1080; + jmss_raw_t *rawchn = JES_MSS_RawOpen(g_jmss, 0, width, height, type); + if(rawchn == NULL){ + printf("==============>>>: %s:%d open rawchn failed\n", strrchr(__FILE__,'/'),__LINE__); + return -1; + } + int ret; + + + readFileAndStoreInGlobal((char *)ID_FILE_PWD); + // 打印全局字符串变量 + std::cout << "ID内容:" << DeviceID < ControlInstructions(20,0x00); + ControlInstructions[0] = 0xCA; //协议头 + ControlInstructions[1] = 0x14; //总长度 + ControlInstructions[2] = 0x01; //操作指令 + + //打开JES的通道 + jmss_raw_t *rawchn = JES_MSS_RawOpen(g_jmss, 0, width, height, type); + if(rawchn == NULL){ + printf("==============>>>: %s:%d open rawchn failed\n", strrchr(__FILE__,'/'),__LINE__); + } + //日志相关内容 + std::string filename = "/demo/bin/temperature_log.txt"; + std::ofstream temperature_log(filename, std::ios::out | std::ios::trunc); + temperature_log << "配置 WARN_TEMPERATURE :" << WARN_TEMPERATURE << endl; + temperature_log << "配置 ALARM_TEMPERATURE :" << ALARM_TEMPERATURE << endl; + temperature_log << "配置 MOVE_THRESHOLD :" << MOVE_THRESHOLD << endl; + temperature_log << "配置 IGNORE_TEMPERATURE :" << IGNORE_TEMPERATURE << endl; + temperature_log << "配置 Confidence_Threshold :" << Confidence_Threshold << endl; + //环境温度 + double temp_env = 0; + + auto last_time = std::chrono::high_resolution_clock::now();// 记录开始时间 + // 创建一个 Alarm 结构体对象 + Alarm Alarm; + // 调用初始化函数来初始化结构体成员 + initializeAlarm(&Alarm); + //用于从全局buffer中复制数据 + unsigned char copyBuffer[1544]; + + /* 参数初始化 */ + int output_nboxes_left =0; + yolov5_detect_result_group_t detect_result_group; + + /* 算法模型初始化 */ + char *path = reinterpret_cast(args); + printf("loading model %s\n",path); + rknn_context ctx; + yolov5_detect_init(&ctx, path); + // yolov5_detect_init(&ctx, "./fire_rv1126.rknn"); + + //配置label + char labels[2][20]; + getLabels(path, labels); + // 设置压缩质量 + std::vector compression_params; + compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); + compression_params.push_back(30); // 设置压缩质量,范围为 0-100 + int overtem_cnt = 0; + std::vector over_tmp_deque(10, 0); + /* 算法运行 */ + while(!quit){ + int time2run = 1; + // auto current_time = std::chrono::high_resolution_clock::now(); + // auto elapsed_time = std::chrono::duration_cast(current_time - last_time);// 计算距离开始时间的时间差 + + // if (elapsed_time.count() - 1000 > 0) { + // last_time = current_time; + // time2run = 1; + // } + sleep(2); + //获取摄像头buffer + JVMediaFrame_t frm; + if(JES_MSS_RawGetFrame(rawchn,&frm) < 0){ + sleep(1); + continue; + } + int overtmp_falg = 0; + Alarm.ifalarm = 0; + Alarm.ifwarn = 0; + + Mat src; + NV12ToRGB(width, height, frm.buffer, src); + //裁剪 + cv::Rect roi(220, 130, 1480, 570); + src = src(roi); + //固定时间间隔去处理红外参数 + char result[4]={'0','0','0','0'}; + char result_warn[4]={'0','0','0','0'}; + char result_fire_rknn[4]={'0','0','0','0'}; + //初始化ControlInstructions + ControlInstructions[3]=0x00; + ControlInstructions[4]=0x00; + ControlInstructions[5]=0x00; + ControlInstructions[6]=0x00; + + if(time2run){ + cout << "run ." << endl; + mtx.lock(); + memcpy(copyBuffer, buffer, sizeof(buffer)); + mtx.unlock(); + //计算温度 + double temperature; + int Col,Row; + int j = 0; + double max_temperature = 0; + for (int i = 4; i < 1540; i += 2) { + // temperature = calibration((buffer[i+1]*256+buffer[i])/100.0); + temperature = (buffer[i+1]*256+buffer[i])/100.0 + 6; + // cout << (buffer[i+1]*256+buffer[i])/100.0 << " " << temperature << endl; + + //按视觉顺序存入数组 + Col = 31-(j)%32; + Row = (j)/32; + temperature_img[Row][Col] = temperature; + j++; + } + temp_env = (buffer[1541]*256+buffer[1540])/100.0; + WARN_TEMPERATURE = (temp_env*2)>WARN_TEMPERATURE? (temp_env*2):WARN_TEMPERATURE; + // ALARM_TEMPERATURE = WARN_TEMPERATURE + 20; + //截去上下5行数据 + for(int i=5; i<19; i++){ + for(j=0; j<32; j++){ + if(temperature_img[i][j] > IGNORE_TEMPERATURE) continue; + if(temperature_img[i][j] > max_temperature) max_temperature = temperature_img[i][j]; + + if(temperature_img[i][j] > ALARM_TEMPERATURE){ + overtmp_falg = 1; + if (0 <= j && j <= 6) + result[0] = '1'; + else if (6 < j && j <= 15) + result[1] = '1'; + else if (15 < j && j <= 24) + result[2] = '1'; + else if (24 < j && j <= 31) + result[3] = '1'; + } + + if(temperature_img[i][j] > WARN_TEMPERATURE){ + Alarm.ifwarn = 1; + if (0 < j && j <= 6) + result_warn[0] = '1'; + else if (6 < j && j <= 15) + result_warn[1] = '1'; + else if (13 < j && j <= 24) + result_warn[2] = '1'; + else if (24 < j && j <= 31) + result_warn[3] = '1'; + } + } + } + + int overtem_cnt_for_log; + if(overtmp_falg){ + over_tmp_deque.push_back(1); + if (over_tmp_deque.size() > 10) { + over_tmp_deque.erase(over_tmp_deque.begin()); + } + overtem_cnt = 0; + for (int i = 0; i < over_tmp_deque.size(); ++i) { + if (over_tmp_deque[i] == 1) { + overtem_cnt++; + } + } + overtem_cnt_for_log = overtem_cnt; + + if(overtem_cnt >= 3){ + Alarm.ifalarm = 1; + over_tmp_deque.assign(9, 0); + over_tmp_deque.push_back(1); + }else{ + memset(result, '0', 4*sizeof(char)); + } + }else{ + over_tmp_deque.push_back(0); + // 保持队列长度为10,如果超过长度则移除最前面的元素 + if (over_tmp_deque.size() > 10) { + over_tmp_deque.erase(over_tmp_deque.begin()); + } + } + + + // temp_env = (buffer[1541]*256+buffer[1540])/100.0; + cout << "最大温度: " << max_temperature << endl; + cout << "环境温度" << temp_env << endl; + cout << "预警温度: " << WARN_TEMPERATURE << endl; + cout << "直接喷水温度: " << ALARM_TEMPERATURE << endl; + if(max_temperature>WARN_TEMPERATURE){ + temperature_log << endl << "---------------------------------------------------" << endl; + temperature_log << Get_Time(1) << endl; + temperature_log << "最大温度: " << max_temperature << endl; + temperature_log << "环境温度: " << temp_env << endl; + temperature_log << "预警温度: " << WARN_TEMPERATURE << endl; + temperature_log << "直接喷水温度: " << ALARM_TEMPERATURE << endl; + temperature_log << "超温队列:"; + for (int i = 0; i < over_tmp_deque.size(); ++i) { + temperature_log << over_tmp_deque[i] << " "; + } + temperature_log << endl; + } + if(Alarm.ifalarm){ + printf("temprature > %d°C !\n", ALARM_TEMPERATURE); + // 将字符数组拼接成字符串 + std::string resultString(result, 4); + std::cout << ALARM_TEMPERATURE <<"度结果: " << resultString << std::endl; + temperature_log << ALARM_TEMPERATURE <<"度结果: " << resultString << std::endl; + std::string resultString_warn(result_warn, 4); + std::cout << WARN_TEMPERATURE << "度结果: " << resultString_warn << std::endl; + temperature_log << WARN_TEMPERATURE <<"度结果: " << resultString_warn << std::endl; + }else if(Alarm.ifwarn){ + printf("temprature > %d°C !\n", WARN_TEMPERATURE); + std::string resultString_warn(result_warn, 4); + std::cout << WARN_TEMPERATURE << "结果: " << resultString_warn << std::endl; + temperature_log << WARN_TEMPERATURE << "结果: " << resultString_warn << std::endl; + } + } + + + if(Alarm.ifwarn){ + struct timeval start; + struct timeval end; + now_result.clear(); + temperature_log << "上帧总的结果:" << endl; + for(auto i : last_result){ + temperature_log << "(" << i[0] << " " << i[1] << ") (" << i[2] << " " << i[3] << ")" << endl; + } + printf("%s_rknn_run\n",labels[0]); + yolov5_detect_run(ctx, src, &detect_result_group); + } + + //计算时间差 + if( detect_result_group.count > 0 ){ + auto current_time_now = std::chrono::high_resolution_clock::now();// 时间 + auto value_time = std::chrono::duration_cast(current_time_now - last_result_time);// 计算距离开始时间的时间差 + + if (value_time.count() - 10000 > 0) { //如果上下帧大于十秒,则清除上一帧 + last_result.clear(); + } + } + + + + /* 算法结果在图像中画出并保存 */ + int name_int = 0; + // char result[4]={'0','0','0','0'}; + for (int i = 0; i < detect_result_group.count; i++) + { + yolov5_detect_result_t *det_result = &(detect_result_group.results[i]); + + if( det_result->prop < Confidence_Threshold ) + { + continue; + } + + + cout << "Detected object: " << det_result->name << endl; + + + //报警打印 + if(Alarm.ifwarn){ + printf("%s @ (%d %d %d %d) %f\n", + det_result->name, + det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom, + det_result->prop); + vector now; + now.push_back(det_result->box.left); + now.push_back(det_result->box.top); + now.push_back(det_result->box.right); + now.push_back(det_result->box.bottom); + now_result.push_back(now); + if(check_whether_in_last_result(last_result,now,temperature_log)){ + continue; + } + } + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + + //检测区域 + if(Alarm.ifwarn){ + checkCoverage(x1, x2, 1480, result_fire_rknn); + } + + char label_text[50]; + memset(label_text, 0 , sizeof(label_text)); + sprintf(label_text, "%s %0.2f",det_result->name, det_result->prop); + plot_one_box(src, x1, x2, y1, y2, label_text, i%10); + } + + if(Alarm.ifwarn){ + // if(!now_result.empty()){ + // last_result.clear(); + // last_result = now_result; + // } + + //若上帧结果为空,依然清理结果队列 --2024.11.4 + last_result.clear(); + last_result = now_result; + last_result_time = std::chrono::high_resolution_clock::now();// 时间 + std::string resultString_fire(result_fire_rknn, 4); + std::cout << "rknn检测结果: " << resultString_fire << std::endl; + temperature_log << "rknn检测结果: " << resultString_fire << std::endl; + //对45度区域和火焰检测区域做出 与操作 + and_result(result_warn,result_fire_rknn); + //对火与45度结果 与 60 度结果 做或操作 + or_result(result,result_warn); + temperature_log << "报警输入:" << string(result, 4) <(orig_data.c_str()), orig_data.length()); + Alarm.alarmBase64 = encoded_data; + Alarm.alarmMsg = std::string(labels[0]); + Alarm.alarmMsg = std::string(labels[name_int]); + pthread_t upload_message_tidp; + pthread_create(&upload_message_tidp, NULL, upload_message, static_cast(&Alarm)); + pthread_t upload_message_controller_tidp; + int ret =pthread_create(&upload_message_controller_tidp, NULL, upload_message_controller, static_cast(&Alarm)); + if (ret != 0) { + std::cerr << "Error creating controller thread: " << strerror(ret) << std::endl; + } else { + std::cerr << "success creating controller thread" << std::endl; + } + } + } + + /* 算法模型空间释放 */ + yolov5_detect_release(ctx); + //文件关闭 + temperature_log.close(); + return 0; + +} + +void *upload_message(void *args) +{ + pthread_detach(pthread_self()); + // 获取上报url + char upload_url[200] = {0}; + if(!_geturlFromfile(DOWNLOAD_VERSION_PATH,upload_url,sizeof(upload_url))){ + printf("结束进程\n"); + return 0; + } + + Alarm* alarm = static_cast(args); + + // 获取当前系统时间 + std::time_t alarmTime_std = std::time(nullptr); + + // 将时间转换为本地时间 + std::tm* localTime = std::localtime(&alarmTime_std); + + // 从本地时间结构中获取时间信息 + int year = localTime->tm_year + 1900; + int month = localTime->tm_mon + 1; + int day = localTime->tm_mday; + int hour = localTime->tm_hour; + int minute = localTime->tm_min; + int second = localTime->tm_sec; + + // 拼接成字符串 + std::ostringstream oss_alarmTime; + oss_alarmTime << std::setfill('0') + << std::setw(4) << year << "-" + << std::setw(2) << month << "-" + << std::setw(2) << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << minute << ":" + << std::setw(2) << second; + + // 获取拼接后的字符串 + std::string formattedTime = oss_alarmTime.str(); + + std::string cameraId = DeviceID; + std::string controllerId = response_controllerId; + std::string msg = (alarm->alarmMsg).c_str(); + std::string type = "1"; + std::string time = formattedTime; + std::string img = (alarm->alarmBase64).c_str(); + std::string Coverage = (alarm->alarmCoverage).c_str(); + + std::cout << "cameraId: " << cameraId << " msg: " << msg << " type: " << type << " time: " << time <(args); + + // 获取当前系统时间 + std::time_t alarmTime_std = std::time(nullptr); + + // 将时间转换为本地时间 + std::tm* localTime = std::localtime(&alarmTime_std); + + // 从本地时间结构中获取时间信息 + int year = localTime->tm_year + 1900; + int month = localTime->tm_mon + 1; + int day = localTime->tm_mday; + int hour = localTime->tm_hour; + int minute = localTime->tm_min; + int second = localTime->tm_sec; + + // 拼接成字符串 + std::ostringstream oss_alarmTime; + oss_alarmTime << std::setfill('0') + << std::setw(4) << year << "-" + << std::setw(2) << month << "-" + << std::setw(2) << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << minute << ":" + << std::setw(2) << second; + + // 获取拼接后的字符串 + std::string formattedTime = oss_alarmTime.str(); + + std::string cameraId = DeviceID; + std::string Coverage = (alarm->alarmCoverage).c_str(); + + std::cout << "cameraid: " << cameraId << " errNum: " << Coverage <(current_time - last_time);// 计算距离开始时间的时间差 + + if (elapsed_time.count() - 30000 > 0) { + last_time = current_time; + time2run = 1; + } + + if(time2run){ + std::cout << "心跳上报" << std::endl; + std::string cameraId = DeviceID; + std::string IP = "987654"; + int state = 1; + std::string http_address = "http"; + + std::string MessageString = +R"({ + "cameraId": ")" + cameraId + R"(", + "IP": ")" + IP + R"(", + "state": )" + std::to_string(state) + R"(, + "http_address": ")" + http_address + R"(" +})"; + CURL *curl; + CURLcode res; + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + + if(curl) + { + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.23:9527/device/heartbeat"); + /* Now specify the POST data */ + struct curl_slist *plist = nullptr; + plist = curl_slist_append(plist, "Content-Type:application/json;charset=UTF-8"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, plist); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, MessageString.c_str()); + + std::string response; + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + + res = curl_easy_perform(curl); + /* Check for errors */ + // if(res != CURLE_OK) + // fprintf(stderr, "curl_easy_perform() failed: %s\n",curl_easy_strerror(res)); + if (res != CURLE_OK) { + std::cerr << "heart beat: Failed to perform cURL request: " << curl_easy_strerror(res) << std::endl; + } else { + std::cout << "heart beat: Request successful!" << std::endl; + std::cout << "heart beat: Response: " << response << std::endl; + // 解析 JSON 字符串 + try { + json jsonData = json::parse(response); + + // 提取 controllerId 字段的内容 + response_controllerId = jsonData["controllerId"]; + + std::cout << "Controller ID: " << response_controllerId << std::endl; + } catch (const json::parse_error& e) { + std::cerr << "JSON parsing error: " << e.what() << std::endl; + } + } + + + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + } + time2run = 0; + } +} + + +void *read_serial_thread(void *args) +{ + pthread_detach(pthread_self()); + ssize_t bytesRead; + + // 打开红外传感器对应的串口设备 + serialPortInfraredSensor = open(SERIAL_PORT_INFRARED_SENSOR, O_RDWR | O_NOCTTY | O_NDELAY); + if (serialPortInfraredSensor == -1) { + printf("Failed to open serial port: %s\n", SERIAL_PORT_INFRARED_SENSOR); + return 0; + } + + struct termios tty; + memset(&tty, 0, sizeof(tty)); // 初始化配置结构体,清零 + if (tcgetattr(serialPortInfraredSensor, &tty) != 0) { + printf("Failed to get serial port attributes\n"); + close(serialPortInfraredSensor); + return 0; + } + // 设置串口输入和输出波特率 + cfsetospeed(&tty, BAUD_RATE); + cfsetispeed(&tty, BAUD_RATE); + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; + tty.c_cflag &= ~(PARENB | PARODD); + tty.c_cflag &= ~CSTOPB; + tty.c_cflag |= CREAD | CLOCAL; + tty.c_iflag = IGNPAR; + tty.c_oflag = 0; + tty.c_lflag = 0; + if (tcsetattr(serialPortInfraredSensor, TCSANOW, &tty) != 0) { + printf("Failed to set serial port attributes\n"); + close(serialPortInfraredSensor); + return 0; + } + + // while(1){ + // mtx.lock(); + // bytesRead = read(serialPortInfraredSensor, buffer, sizeof(buffer)); + // mtx.unlock(); + // if (bytesRead>0) { + // if((buffer[0]== 0x5a)&&(buffer[1]==0x5a)) { + // // printf("readed serialPortInfraredSensor date\n"); + // }else{ + // // printf("read failed\n"); + // } + // }else{ + // // printf("empty to read\n"); + // } + // } + + // 设置文件描述符集合和超时时间,准备使用 select() 进行串口数据的检测 + fd_set readfds; + struct timeval timeout; + int selectResult; + // 此处后续可以继续添加使用 select() 监听串口数据的逻辑… + + while (1) { + FD_ZERO(&readfds); + FD_SET(serialPortInfraredSensor, &readfds); + + timeout.tv_sec = 5; // 5秒超时 + timeout.tv_usec = 0; + + selectResult = select(serialPortInfraredSensor + 1, &readfds, NULL, NULL, &timeout); + + if (selectResult > 0) { + if (FD_ISSET(serialPortInfraredSensor, &readfds)) { + mtx.lock(); + bytesRead = read(serialPortInfraredSensor, buffer, sizeof(buffer)); + mtx.unlock(); + + if (bytesRead > 0) { + if (buffer[0] == 0x5a && buffer[1] == 0x5a) { + // printf("readed serialPortInfraredSensor date\n"); + } else { + // printf("read failed\n"); + } + } + } + } else if (selectResult == 0) { + // 超时,没有数据可读 + // printf("Timeout, no data available\n"); + } else { + // select出错 + printf("select() failed\n"); + break; + } + } +} \ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..dd91d70 --- /dev/null +++ b/src/main.h @@ -0,0 +1,95 @@ +#ifndef _ATK_YOLOV5_OBJECT_RECOGNIZE_H +#define _ATK_YOLOV5_OBJECT_RECOGNIZE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _BASETSD_H + +#include "im2d.h" +#include "rga.h" +#include "drm_func.h" +#include "rga_func.h" +#include "rknn_api.h" +#include "rkmedia_api.h" +#include "rkmedia_venc.h" +#include "sample_common.h" +#include "opencv2/opencv.hpp" + +#include "librtsp/rtsp_demo.h" +#include "rk_aiq_user_api_sysctl.h" +#include "rk_aiq_user_api_afec.h" +// #include "rk_aiq_uapi_afec_int.h" + +#include +#include +#include "opencv2/imgcodecs.hpp" + +using namespace cv; + + + +RK_U32 video_width = 1920; +RK_U32 video_height = 1072; +// RK_U32 video_height = 720; +int disp_width = 720; +int disp_height = 1280; + +static bool quit = false; +rtsp_demo_handle g_rtsplive = NULL; +static rtsp_session_handle g_rtsp_session; + +int rgb24_resize(unsigned char *input_rgb, unsigned char *output_rgb, int width,int height, int outwidth, int outheight); + +static unsigned char *load_model(const char *filename, int *model_size); + +static void printRKNNTensor(rknn_tensor_attr *attr); + +void *rkmedia_rknn_thread(void *args); +void *venc_rtsp_tidp(void *args); +void *upload_message(void *args); //上传检测信息与截取的图片 +void *upload_message_controller(void *args); +void *heart_beat(void *args); //上传心跳检测 +void *distortion(void *args); //矫正 +void *read_serial_thread(void *args); //读取串口传来的红外温度数据 + +struct Alarm { + int ifalarm; + int ifwarn; + std::string alarmMsg; + std::string alarmType; + std::string alarmBase64; + std::string alarmCoverage; + float alarmprop; +}; + +void initializeAlarm(Alarm* Alarm) { + // 初始化结构体成员 + Alarm->ifalarm = 0; + Alarm->ifwarn = 0; + Alarm->alarmMsg = "无"; + Alarm->alarmType = "无"; + Alarm->alarmBase64 = "无"; + Alarm->alarmCoverage = "无"; + Alarm->alarmprop = 0.0; +} + +void NV12ToRGB(int width, int height, unsigned char* nv12, Mat& rgb) { + // Step 1: Create a cv::Mat object for NV12 data + Mat nv12Mat(height + height / 2, width, CV_8UC1, nv12); + + // Step 2: Convert NV12 to RGB + cvtColor(nv12Mat, rgb, COLOR_YUV2RGB_NV12); +} + +#endif \ No newline at end of file diff --git a/src/rga_func.c b/src/rga_func.c new file mode 100644 index 0000000..aaea364 --- /dev/null +++ b/src/rga_func.c @@ -0,0 +1,129 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "rga_func.h" + +int RGA_init(rga_context *rga_ctx) +{ + rga_ctx->rga_handle = dlopen("librga.so", RTLD_LAZY); + if (!rga_ctx->rga_handle) + { + printf("dlopen librga.so failed\n"); + printf("dlopen error: %s\n", dlerror()); + return -1; + } + rga_ctx->init_func = (FUNC_RGA_INIT)dlsym(rga_ctx->rga_handle, "c_RkRgaInit"); + rga_ctx->deinit_func = (FUNC_RGA_DEINIT)dlsym(rga_ctx->rga_handle, "c_RkRgaDeInit"); + rga_ctx->blit_func = (FUNC_RGA_BLIT)dlsym(rga_ctx->rga_handle, "c_RkRgaBlit"); + rga_ctx->init_func(); + return 0; +} + +void img_resize_fast(rga_context *rga_ctx, int src_fd, int src_w, int src_h, uint64_t dst_phys, int dst_w, int dst_h) +{ + // printf("rga use fd, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + + if (rga_ctx->rga_handle) + { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = src_fd; + src.mmuFlag = 1; + // src.virAddr = (void *)psrc; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 0; + +#if defined(__arm__) + dst.phyAddr = (void *)((uint32_t)dst_phys); +#else + dst.phyAddr = (void *)dst_phys; +#endif + + dst.nn.nn_flag = 0; + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h, dst_w, dst_h, RK_FORMAT_RGB_888); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret) + { + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +void img_resize_slow(rga_context *rga_ctx, void *src_virt, int src_w, int src_h, void *dst_virt, int dst_w, int dst_h, + int w_offset, int h_offset, RgaSURF_FORMAT color, bool add_extra_sz_w, bool add_extra_sz_h) +{ + // printf("rga use virtual, src(%dx%d) -> dst(%dx%d)\n", src_w, src_h, dst_w, dst_h); + if (rga_ctx->rga_handle) + { + int ret = 0; + rga_info_t src, dst; + + memset(&src, 0, sizeof(rga_info_t)); + src.fd = -1; + src.mmuFlag = 1; + src.virAddr = (void *)src_virt; + + memset(&dst, 0, sizeof(rga_info_t)); + dst.fd = -1; + dst.mmuFlag = 1; + dst.virAddr = dst_virt; + + dst.nn.nn_flag = 0; + // printf("input to rga para, w_offset: %d, h_offset: %d, dst_w: %d, dst_h: %d\n", + // w_offset, h_offset, dst_w, dst_h); + + rga_set_rect(&src.rect, 0, 0, src_w, src_h, src_w, src_h, RK_FORMAT_RGB_888); + int src_w = dst_w + 2*w_offset; + int src_h = dst_h + 2*h_offset; + if (add_extra_sz_w){ + src_w += 1; + // printf("Adding extra sz w: %d\n", src_w); + } + else if (add_extra_sz_h){ + src_h += 1; + // printf("Adding extra sz h: %d\n", src_h); + } + + //rga_set_rect(&dst.rect, w_offset, h_offset, dst_w, dst_h, src_w, dst_h + 2*h_offset, color); + rga_set_rect(&dst.rect, w_offset, h_offset, dst_w, dst_h, src_w, src_h, color); + + ret = rga_ctx->blit_func(&src, &dst, NULL); + if (ret){ + printf("c_RkRgaBlit error : %s\n", strerror(errno)); + } + + return; + } + return; +} + +int RGA_deinit(rga_context *rga_ctx) +{ + if(rga_ctx->rga_handle) + { + dlclose(rga_ctx->rga_handle); + rga_ctx->rga_handle = NULL; + } +} \ No newline at end of file diff --git a/src/yolov5_detect.cpp b/src/yolov5_detect.cpp new file mode 100644 index 0000000..f9aa70d --- /dev/null +++ b/src/yolov5_detect.cpp @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "yolov5_detect.h" +#include "rknn_api.h" + +#include + +using namespace std; +using namespace cv; + + +//unsigned char *model; +//detection* dets; + +static void printRKNNTensor(rknn_tensor_attr *attr) +{ + printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d " + "fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n", + attr->index, attr->name, attr->n_dims, attr->dims[3], attr->dims[2], + attr->dims[1], attr->dims[0], attr->n_elems, attr->size, 0, attr->type, + attr->qnt_type, attr->fl, attr->zp, attr->scale); +} + +static int letter_box(cv::Mat input_image, cv::Mat *output_image, int model_input_size) +{ + int input_width, input_height; + + input_width = input_image.cols; + input_height = input_image.rows; + float ratio; + ratio = min((float)model_input_size / input_width, (float)model_input_size / input_height); + + int new_width, new_height; + new_width = round(ratio * input_width ); + new_height = round(ratio * input_height); + + + int height_padding = 0; + int width_padding = 0; + int top = 0; + int bottom = 0; + int left = 0; + int right = 0; + if( new_width >= new_height) + { + height_padding = new_width - new_height; + if( (height_padding % 2) == 0 ) + { + top = (int)((float)(height_padding/2)); + bottom = (int)((float)(height_padding/2)); + } + else + { + top = (int)((float)(height_padding/2)); + bottom = (int)((float)(height_padding/2))+1; + } + } + else + { + width_padding = new_height - new_width; + if( (width_padding % 2) == 0 ) + { + left = (int)((float)(width_padding/2)); + right = (int)((float)(width_padding/2)); + } + else + { + left = (int)((float)(width_padding/2)); + right = (int)((float)(width_padding/2))+1; + } + + } + + cv::Mat resize_img; + + cv::resize(input_image, resize_img, cv::Size(new_width, new_height)); + cv::copyMakeBorder(resize_img, *output_image, top, bottom, left, right, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); + + return 0; +} + +int yolov5_detect_init(rknn_context *ctx, const char * path) +{ + int ret; + + // Load model + FILE *fp = fopen(path, "rb"); + if(fp == NULL) + { + printf("fopen %s fail!\n", path); + return -1; + } + fseek(fp, 0, SEEK_END); //fp指向end,fseek(FILE *stream, long offset, int fromwhere); + int model_len = ftell(fp); //相对文件首偏移 + unsigned char *model_data = (unsigned char*)malloc(model_len); + + fseek(fp, 0, SEEK_SET); //SEEK_SET为文件头 + if(model_len != fread(model_data, 1, model_len, fp)) + { + printf("fread %s fail!\n", path); + free(model_data); + return -1; + } + fclose(fp); + + //init + ret = rknn_init(ctx, model_data, model_len, RKNN_FLAG_PRIOR_MEDIUM); + if(ret < 0) + { + printf("rknn_init fail! ret=%d\n", ret); + return -1; + } + + free(model_data); + + return 0; +} + +static int scale_coords(yolov5_detect_result_group_t *detect_result_group, int img_width, int img_height, int model_size) +{ + for (int i = 0; i < detect_result_group->count; i++) + { + yolov5_detect_result_t *det_result = &(detect_result_group->results[i]); + + + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + + + if( img_width >= img_height ) + { + int image_max_len = img_width; + float gain; + gain = (float)model_size / image_max_len; + int resized_height = img_height * gain; + int height_pading = (model_size - resized_height)/2; + y1 = (y1 - height_pading); + y2 = (y2 - height_pading); + x1 = int(x1 / gain); + y1 = int(y1 / gain); + x2 = int(x2 / gain); + y2 = int(y2 / gain); + + det_result->box.left = x1; + det_result->box.top = y1; + det_result->box.right = x2; + det_result->box.bottom = y2; + } + else + { + int image_max_len = img_height; + float gain; + gain = (float)model_size / image_max_len; + int resized_width = img_width * gain; + int width_pading = (model_size - resized_width)/2; + x1 = (x1 - width_pading); + x2 = (x2 - width_pading); + x1 = int(x1 / gain); + y1 = int(y1 / gain); + x2 = int(x2 / gain); + y2 = int(y2 / gain); + + det_result->box.left = x1; + det_result->box.top = y1; + det_result->box.right = x2; + det_result->box.bottom = y2; + } + + } + + return 0; +} + + +int yolov5_detect_run(rknn_context ctx, cv::Mat input_image, yolov5_detect_result_group_t *detect_result_group) +{ + int img_width = 0; + int img_height = 0; + int img_channel = 0; + + size_t actual_size = 0; + const float vis_threshold = 0.1; + const float nms_threshold = 0.5; + const float conf_threshold = 0.2; + int ret; + + img_width = input_image.cols; + img_height = input_image.rows; + + rknn_sdk_version version; + ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, + sizeof(rknn_sdk_version)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + /* + printf("sdk version: %s driver version: %s\n", version.api_version, + version.drv_version); + */ + + rknn_input_output_num io_num; + ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + /* + printf("model input num: %d, output num: %d\n", io_num.n_input, + io_num.n_output); + */ + + rknn_tensor_attr input_attrs[io_num.n_input]; + memset(input_attrs, 0, sizeof(input_attrs)); + for (int i = 0; i < io_num.n_input; i++) + { + input_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), + sizeof(rknn_tensor_attr)); + if (ret < 0) + { + printf("rknn_init error ret=%d\n", ret); + return -1; + } + //printRKNNTensor(&(input_attrs[i])); + } + + rknn_tensor_attr output_attrs[io_num.n_output]; + memset(output_attrs, 0, sizeof(output_attrs)); + for (int i = 0; i < io_num.n_output; i++) + { + output_attrs[i].index = i; + ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), + sizeof(rknn_tensor_attr)); + //printRKNNTensor(&(output_attrs[i])); + } + + int input_channel = 3; + int input_width = 0; + int input_height = 0; + if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) + { + //printf("model is NCHW input fmt\n"); + input_width = input_attrs[0].dims[0]; + input_height = input_attrs[0].dims[1]; + } + else + { + //printf("model is NHWC input fmt\n"); + input_width = input_attrs[0].dims[1]; + input_height = input_attrs[0].dims[2]; + } + + /* + printf("model input height=%d, width=%d, channel=%d\n", height, width, + channel); + */ + + /* Init input tensor */ + rknn_input inputs[1]; + memset(inputs, 0, sizeof(inputs)); + inputs[0].index = 0; + inputs[0].type = RKNN_TENSOR_UINT8; + inputs[0].size = input_width * input_height * input_channel; + inputs[0].fmt = RKNN_TENSOR_NHWC; + inputs[0].pass_through = 0; + + /* Init output tensor */ + rknn_output outputs[io_num.n_output]; + memset(outputs, 0, sizeof(outputs)); + + for (int i = 0; i < io_num.n_output; i++) + { + outputs[i].want_float = 0; + } + + cv::Mat letter_image; + letter_box(input_image, &letter_image, input_width); + inputs[0].buf = letter_image.data; + + rknn_inputs_set(ctx, io_num.n_input, inputs); + ret = rknn_run(ctx, NULL); + ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); + + // Post process + std::vector out_scales; + std::vector out_zps; + for (int i = 0; i < io_num.n_output; ++i) + { + out_scales.push_back(output_attrs[i].scale); + out_zps.push_back(output_attrs[i].zp); + } + + + yolov5_post_process_u8((uint8_t *)outputs[0].buf, (uint8_t *)outputs[1].buf, (uint8_t *)outputs[2].buf, input_height, input_width, + conf_threshold, nms_threshold, out_zps, out_scales, detect_result_group); + + + /* + yolov5_post_process_fp((float *)outputs[0].buf, (float *)outputs[1].buf, (float *)outputs[2].buf, input_height, input_width, + conf_threshold, nms_threshold, &detect_result_group); + */ + + rknn_outputs_release(ctx, io_num.n_output, outputs); + + scale_coords(detect_result_group, img_width, img_height, input_width); + + return 0; +} + +int yolov5_detect_release(rknn_context ctx) +{ + rknn_destroy(ctx); + return 0; +} + + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + } + + return ret; +} + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} \ No newline at end of file diff --git a/src/yolov5_detect_postprocess.cpp b/src/yolov5_detect_postprocess.cpp new file mode 100644 index 0000000..e57518d --- /dev/null +++ b/src/yolov5_detect_postprocess.cpp @@ -0,0 +1,469 @@ +// Copyright (c) 2021 by Rockchip Electronics Co., Ltd. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include "yolov5_detect_postprocess.h" +#include + + +static char labels[YOLOV5_CLASS_NUM][30] = {"0", "1"}; + +const int anchor0[6] = {10, 13, 16, 30, 33, 23}; +const int anchor1[6] = {30, 61, 62, 45, 59, 119}; +const int anchor2[6] = {116, 90, 156, 198, 373, 326}; + +inline static int clamp(float val, int min, int max) +{ + return val > min ? (val < max ? val : max) : min; +} + +static float CalculateOverlap(float xmin0, float ymin0, float xmax0, float ymax0, float xmin1, float ymin1, float xmax1, float ymax1) +{ + float w = fmax(0.f, fmin(xmax0, xmax1) - fmax(xmin0, xmin1) + 1.0); + float h = fmax(0.f, fmin(ymax0, ymax1) - fmax(ymin0, ymin1) + 1.0); + float i = w * h; + float u = (xmax0 - xmin0 + 1.0) * (ymax0 - ymin0 + 1.0) + (xmax1 - xmin1 + 1.0) * (ymax1 - ymin1 + 1.0) - i; + return u <= 0.f ? 0.f : (i / u); +} + +static int nms(int validCount, std::vector &outputLocations, std::vector &order, float threshold) +{ + for (int i = 0; i < validCount; ++i) + { + if (order[i] == -1) + { + continue; + } + int n = order[i]; + for (int j = i + 1; j < validCount; ++j) + { + int m = order[j]; + if (m == -1) + { + continue; + } + float xmin0 = outputLocations[n * 4 + 0]; + float ymin0 = outputLocations[n * 4 + 1]; + float xmax0 = outputLocations[n * 4 + 0] + outputLocations[n * 4 + 2]; + float ymax0 = outputLocations[n * 4 + 1] + outputLocations[n * 4 + 3]; + + float xmin1 = outputLocations[m * 4 + 0]; + float ymin1 = outputLocations[m * 4 + 1]; + float xmax1 = outputLocations[m * 4 + 0] + outputLocations[m * 4 + 2]; + float ymax1 = outputLocations[m * 4 + 1] + outputLocations[m * 4 + 3]; + + float iou = CalculateOverlap(xmin0, ymin0, xmax0, ymax0, xmin1, ymin1, xmax1, ymax1); + + if (iou > threshold) + { + order[j] = -1; + } + } + } + return 0; +} + +static int quick_sort_indice_inverse( + std::vector &input, + int left, + int right, + std::vector &indices) +{ + float key; + int key_index; + int low = left; + int high = right; + if (left < right) + { + key_index = indices[left]; + key = input[left]; + while (low < high) + { + while (low < high && input[high] <= key) + { + high--; + } + input[low] = input[high]; + indices[low] = indices[high]; + while (low < high && input[low] >= key) + { + low++; + } + input[high] = input[low]; + indices[high] = indices[low]; + } + input[low] = key; + indices[low] = key_index; + quick_sort_indice_inverse(input, left, low - 1, indices); + quick_sort_indice_inverse(input, low + 1, right, indices); + } + return low; +} + +static float sigmoid(float x) +{ + return 1.0 / (1.0 + expf(-x)); +} + +static float unsigmoid(float y) +{ + return -1.0 * logf((1.0 / y) - 1.0); +} + +inline static int32_t __clip(float val, float min, float max) +{ + float f = val <= min ? min : (val >= max ? max : val); + return f; +} + +static uint8_t qnt_f32_to_affine(float f32, uint8_t zp, float scale) +{ + float dst_val = (f32 / scale) + zp; + uint8_t res = (uint8_t)__clip(dst_val, 0, 255); + return res; +} + +static float deqnt_affine_to_f32(uint8_t qnt, uint8_t zp, float scale) +{ + return ((float)qnt - (float)zp) * scale; +} + +static int process_u8(uint8_t *input, int *anchor, int grid_h, int grid_w, int height, int width, int stride, + std::vector &boxes, std::vector &boxScores, std::vector &classId, + float threshold, uint8_t zp, float scale) +{ + + int validCount = 0; + int grid_len = grid_h * grid_w; + float thres = unsigmoid(threshold); + uint8_t thres_u8 = qnt_f32_to_affine(thres, zp, scale); + for (int a = 0; a < 3; a++) + { + for (int i = 0; i < grid_h; i++) + { + for (int j = 0; j < grid_w; j++) + { + uint8_t box_confidence = input[(YOLOV5_PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j]; + if (box_confidence >= thres_u8) + { + int offset = (YOLOV5_PROP_BOX_SIZE * a) * grid_len + i * grid_w + j; + uint8_t *in_ptr = input + offset; + float box_x = sigmoid(deqnt_affine_to_f32(*in_ptr, zp, scale)) * 2.0 - 0.5; + float box_y = sigmoid(deqnt_affine_to_f32(in_ptr[grid_len], zp, scale)) * 2.0 - 0.5; + float box_w = sigmoid(deqnt_affine_to_f32(in_ptr[2 * grid_len], zp, scale)) * 2.0; + float box_h = sigmoid(deqnt_affine_to_f32(in_ptr[3 * grid_len], zp, scale)) * 2.0; + box_x = (box_x + j) * (float)stride; + box_y = (box_y + i) * (float)stride; + box_w = box_w * box_w * (float)anchor[a * 2]; + box_h = box_h * box_h * (float)anchor[a * 2 + 1]; + box_x -= (box_w / 2.0); + box_y -= (box_h / 2.0); + boxes.push_back(box_x); + boxes.push_back(box_y); + boxes.push_back(box_w); + boxes.push_back(box_h); + + uint8_t maxClassProbs = in_ptr[5 * grid_len]; + int maxClassId = 0; + for (int k = 1; k < YOLOV5_CLASS_NUM; ++k) + { + uint8_t prob = in_ptr[(5 + k) * grid_len]; + if (prob > maxClassProbs) + { + maxClassId = k; + maxClassProbs = prob; + } + } + float box_conf_f32 = sigmoid(deqnt_affine_to_f32(box_confidence, zp, scale)); + float class_prob_f32 = sigmoid(deqnt_affine_to_f32(maxClassProbs, zp, scale)); + boxScores.push_back(box_conf_f32* class_prob_f32); + classId.push_back(maxClassId); + validCount++; + } + } + } + } + return validCount; +} + +static int process_fp(float *input, int *anchor, int grid_h, int grid_w, int height, int width, int stride, + std::vector &boxes, std::vector &boxScores, std::vector &classId, + float threshold) +{ + + int validCount = 0; + int grid_len = grid_h * grid_w; + float thres_sigmoid = unsigmoid(threshold); + for (int a = 0; a < 3; a++) + { + for (int i = 0; i < grid_h; i++) + { + for (int j = 0; j < grid_w; j++) + { + float box_confidence = input[(YOLOV5_PROP_BOX_SIZE * a + 4) * grid_len + i * grid_w + j]; + if (box_confidence >= thres_sigmoid) + { + int offset = (YOLOV5_PROP_BOX_SIZE * a) * grid_len + i * grid_w + j; + float *in_ptr = input + offset; + float box_x = sigmoid(*in_ptr) * 2.0 - 0.5; + float box_y = sigmoid(in_ptr[grid_len]) * 2.0 - 0.5; + float box_w = sigmoid(in_ptr[2 * grid_len]) * 2.0; + float box_h = sigmoid(in_ptr[3 * grid_len]) * 2.0; + box_x = (box_x + j) * (float)stride; + box_y = (box_y + i) * (float)stride; + box_w = box_w * box_w * (float)anchor[a * 2]; + box_h = box_h * box_h * (float)anchor[a * 2 + 1]; + box_x -= (box_w / 2.0); + box_y -= (box_h / 2.0); + boxes.push_back(box_x); + boxes.push_back(box_y); + boxes.push_back(box_w); + boxes.push_back(box_h); + + float maxClassProbs = in_ptr[5 * grid_len]; + int maxClassId = 0; + for (int k = 1; k < YOLOV5_CLASS_NUM; ++k) + { + float prob = in_ptr[(5 + k) * grid_len]; + if (prob > maxClassProbs) + { + maxClassId = k; + maxClassProbs = prob; + } + } + float box_conf_f32 = sigmoid(box_confidence); + float class_prob_f32 = sigmoid(maxClassProbs); + boxScores.push_back(box_conf_f32* class_prob_f32); + classId.push_back(maxClassId); + validCount++; + } + } + } + } + return validCount; +} + +int yolov5_post_process_u8(uint8_t *input0, uint8_t *input1, uint8_t *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, + std::vector &qnt_zps, std::vector &qnt_scales, + yolov5_detect_result_group_t *group) +{ + static int init = -1; + if (init == -1) + { + /* + int ret = 0; + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) + { + return -1; + } + */ + init = 0; + } + memset(group, 0, sizeof(yolov5_detect_result_group_t)); + + std::vector filterBoxes; + std::vector boxesScore; + std::vector classId; + int stride0 = 8; + int grid_h0 = model_in_h / stride0; + int grid_w0 = model_in_w / stride0; + int validCount0 = 0; + validCount0 = process_u8(input0, (int *)anchor0, grid_h0, grid_w0, model_in_h, model_in_w, + stride0, filterBoxes, boxesScore, classId, conf_threshold, qnt_zps[0], qnt_scales[0]); + + int stride1 = 16; + int grid_h1 = model_in_h / stride1; + int grid_w1 = model_in_w / stride1; + int validCount1 = 0; + validCount1 = process_u8(input1, (int *)anchor1, grid_h1, grid_w1, model_in_h, model_in_w, + stride1, filterBoxes, boxesScore, classId, conf_threshold, qnt_zps[1], qnt_scales[1]); + + int stride2 = 32; + int grid_h2 = model_in_h / stride2; + int grid_w2 = model_in_w / stride2; + int validCount2 = 0; + validCount2 = process_u8(input2, (int *)anchor2, grid_h2, grid_w2, model_in_h, model_in_w, + stride2, filterBoxes, boxesScore, classId, conf_threshold, qnt_zps[2], qnt_scales[2]); + + int validCount = validCount0 + validCount1 + validCount2; + // no object detect + if (validCount <= 0) + { + return 0; + } + + std::vector indexArray; + for (int i = 0; i < validCount; ++i) + { + indexArray.push_back(i); + } + + quick_sort_indice_inverse(boxesScore, 0, validCount - 1, indexArray); + + nms(validCount, filterBoxes, indexArray, nms_threshold); + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) + { + + if (indexArray[i] == -1 || boxesScore[i] < conf_threshold || last_count >= YOLOV5_NUMB_MAX_SIZE) + { + continue; + } + int n = indexArray[i]; + + float x1 = filterBoxes[n * 4 + 0]; + float y1 = filterBoxes[n * 4 + 1]; + float x2 = x1 + filterBoxes[n * 4 + 2]; + float y2 = y1 + filterBoxes[n * 4 + 3]; + int id = classId[n]; + + /* + group->results[last_count].box.left = (int)((clamp(x1, 0, model_in_w) - w_offset) / resize_scale); + group->results[last_count].box.top = (int)((clamp(y1, 0, model_in_h) - h_offset) / resize_scale); + group->results[last_count].box.right = (int)((clamp(x2, 0, model_in_w) - w_offset) / resize_scale); + group->results[last_count].box.bottom = (int)((clamp(y2, 0, model_in_h) - h_offset) / resize_scale); + */ + group->results[last_count].box.left = (int) clamp(x1, 0, model_in_w); + group->results[last_count].box.top = (int) clamp(y1, 0, model_in_h); + group->results[last_count].box.right = (int) clamp(x2, 0, model_in_w); + group->results[last_count].box.bottom = (int) clamp(y2, 0, model_in_h); + + group->results[last_count].prop = boxesScore[i]; + group->results[last_count].class_index = id; + char *label = labels[id]; + strncpy(group->results[last_count].name, label, YOLOV5_NAME_MAX_SIZE); + + // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, group->results[last_count].box.top, + // group->results[last_count].box.right, group->results[last_count].box.bottom, label); + last_count++; + } + group->count = last_count; + + return 0; +} + + +int yolov5_post_process_fp(float *input0, float *input1, float *input2, int model_in_h, int model_in_w, + float conf_threshold, float nms_threshold, + yolov5_detect_result_group_t *group) +{ + static int init = -1; + if (init == -1) + { + /* + int ret = 0; + ret = loadLabelName(LABEL_NALE_TXT_PATH, labels); + if (ret < 0) + { + return -1; + } + */ + + init = 0; + } + memset(group, 0, sizeof(yolov5_detect_result_group_t)); + + std::vector filterBoxes; + std::vector boxesScore; + std::vector classId; + int stride0 = 8; + int grid_h0 = model_in_h / stride0; + int grid_w0 = model_in_w / stride0; + int validCount0 = 0; + validCount0 = process_fp(input0, (int *)anchor0, grid_h0, grid_w0, model_in_h, model_in_w, + stride0, filterBoxes, boxesScore, classId, conf_threshold); + + int stride1 = 16; + int grid_h1 = model_in_h / stride1; + int grid_w1 = model_in_w / stride1; + int validCount1 = 0; + validCount1 = process_fp(input1, (int *)anchor1, grid_h1, grid_w1, model_in_h, model_in_w, + stride1, filterBoxes, boxesScore, classId, conf_threshold); + + int stride2 = 32; + int grid_h2 = model_in_h / stride2; + int grid_w2 = model_in_w / stride2; + int validCount2 = 0; + validCount2 = process_fp(input2, (int *)anchor2, grid_h2, grid_w2, model_in_h, model_in_w, + stride2, filterBoxes, boxesScore, classId, conf_threshold); + + int validCount = validCount0 + validCount1 + validCount2; + // no object detect + if (validCount <= 0) + { + return 0; + } + + std::vector indexArray; + for (int i = 0; i < validCount; ++i) + { + indexArray.push_back(i); + } + + quick_sort_indice_inverse(boxesScore, 0, validCount - 1, indexArray); + + nms(validCount, filterBoxes, indexArray, nms_threshold); + + int last_count = 0; + group->count = 0; + /* box valid detect target */ + for (int i = 0; i < validCount; ++i) + { + + if (indexArray[i] == -1 || boxesScore[i] < conf_threshold || last_count >= YOLOV5_NUMB_MAX_SIZE) + { + continue; + } + int n = indexArray[i]; + + float x1 = filterBoxes[n * 4 + 0]; + float y1 = filterBoxes[n * 4 + 1]; + float x2 = x1 + filterBoxes[n * 4 + 2]; + float y2 = y1 + filterBoxes[n * 4 + 3]; + int id = classId[n]; + + /* + group->results[last_count].box.left = (int)((clamp(x1, 0, model_in_w) - w_offset) / resize_scale); + group->results[last_count].box.top = (int)((clamp(y1, 0, model_in_h) - h_offset) / resize_scale); + group->results[last_count].box.right = (int)((clamp(x2, 0, model_in_w) - w_offset) / resize_scale); + group->results[last_count].box.bottom = (int)((clamp(y2, 0, model_in_h) - h_offset) / resize_scale); + */ + group->results[last_count].box.left = (int) clamp(x1, 0, model_in_w); + group->results[last_count].box.top = (int) clamp(y1, 0, model_in_h); + group->results[last_count].box.right = (int) clamp(x2, 0, model_in_w); + group->results[last_count].box.bottom = (int) clamp(y2, 0, model_in_h); + + group->results[last_count].prop = boxesScore[i]; + group->results[last_count].class_index = id; + char *label = labels[id]; + strncpy(group->results[last_count].name, label, YOLOV5_NAME_MAX_SIZE); + + // printf("result %2d: (%4d, %4d, %4d, %4d), %s\n", i, group->results[last_count].box.left, group->results[last_count].box.top, + // group->results[last_count].box.right, group->results[last_count].box.bottom, label); + last_count++; + } + group->count = last_count; + + return 0; +} diff --git a/src/yuanbenmain.cpp b/src/yuanbenmain.cpp new file mode 100644 index 0000000..391b868 --- /dev/null +++ b/src/yuanbenmain.cpp @@ -0,0 +1,1175 @@ +#include "main.h" +//4 13 22 31 +#include +#include +#include +#include"yolov5_detect.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define MSS_TEST_BUS_NAME "yolov5test" +static jbus_hdl_t g_jbus; +static jmss_hdl_t g_jmss; +static jisp_hdl_t g_jisp; +static bool g_running; +jmss_stm_t *g_stm[5]; + + +#define modelpath_fire "/demo/bin/fire2025_pre.rknn" +#define DOWNLOAD_VERSION_PATH "/tmp/version" +#define ID_FILE_PWD "/etc/DeviceID" +std::string DeviceID; +std::string response_controllerId = "NULL"; + +static char labels_fire[2][20] = {"fire", "any"}; +static char labels_smog[2][20] = {"smog", "any"}; + +#define SERIAL_PORT_INFRARED_SENSOR "/dev/ttyS4" +#define SERIAL_PORT_SOLENOID "/dev/ttyS5" +#define BAUD_RATE B115200 +static int serialPortInfraredSensor; +static int serialPortSolenoid; +float temperature_img[24][32]; +unsigned char buffer[1544]; +std::mutex mtx; + +using namespace cv; +using namespace std; +using json = nlohmann::json; + +int ALARM_TEMPERATURE; +int WARN_TEMPERATURE; +int MOVE_THRESHOLD; +int IGNORE_TEMPERATURE; +double Confidence_Threshold; + +vector> last_result; +vector> now_result; + +int width = 1920, height = 1080; +JMediaRawFrameType_e type = JMEDIA_RAWFRAMETYPE_NV12; + + +bool check_whether_in_last_result(vector> &last_result, vector &now, std::ofstream& temperature_log){ + if(last_result.empty()) return true; + //查找now的框坐标是否有出现在上一帧 + for(auto i : last_result){ + //范围5像素以内都视为不变 + if(abs(i[0] - now[0]) <= MOVE_THRESHOLD && abs(i[1] - now[1]) <= MOVE_THRESHOLD && abs(i[2] - now[2]) <= MOVE_THRESHOLD && abs(i[3] - now[3]) <= MOVE_THRESHOLD){ + temperature_log << "有重复 此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" + << " 上帧结果:(" << i[0] << " " << i[1] << ") (" << i[2] << " " << i[3] << ")"<< endl; + cout << "上帧结果:(" << i[0] << " " << i[1] << ") (" << i[2] << " " << i[3] << ")" << endl; + cout << "此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" << endl; + return true; + } + } + cout << "无重复 此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" << endl; + temperature_log << "无重复 此帧结果:(" << now[0] << " " << now[1] << ") (" << now[2] << " " << now[3] << ")" << endl; + return false; +} + + +string Get_Time(int input){ + // 获取当前系统时间 + std::time_t alarmTime_std = std::time(nullptr); + // 将时间转换为本地时间 + std::tm* localTime = std::localtime(&alarmTime_std); + // 从本地时间结构中获取时间信息 + int year = localTime->tm_year + 1900; + int month = localTime->tm_mon + 1; + int day = localTime->tm_mday; + int hour = localTime->tm_hour; + int minute = localTime->tm_min; + int second = localTime->tm_sec; + // 拼接成字符串 + std::ostringstream oss_alarmTime; + if(input == 1){ + oss_alarmTime << std::setfill('0') + << std::setw(4) << year << "-" + << std::setw(2) << month << "-" + << std::setw(2) << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << minute << ":" + << std::setw(2) << second; + }else{ + oss_alarmTime << std::setfill('0') + << std::setw(2) << month << "_" + << std::setw(2) << day << "_" + << std::setw(2) << hour << "_" + << std::setw(2) << minute << "_" + << std::setw(2) << second; + } + // 获取拼接后的字符串 + std::string formattedTime = oss_alarmTime.str(); + return formattedTime; +} + + + +double calibration(double x){ + double p1 = -9.052e-08; + double p2 = 8.313e-05; + double p3 = -0.02813; + double p4 = 4.16; + double p5 = -219.7; + double res = p1*pow(x, 4) + p2*pow(x, 3) + p3*pow(x, 2) + p4*x + p5; + return res; +} + + +size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* response){ + size_t totalSize = size * nmemb; + response->append(static_cast(contents), totalSize); + return totalSize; +} + + +bool _geturlFromfile(const char * filepath,char *url, int maxLength) { + if (filepath == NULL) { + printf("getrulVersion is error, url == null.\n"); + return false; + } + FILE *fp = fopen(filepath, "r"); + if (fp == NULL) { + printf("open %s failed, error is %s.\n", filepath, strerror(errno)); + return false; + } + int iffind=0; + char *line = NULL; + size_t len = 0; + size_t read; + while ((read = getline(&line, &len, fp)) != -1) { + if (read == 0 || line[0] == '#') { + continue; + } + char *pline = strstr(line, "upload_url"); + if (pline != NULL && (pline = strstr(pline, "=")) != NULL) { + pline++; //过滤掉等于号 + //过滤掉空格 + while (*pline == ' ') { + pline++; + } + int pline_len = strlen(pline) - 1; + int version_len = (pline_len > maxLength ? maxLength:pline_len); + memcpy(url, pline, version_len); + printf("upload_url = %s\n", url); + iffind=1; + break; + } + } + if (iffind == 0 ){ + printf("Can not find upload_url\n"); + return false; + } + free(line); + fclose(fp); + return true; +} + + +// 函数用于读取文件并将其内容保存在全局字符串变量中 +void readFileAndStoreInGlobal(const std::string& filename) { + std::ifstream file(filename); + if (file.is_open()) { + std::getline(file, DeviceID); // 读取一行并存储在全局字符串中 + file.close(); + } else { + std::cerr << "无法打开文件: " << filename << std::endl; + } +} + + +std::string generateIndices(char result[4],Alarm* Alarm) { + std::string indices; + for (int i = 0; i < 4; ++i) { + if (result[i] == '1') { + // 将索引值加入到生成的字符串中 + indices += std::to_string(i + 1); + Alarm->ifalarm = 1; + } + } + return indices; +} + + +void checkCoverage(int x1, int x2, int width, char result[4]) { + // 初始化为 '1',表示覆盖 + // std::string resultString1(result, 4); + // std::cout << "result in: " << resultString1 << std::endl; + char result_now[4]={'1','1','1','1'}; + int line1 = width*7/32; + int line2 = width/2; + int line3 = width*25/32; + if(x1>line1){ + result_now[0] = '0'; + } + if(x1>line2){ + result_now[0] = '0'; + result_now[1] = '0'; + } + if(x1>line3){ + result_now[0] = '0'; + result_now[1] = '0'; + result_now[2] = '0'; + } + + if(x2& data) { + uint16_t sum = 0; + for (size_t i = 0; i < data.size()-1; ++i) { + sum += data[i]; + } + return static_cast(sum & 0xFF); +} + + +void printVector(const std::vector& vec) { + std::cout << "Vector contents: "; + for (const auto& byte : vec) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte) << " "; + } + std::cout << std::dec << std::endl; // 切换回十进制 +} + + +int main(int argc, char **argv) +{ + std::string folderPath = "/demo/pic"; + if(!createDirectory(folderPath)){ + return 0; + } + + // 打开 JSON 文件 + cout << "解析JSON文件" << endl; + std::ifstream file("/demo/bin/config.json"); + + // 检查文件是否成功打开 + if (!file.is_open()) { + std::cerr << "Failed to open json file" << std::endl; + return 1; + } + + try { + // 解析 JSON 文件 + json Config_jsonData; + file >> Config_jsonData; + // 关闭文件 + + // 赋值给全局常量 + WARN_TEMPERATURE = Config_jsonData["WARN_TEMPERATURE"]; + ALARM_TEMPERATURE = Config_jsonData["ALARM_TEMPERATURE"]; + MOVE_THRESHOLD = Config_jsonData["MOVE_THRESHOLD"]; + IGNORE_TEMPERATURE = Config_jsonData["IGNORE_TEMPERATURE"]; + Confidence_Threshold = Config_jsonData["Confidence_Threshold"]; + } + catch (const json::parse_error& e) + { + std::cerr << "JSON parsing error: " << e.what() << std::endl; + return 0; + } + file.close(); + + cout << "配置 WARN_TEMPERATURE :" << WARN_TEMPERATURE << endl; + cout << "配置 ALARM_TEMPERATURE :" << ALARM_TEMPERATURE << endl; + cout << "配置 MOVE_THRESHOLD :" << MOVE_THRESHOLD << endl; + cout << "配置 IGNORE_TEMPERATURE :" << IGNORE_TEMPERATURE << endl; + cout << "配置 Confidence_Threshold :" << Confidence_Threshold << endl; + cout << SERIAL_PORT_INFRARED_SENSOR <=2?(JMediaRawFrameType_e)atoi(argv[1]):JMEDIA_RAWFRAMETYPE_NV12; + int width = 1920, height = 1080; + jmss_raw_t *rawchn = JES_MSS_RawOpen(g_jmss, 0, width, height, type); + if(rawchn == NULL){ + printf("==============>>>: %s:%d open rawchn failed\n", strrchr(__FILE__,'/'),__LINE__); + return -1; + } + int ret; + + + readFileAndStoreInGlobal((char *)ID_FILE_PWD); + // 打印全局字符串变量 + std::cout << "ID内容:" << DeviceID < ControlInstructions(20,0x00); + ControlInstructions[0] = 0xCA; //协议头 + ControlInstructions[1] = 0x14; //总长度 + ControlInstructions[2] = 0x01; //操作指令 + + //打开JES的通道 + jmss_raw_t *rawchn = JES_MSS_RawOpen(g_jmss, 0, width, height, type); + if(rawchn == NULL){ + printf("==============>>>: %s:%d open rawchn failed\n", strrchr(__FILE__,'/'),__LINE__); + } + //日志相关内容 + std::string filename = "/demo/bin/temperature_log.txt"; + std::ofstream temperature_log(filename, std::ios::out | std::ios::trunc); + temperature_log << "配置 WARN_TEMPERATURE :" << WARN_TEMPERATURE << endl; + temperature_log << "配置 ALARM_TEMPERATURE :" << ALARM_TEMPERATURE << endl; + temperature_log << "配置 MOVE_THRESHOLD :" << MOVE_THRESHOLD << endl; + temperature_log << "配置 IGNORE_TEMPERATURE :" << IGNORE_TEMPERATURE << endl; + temperature_log << "配置 Confidence_Threshold :" << Confidence_Threshold << endl; + //环境温度 + double temp_env = 0; + + auto last_time = std::chrono::high_resolution_clock::now();// 记录开始时间 + // 创建一个 Alarm 结构体对象 + Alarm Alarm; + // 调用初始化函数来初始化结构体成员 + initializeAlarm(&Alarm); + //用于从全局buffer中复制数据 + unsigned char copyBuffer[1544]; + + /* 参数初始化 */ + int output_nboxes_left =0; + yolov5_detect_result_group_t detect_result_group; + + /* 算法模型初始化 */ + char *path = reinterpret_cast(args); + printf("loading model %s\n",path); + rknn_context ctx; + yolov5_detect_init(&ctx, path); + // yolov5_detect_init(&ctx, "./fire_rv1126.rknn"); + + //配置label + char labels[2][20]; + getLabels(path, labels); + // 设置压缩质量 + std::vector compression_params; + compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); + compression_params.push_back(30); // 设置压缩质量,范围为 0-100 + int overtem_cnt = 0; + std::vector over_tmp_deque(10, 0); + /* 算法运行 */ + while(!quit){ + int time2run = 1; + // auto current_time = std::chrono::high_resolution_clock::now(); + // auto elapsed_time = std::chrono::duration_cast(current_time - last_time);// 计算距离开始时间的时间差 + + // if (elapsed_time.count() - 1000 > 0) { + // last_time = current_time; + // time2run = 1; + // } + sleep(2); + //获取摄像头buffer + JVMediaFrame_t frm; + if(JES_MSS_RawGetFrame(rawchn,&frm) < 0){ + sleep(1); + continue; + } + int overtmp_falg = 0; + Alarm.ifalarm = 0; + Alarm.ifwarn = 0; + + Mat src; + NV12ToRGB(width, height, frm.buffer, src); + //裁剪 + cv::Rect roi(220, 130, 1480, 570); + src = src(roi); + //固定时间间隔去处理红外参数 + char result[4]={'0','0','0','0'}; + char result_warn[4]={'0','0','0','0'}; + char result_fire_rknn[4]={'0','0','0','0'}; + //初始化ControlInstructions + ControlInstructions[3]=0x00; + ControlInstructions[4]=0x00; + ControlInstructions[5]=0x00; + ControlInstructions[6]=0x00; + + if(time2run){ + cout << "run ." << endl; + mtx.lock(); + memcpy(copyBuffer, buffer, sizeof(buffer)); + mtx.unlock(); + //计算温度 + double temperature; + int Col,Row; + int j = 0; + double max_temperature = 0; + for (int i = 4; i < 1540; i += 2) { + // temperature = calibration((buffer[i+1]*256+buffer[i])/100.0); + temperature = (buffer[i+1]*256+buffer[i])/100.0 + 6; + // cout << (buffer[i+1]*256+buffer[i])/100.0 << " " << temperature << endl; + + //按视觉顺序存入数组 + Col = 31-(j)%32; + Row = (j)/32; + temperature_img[Row][Col] = temperature; + j++; + } + temp_env = (buffer[1541]*256+buffer[1540])/100.0; + WARN_TEMPERATURE = (temp_env*2)>WARN_TEMPERATURE? (temp_env*2):WARN_TEMPERATURE; + // ALARM_TEMPERATURE = WARN_TEMPERATURE + 20; + //截去上下5行数据 + for(int i=5; i<19; i++){ + for(j=0; j<32; j++){ + if(temperature_img[i][j] > IGNORE_TEMPERATURE) continue; + if(temperature_img[i][j] > max_temperature) max_temperature = temperature_img[i][j]; + + if(temperature_img[i][j] > ALARM_TEMPERATURE){ + overtmp_falg = 1; + if (0 <= j && j <= 6) + result[0] = '1'; + else if (6 < j && j <= 15) + result[1] = '1'; + else if (15 < j && j <= 24) + result[2] = '1'; + else if (24 < j && j <= 31) + result[3] = '1'; + } + + if(temperature_img[i][j] > WARN_TEMPERATURE){ + Alarm.ifwarn = 1; + if (0 < j && j <= 6) + result_warn[0] = '1'; + else if (6 < j && j <= 15) + result_warn[1] = '1'; + else if (13 < j && j <= 24) + result_warn[2] = '1'; + else if (24 < j && j <= 31) + result_warn[3] = '1'; + } + } + } + + int overtem_cnt_for_log; + if(overtmp_falg){ + over_tmp_deque.push_back(1); + if (over_tmp_deque.size() > 10) { + over_tmp_deque.erase(over_tmp_deque.begin()); + } + overtem_cnt = 0; + for (int i = 0; i < over_tmp_deque.size(); ++i) { + if (over_tmp_deque[i] == 1) { + overtem_cnt++; + } + } + overtem_cnt_for_log = overtem_cnt; + + if(overtem_cnt >= 3){ + Alarm.ifalarm = 1; + over_tmp_deque.assign(9, 0); + over_tmp_deque.push_back(1); + }else{ + memset(result, '0', 4*sizeof(char)); + } + }else{ + over_tmp_deque.push_back(0); + // 保持队列长度为10,如果超过长度则移除最前面的元素 + if (over_tmp_deque.size() > 10) { + over_tmp_deque.erase(over_tmp_deque.begin()); + } + } + + + // temp_env = (buffer[1541]*256+buffer[1540])/100.0; + cout << "最大温度: " << max_temperature << endl; + cout << "环境温度" << temp_env << endl; + cout << "预警温度: " << WARN_TEMPERATURE << endl; + cout << "直接喷水温度: " << ALARM_TEMPERATURE << endl; + if(max_temperature>WARN_TEMPERATURE){ + temperature_log << endl << "---------------------------------------------------" << endl; + temperature_log << Get_Time(1) << endl; + temperature_log << "最大温度: " << max_temperature << endl; + temperature_log << "环境温度: " << temp_env << endl; + temperature_log << "预警温度: " << WARN_TEMPERATURE << endl; + temperature_log << "直接喷水温度: " << ALARM_TEMPERATURE << endl; + temperature_log << "超温队列:"; + for (int i = 0; i < over_tmp_deque.size(); ++i) { + temperature_log << over_tmp_deque[i] << " "; + } + temperature_log << endl; + } + if(Alarm.ifalarm){ + printf("temprature > %d°C !\n", ALARM_TEMPERATURE); + // 将字符数组拼接成字符串 + std::string resultString(result, 4); + std::cout << ALARM_TEMPERATURE <<"度结果: " << resultString << std::endl; + temperature_log << ALARM_TEMPERATURE <<"度结果: " << resultString << std::endl; + std::string resultString_warn(result_warn, 4); + std::cout << WARN_TEMPERATURE << "度结果: " << resultString_warn << std::endl; + temperature_log << WARN_TEMPERATURE <<"度结果: " << resultString_warn << std::endl; + }else if(Alarm.ifwarn){ + printf("temprature > %d°C !\n", WARN_TEMPERATURE); + std::string resultString_warn(result_warn, 4); + std::cout << WARN_TEMPERATURE << "结果: " << resultString_warn << std::endl; + temperature_log << WARN_TEMPERATURE << "结果: " << resultString_warn << std::endl; + } + } + + if(Alarm.ifwarn){ + struct timeval start; + struct timeval end; + now_result.clear(); + temperature_log << "上帧总的结果:" << endl; + for(auto i : last_result){ + temperature_log << "(" << i[0] << " " << i[1] << ") (" << i[2] << " " << i[3] << ")" << endl; + } + printf("%s_rknn_run\n",labels[0]); + yolov5_detect_run(ctx, src, &detect_result_group); + } + + /* 算法结果在图像中画出并保存 */ + int name_int = 0; + // char result[4]={'0','0','0','0'}; + for (int i = 0; i < detect_result_group.count; i++) + { + yolov5_detect_result_t *det_result = &(detect_result_group.results[i]); + if( det_result->prop < Confidence_Threshold ) + { + continue; + } + int name_int = std::stoi(det_result->name); + if(Alarm.ifwarn){ + printf("%s @ (%d %d %d %d) %f\n", + labels[name_int], + det_result->box.left, det_result->box.top, det_result->box.right, det_result->box.bottom, + det_result->prop); + vector now; + now.push_back(det_result->box.left); + now.push_back(det_result->box.top); + now.push_back(det_result->box.right); + now.push_back(det_result->box.bottom); + now_result.push_back(now); + if(check_whether_in_last_result(last_result,now,temperature_log)){ + continue; + } + } + int x1 = det_result->box.left; + int y1 = det_result->box.top; + int x2 = det_result->box.right; + int y2 = det_result->box.bottom; + + //检测区域 + if(Alarm.ifwarn){ + checkCoverage(x1, x2, 1480, result_fire_rknn); + } + + char label_text[50]; + memset(label_text, 0 , sizeof(label_text)); + sprintf(label_text, "%s %0.2f",labels[name_int], det_result->prop); + plot_one_box(src, x1, x2, y1, y2, label_text, i%10); + } + + if(Alarm.ifwarn){ + // if(!now_result.empty()){ + // last_result.clear(); + // last_result = now_result; + // } + + //若上帧结果为空,依然清理结果队列 --2024.11.4 + last_result.clear(); + last_result = now_result; + + std::string resultString_fire(result_fire_rknn, 4); + std::cout << "rknn检测结果: " << resultString_fire << std::endl; + temperature_log << "rknn检测结果: " << resultString_fire << std::endl; + //对45度区域和火焰检测区域做出 与操作 + and_result(result_warn,result_fire_rknn); + //对火与45度结果 与 60 度结果 做或操作 + or_result(result,result_warn); + temperature_log << "报警输入:" << string(result, 4) <(orig_data.c_str()), orig_data.length()); + Alarm.alarmBase64 = encoded_data; + Alarm.alarmMsg = std::string(labels[name_int]); + pthread_t upload_message_tidp; + pthread_create(&upload_message_tidp, NULL, upload_message, static_cast(&Alarm)); + pthread_t upload_message_controller_tidp; + int ret =pthread_create(&upload_message_controller_tidp, NULL, upload_message_controller, static_cast(&Alarm)); + if (ret != 0) { + std::cerr << "Error creating controller thread: " << strerror(ret) << std::endl; + } else { + std::cerr << "success creating controller thread" << std::endl; + } + } + } + + /* 算法模型空间释放 */ + yolov5_detect_release(ctx); + //文件关闭 + temperature_log.close(); + return 0; + +} + +void *upload_message(void *args) +{ + pthread_detach(pthread_self()); + // 获取上报url + char upload_url[200] = {0}; + if(!_geturlFromfile(DOWNLOAD_VERSION_PATH,upload_url,sizeof(upload_url))){ + printf("结束进程\n"); + return 0; + } + + Alarm* alarm = static_cast(args); + + // 获取当前系统时间 + std::time_t alarmTime_std = std::time(nullptr); + + // 将时间转换为本地时间 + std::tm* localTime = std::localtime(&alarmTime_std); + + // 从本地时间结构中获取时间信息 + int year = localTime->tm_year + 1900; + int month = localTime->tm_mon + 1; + int day = localTime->tm_mday; + int hour = localTime->tm_hour; + int minute = localTime->tm_min; + int second = localTime->tm_sec; + + // 拼接成字符串 + std::ostringstream oss_alarmTime; + oss_alarmTime << std::setfill('0') + << std::setw(4) << year << "-" + << std::setw(2) << month << "-" + << std::setw(2) << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << minute << ":" + << std::setw(2) << second; + + // 获取拼接后的字符串 + std::string formattedTime = oss_alarmTime.str(); + + std::string cameraId = DeviceID; + std::string controllerId = response_controllerId; + std::string msg = (alarm->alarmMsg).c_str(); + std::string type = "1"; + std::string time = formattedTime; + std::string img = (alarm->alarmBase64).c_str(); + std::string Coverage = (alarm->alarmCoverage).c_str(); + + std::cout << "cameraId: " << cameraId << " msg: " << msg << " type: " << type << " time: " << time <(args); + + // 获取当前系统时间 + std::time_t alarmTime_std = std::time(nullptr); + + // 将时间转换为本地时间 + std::tm* localTime = std::localtime(&alarmTime_std); + + // 从本地时间结构中获取时间信息 + int year = localTime->tm_year + 1900; + int month = localTime->tm_mon + 1; + int day = localTime->tm_mday; + int hour = localTime->tm_hour; + int minute = localTime->tm_min; + int second = localTime->tm_sec; + + // 拼接成字符串 + std::ostringstream oss_alarmTime; + oss_alarmTime << std::setfill('0') + << std::setw(4) << year << "-" + << std::setw(2) << month << "-" + << std::setw(2) << day << " " + << std::setw(2) << hour << ":" + << std::setw(2) << minute << ":" + << std::setw(2) << second; + + // 获取拼接后的字符串 + std::string formattedTime = oss_alarmTime.str(); + + std::string cameraId = DeviceID; + std::string Coverage = (alarm->alarmCoverage).c_str(); + + std::cout << "cameraid: " << cameraId << " errNum: " << Coverage <(current_time - last_time);// 计算距离开始时间的时间差 + + if (elapsed_time.count() - 30000 > 0) { + last_time = current_time; + time2run = 1; + } + + if(time2run){ + std::cout << "心跳上报" << std::endl; + std::string cameraId = DeviceID; + std::string IP = "987654"; + int state = 1; + std::string http_address = "http"; + + std::string MessageString = +R"({ + "cameraId": ")" + cameraId + R"(", + "IP": ")" + IP + R"(", + "state": )" + std::to_string(state) + R"(, + "http_address": ")" + http_address + R"(" +})"; + CURL *curl; + CURLcode res; + curl_global_init(CURL_GLOBAL_ALL); + curl = curl_easy_init(); + + if(curl) + { + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); + curl_easy_setopt(curl, CURLOPT_URL, "http://192.168.1.23:9527/device/heartbeat"); + /* Now specify the POST data */ + struct curl_slist *plist = nullptr; + plist = curl_slist_append(plist, "Content-Type:application/json;charset=UTF-8"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, plist); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, MessageString.c_str()); + + std::string response; + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + + res = curl_easy_perform(curl); + /* Check for errors */ + // if(res != CURLE_OK) + // fprintf(stderr, "curl_easy_perform() failed: %s\n",curl_easy_strerror(res)); + if (res != CURLE_OK) { + std::cerr << "heart beat: Failed to perform cURL request: " << curl_easy_strerror(res) << std::endl; + } else { + std::cout << "heart beat: Request successful!" << std::endl; + std::cout << "heart beat: Response: " << response << std::endl; + // 解析 JSON 字符串 + try { + json jsonData = json::parse(response); + + // 提取 controllerId 字段的内容 + response_controllerId = jsonData["controllerId"]; + + std::cout << "Controller ID: " << response_controllerId << std::endl; + } catch (const json::parse_error& e) { + std::cerr << "JSON parsing error: " << e.what() << std::endl; + } + } + + + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + } + time2run = 0; + } +} + + +void *read_serial_thread(void *args) +{ + pthread_detach(pthread_self()); + ssize_t bytesRead; + serialPortInfraredSensor = open(SERIAL_PORT_INFRARED_SENSOR, O_RDWR | O_NOCTTY | O_NDELAY); + if (serialPortInfraredSensor == -1) { + printf("Failed to open serial port: %s\n", SERIAL_PORT_INFRARED_SENSOR); + return 0; + } + + struct termios tty; + memset(&tty, 0, sizeof(tty)); + if (tcgetattr(serialPortInfraredSensor, &tty) != 0) { + printf("Failed to get serial port attributes\n"); + close(serialPortInfraredSensor); + return 0; + } + cfsetospeed(&tty, BAUD_RATE); + cfsetispeed(&tty, BAUD_RATE); + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; + tty.c_cflag &= ~(PARENB | PARODD); + tty.c_cflag &= ~CSTOPB; + tty.c_cflag |= CREAD | CLOCAL; + tty.c_iflag = IGNPAR; + tty.c_oflag = 0; + tty.c_lflag = 0; + if (tcsetattr(serialPortInfraredSensor, TCSANOW, &tty) != 0) { + printf("Failed to set serial port attributes\n"); + close(serialPortInfraredSensor); + return 0; + } + + // while(1){ + // mtx.lock(); + // bytesRead = read(serialPortInfraredSensor, buffer, sizeof(buffer)); + // mtx.unlock(); + // if (bytesRead>0) { + // if((buffer[0]== 0x5a)&&(buffer[1]==0x5a)) { + // // printf("readed serialPortInfraredSensor date\n"); + // }else{ + // // printf("read failed\n"); + // } + // }else{ + // // printf("empty to read\n"); + // } + // } + fd_set readfds; + struct timeval timeout; + int selectResult; + + while (1) { + FD_ZERO(&readfds); + FD_SET(serialPortInfraredSensor, &readfds); + + timeout.tv_sec = 5; // 5秒超时 + timeout.tv_usec = 0; + + selectResult = select(serialPortInfraredSensor + 1, &readfds, NULL, NULL, &timeout); + + if (selectResult > 0) { + if (FD_ISSET(serialPortInfraredSensor, &readfds)) { + mtx.lock(); + bytesRead = read(serialPortInfraredSensor, buffer, sizeof(buffer)); + mtx.unlock(); + + if (bytesRead > 0) { + if (buffer[0] == 0x5a && buffer[1] == 0x5a) { + // printf("readed serialPortInfraredSensor date\n"); + } else { + // printf("read failed\n"); + } + } + } + } else if (selectResult == 0) { + // 超时,没有数据可读 + // printf("Timeout, no data available\n"); + } else { + // select出错 + printf("select() failed\n"); + break; + } + } +} \ No newline at end of file